All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.reactivex.mantis.remote.observable.RemoteObservable Maven / Gradle / Ivy

There is a newer version: 3.1.4
Show newest version
/*
 * Copyright 2019 Netflix, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.reactivex.mantis.remote.observable;

import com.mantisrx.common.utils.NettyUtils;
import io.mantisrx.common.MantisGroup;
import io.mantisrx.common.codec.Decoder;
import io.mantisrx.common.codec.Encoder;
import io.mantisrx.server.core.ServiceRegistry;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.compression.JdkZlibDecoder;
import io.netty.handler.codec.compression.JdkZlibEncoder;
import io.netty.handler.codec.compression.ZlibWrapper;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.reactivex.mantis.remote.observable.ingress.IngressPolicies;
import io.reactivex.mantis.remote.observable.ingress.IngressPolicy;
import io.reactivx.mantis.operators.DropOperator;
import io.reactivx.mantis.operators.GroupedObservableUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.concurrent.TimeUnit;
import mantis.io.reactivex.netty.RxNetty;
import mantis.io.reactivex.netty.channel.ObservableConnection;
import mantis.io.reactivex.netty.pipeline.PipelineConfigurator;
import mantis.io.reactivex.netty.pipeline.PipelineConfiguratorComposite;
import org.jctools.queues.SpscArrayQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rx.Notification;
import rx.Observable;
import rx.Observable.OnSubscribe;
import rx.Observer;
import rx.Subscriber;
import rx.functions.Action0;
import rx.functions.Func1;
import rx.functions.Func2;
import rx.observables.GroupedObservable;


public class RemoteObservable {

    private static final Logger logger = LoggerFactory.getLogger(RemoteObservable.class);

    private static boolean enableHeartBeating = true;
    private static boolean enableNettyLogging = false;
    private static boolean enableCompression = true;
    private static int maxFrameLength = 5242880; // 5 MB max frame

    // NJ
    static {
        NettyUtils.setNettyThreads();

    }


    private RemoteObservable() { }

    private static void loadFastProperties() {
        String enableHeartBeatingStr =
                ServiceRegistry.INSTANCE.getPropertiesService().getStringValue("mantis.netty.enableHeartBeating", "true");
        if (enableHeartBeatingStr.equals("false")) {
            enableHeartBeating = false;
        }

        String enableNettyLoggingStr =
                ServiceRegistry.INSTANCE.getPropertiesService().getStringValue("mantis.netty.enableLogging", "false");
        if (enableNettyLoggingStr.equals("true")) {
            enableNettyLogging = true;
        }

        String enableCompressionStr =
                ServiceRegistry.INSTANCE.getPropertiesService().getStringValue("mantis.netty.enableCompression", "true");
        if (enableCompressionStr.equals("false")) {
            enableCompression = false;
        }

        String maxFrameLengthStr =
                ServiceRegistry.INSTANCE.getPropertiesService().getStringValue("mantis.netty.maxFrameLength", "5242880");
        if (maxFrameLengthStr != null && maxFrameLengthStr.length() > 0) {
            maxFrameLength = Integer.parseInt(maxFrameLengthStr);
        }
    }

    private static Func1, ? extends Observable> retryLogic(final
                                                                                                      ConnectToConfig params) {
        return new Func1, Observable>() {
            @Override
            public Observable call(
                    Observable attempts) {
                return
                        attempts
                                .zipWith(Observable.range(1, params.getSubscribeAttempts())
                                        , new Func2() {
                                            @Override
                                            public ThrowableWithCount call(Throwable t1, Integer retryAttempt) {

                                                return new ThrowableWithCount(t1, retryAttempt);
                                            }
                                        })
                                .flatMap(new Func1>() {
                                    @Override
                                    public Observable call(ThrowableWithCount notificationWithCount) {
                                        logger.debug("Failed to subscribe to remote observable: " + params.getName(),
                                                notificationWithCount.getThrowable());
                                        logger.info("Failed to subscribe to remote observable: " + params.getName() + ""
                                                + " at host: " + params.getHost() + " on port: " + params.getPort() + " subscribe "
                                                + "attempt: " + notificationWithCount.getCount() + " of: " + params.getSubscribeAttempts());
                                        if (notificationWithCount.getCount() == params.getSubscribeAttempts()) {
                                            Throwable t = notificationWithCount.getThrowable();
                                            if (t != null) {
                                                return Observable.error(notificationWithCount.getThrowable());
                                            }
                                        }
                                        return Observable.timer(notificationWithCount.getCount(), TimeUnit.SECONDS);
                                    }
                                });
            }
        };
    }

    public static  RemoteRxConnection connect(final ConnectToObservable params) {
        final RxMetrics metrics = new RxMetrics();

        return new RemoteRxConnection(Observable.create(new OnSubscribe() {
            @Override
            public void call(Subscriber subscriber) {
                RemoteUnsubscribe remoteUnsubscribe = new RemoteUnsubscribe(params.getName());
                // wrapped in Observable.create() to inject unsubscribe callback
                subscriber.add(remoteUnsubscribe); // unsubscribed callback
                // create connection
                createTcpConnectionToServer(params, remoteUnsubscribe, metrics,
                        params.getConnectionDisconnectCallback(),
                        params.getCloseTrigger())
                        .subscribe(subscriber);
            }
        }), metrics, params.getCloseTrigger());
    }

    public static  RemoteRxConnection> connect(
            final ConnectToGroupedObservable config) {
        final RxMetrics metrics = new RxMetrics();
        return new RemoteRxConnection>(Observable.create(
                new OnSubscribe>() {
                    @Override
                    public void call(Subscriber> subscriber) {
                        RemoteUnsubscribe remoteUnsubscribe = new RemoteUnsubscribe(config.getName());
                        // wrapped in Observable.create() to inject unsubscribe callback
                        subscriber.add(remoteUnsubscribe); // unsubscribed callback
                        // create connection
                        createTcpConnectionToServer(config, remoteUnsubscribe, metrics,
                                config.getConnectionDisconnectCallback(),
                                config.getCloseTrigger())
                                .retryWhen(retryLogic(config))
                                .subscribe(subscriber);
                    }
                }), metrics, config.getCloseTrigger());
    }

    // NJ
    public static  RemoteRxConnection> connectToMGO(
            final ConnectToGroupedObservable config, final SpscArrayQueue> inputQueue) {
        final RxMetrics metrics = new RxMetrics();
        return new RemoteRxConnection>(Observable.create(
                new OnSubscribe>() {
                    @Override
                    public void call(Subscriber> subscriber) {
                        RemoteUnsubscribe remoteUnsubscribe = new RemoteUnsubscribe(config.getName());
                        // wrapped in Observable.create() to inject unsubscribe callback
                        subscriber.add(remoteUnsubscribe); // unsubscribed callback
                        // create connection
                        createTcpConnectionToGOServer(config, remoteUnsubscribe, metrics,
                                config.getConnectionDisconnectCallback(),
                                config.getCloseTrigger(),
                                inputQueue)
                                .retryWhen(retryLogic(config))

                                .subscribe(subscriber);
                    }
                }), metrics, config.getCloseTrigger());
    }


    public static  Observable connect(final String host, final int port, final Decoder decoder) {
        return connect(new ConnectToObservable.Builder()
                .host(host)
                .port(port)
                .decoder(decoder)
                .build()).getObservable();
    }

    private static  Observable> createTcpConnectionToServer(final ConnectToGroupedObservable params,
                                                                                          final RemoteUnsubscribe remoteUnsubscribe, final RxMetrics metrics,
                                                                                          final Action0 connectionDisconnectCallback, Observable closeTrigger) {
        final Decoder keyDecoder = params.getKeyDecoder();
        final Decoder valueDecoder = params.getValueDecoder();
        loadFastProperties();
        return
                RxNetty.createTcpClient(params.getHost(), params.getPort(), new PipelineConfiguratorComposite>(
                        new PipelineConfigurator>() {
                            @Override
                            public void configureNewPipeline(ChannelPipeline pipeline) {
                                if (enableNettyLogging) {
                                    pipeline.addFirst(new LoggingHandler(LogLevel.ERROR)); // uncomment to enable debug logging
                                }
                                if (enableHeartBeating) {
                                    pipeline.addLast("idleStateHandler", new IdleStateHandler(10, 2, 0));
                                    pipeline.addLast("heartbeat", new HeartbeatHandler());
                                }
                                if (enableCompression) {
                                    pipeline.addLast("gzipInflater", new JdkZlibEncoder(ZlibWrapper.GZIP));
                                    pipeline.addLast("gzipDeflater", new JdkZlibDecoder(ZlibWrapper.GZIP));
                                }
                                pipeline.addLast("frameEncoder", new LengthFieldPrepender(4)); // 4 bytes to encode length
                                pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(maxFrameLength, 0, 4, 0, 4)); // max frame = half MB
                            }
                        }, new BatchedRxEventPipelineConfigurator()))
                        .connect()
                        // send subscription request, get input stream
                        .flatMap(new Func1>, Observable>() {
                            @Override
                            public Observable call(final ObservableConnection> connection) {
                                connection.writeAndFlush(RemoteRxEvent.subscribed(params.getName(), params.getSubscribeParameters())); // send subscribe event to server
                                remoteUnsubscribe.setConnection(connection);
                                return connection.getInput()
                                        .lift(new DropOperator("incoming_" + RemoteObservable.class.getCanonicalName() + "_createTcpConnectionToServerGroups"));
                            }
                        })
                        .doOnCompleted(new Action0() {
                            @Override
                            public void call() {
                                // connection completed
                                logger.warn("Detected connection completed when trying to connect to host: " + params.getHost() + " port: " + params.getPort());
                                connectionDisconnectCallback.call();
                            }
                        })
                        .onErrorResumeNext(new Func1>() {
                            @Override
                            public Observable call(Throwable t1) {
                                logger.warn("Detected connection error when trying to connect to host: " + params.getHost() + " port: " + params.getPort(), t1);
                                connectionDisconnectCallback.call();
                                // complete if error occurs
                                return Observable.empty();
                            }
                        })
                        .takeUntil(closeTrigger)
                        // data received from server
                        .map(new Func1>() {
                            @Override
                            public Notification call(RemoteRxEvent rxEvent) {
                                if (rxEvent.getType() == RemoteRxEvent.Type.next) {
                                    metrics.incrementNextCount();
                                    return Notification.createOnNext(rxEvent.getData());
                                } else if (rxEvent.getType() == RemoteRxEvent.Type.error) {
                                    metrics.incrementErrorCount();
                                    return Notification.createOnError(fromBytesToThrowable(rxEvent.getData()));
                                } else if (rxEvent.getType() == RemoteRxEvent.Type.completed) {
                                    metrics.incrementCompletedCount();
                                    return Notification.createOnCompleted();
                                } else {
                                    throw new RuntimeException("RemoteRxEvent of type:" + rxEvent.getType() + ", not supported.");
                                }
                            }
                        })
                        .dematerialize()
                        .groupBy(
                                new Func1() {
                                    @Override
                                    public K call(byte[] bytes) {
                                        ByteBuffer buff = ByteBuffer.wrap((byte[]) bytes);
                                        buff.get(); // ignore notification type in key selector
                                        int keyLength = buff.getInt();
                                        byte[] key = new byte[keyLength];
                                        buff.get(key);
                                        return keyDecoder.decode(key);
                                    }
                                }
                                , new Func1>() {
                                    @Override
                                    public Notification call(byte[] bytes) {
                                        ByteBuffer buff = ByteBuffer.wrap((byte[]) bytes);
                                        byte notificationType = buff.get();
                                        if (notificationType == 1) {
                                            int keyLength = buff.getInt();
                                            int end = buff.limit();
                                            int dataLength = end - 4 - 1 - keyLength;
                                            byte[] valueBytes = new byte[dataLength];
                                            buff.position(4 + 1 + keyLength);
                                            buff.get(valueBytes, 0, dataLength);
                                            V value = valueDecoder.decode(valueBytes);
                                            return Notification.createOnNext(value);
                                        } else if (notificationType == 2) {
                                            return Notification.createOnCompleted();
                                        } else if (notificationType == 3) {
                                            int keyLength = buff.getInt();
                                            int end = buff.limit();
                                            int dataLength = end - 4 - 1 - keyLength;
                                            byte[] errorBytes = new byte[dataLength];
                                            buff.position(4 + 1 + keyLength);
                                            buff.get(errorBytes, 0, dataLength);
                                            return Notification.createOnError(fromBytesToThrowable(errorBytes));
                                        } else {
                                            throw new RuntimeException("Notification encoding not support: " + notificationType);
                                        }
                                    }
                                })
                        .map(new Func1>, GroupedObservable>() {
                            @Override
                            public GroupedObservable call(
                                    GroupedObservable> group) {
                                return GroupedObservableUtils.createGroupedObservable(
                                        group.getKey(), group.dematerialize());
                            }
                        })
                        .doOnEach(new Observer>() {
                            @Override
                            public void onCompleted() {
                                logger.info("RemoteRxEvent, name: {} onCompleted()", params.getName());

                            }

                            @Override
                            public void onError(Throwable e) {
                                logger.error("RemoteRxEvent, name: {} onError()", params.getName(), e);
                            }

                            @Override
                            public void onNext(GroupedObservable group) {
                                if (logger.isDebugEnabled()) {
                                    logger.debug("RemoteRxEvent, name: {} new key: {}", params.getName(), group.getKey());
                                }
                            }
                        });
    }

    private static  Observable> createTcpConnectionToGOServer(final ConnectToGroupedObservable params,
                                                                                      final RemoteUnsubscribe remoteUnsubscribe, final RxMetrics metrics,
                                                                                      final Action0 connectionDisconnectCallback, Observable closeTrigger,
                                                                                      final SpscArrayQueue> inputQueue) {
        final Decoder keyDecoder = params.getKeyDecoder();
        final Decoder valueDecoder = params.getValueDecoder();
        loadFastProperties();
        return
                RxNetty.createTcpClient(params.getHost(), params.getPort(), new PipelineConfiguratorComposite>(
                        new PipelineConfigurator>() {
                            @Override
                            public void configureNewPipeline(ChannelPipeline pipeline) {
                                if (enableNettyLogging) {
                                    pipeline.addFirst(new LoggingHandler(LogLevel.ERROR)); // uncomment to enable debug logging
                                }
                                if (enableHeartBeating) {
                                    pipeline.addLast("idleStateHandler", new IdleStateHandler(10, 2, 0));
                                    pipeline.addLast("heartbeat", new HeartbeatHandler());
                                }
                                if (enableCompression) {
                                    pipeline.addLast("gzipInflater", new JdkZlibEncoder(ZlibWrapper.GZIP));
                                    pipeline.addLast("gzipDeflater", new JdkZlibDecoder(ZlibWrapper.GZIP));
                                }
                                pipeline.addLast("frameEncoder", new LengthFieldPrepender(4)); // 4 bytes to encode length
                                pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(maxFrameLength, 0, 4, 0, 4)); // max frame = half MB
                            }
                        }, new BatchedRxEventPipelineConfigurator()))
                        .connect()
                        // send subscription request, get input stream
                        .flatMap(new Func1>, Observable>() {
                            @Override
                            public Observable call(final ObservableConnection> connection) {
                                connection.writeAndFlush(RemoteRxEvent.subscribed(params.getName(), params.getSubscribeParameters())); // send subscribe event to server
                                remoteUnsubscribe.setConnection(connection);
                                return connection.getInput()
                                        .lift(new DropOperator("incoming_" + RemoteObservable.class.getCanonicalName() + "_createTcpConnectionToServerGroups"));
                            }
                        })
                        .doOnCompleted(new Action0() {
                            @Override
                            public void call() {
                                // connection completed
                                logger.warn("Detected connection completed when trying to connect to host: " + params.getHost() + " port: " + params.getPort());
                                connectionDisconnectCallback.call();
                            }
                        })
                        .onErrorResumeNext(new Func1>() {
                            @Override
                            public Observable call(Throwable t1) {
                                logger.warn("Detected connection error when trying to connect to host: " + params.getHost() + " port: " + params.getPort(), t1);
                                connectionDisconnectCallback.call();
                                // complete if error occurs
                                return Observable.empty();
                            }
                        })
                        .takeUntil(closeTrigger)
                        .filter(new Func1() {

                            @Override
                            public Boolean call(RemoteRxEvent rxEvent) {
                                return (rxEvent.getType() == RemoteRxEvent.Type.next);
                            }

                        })
                        // data received from server
                        .map(new Func1>() {
                            @Override
                            public Notification call(RemoteRxEvent rxEvent) {
                                metrics.incrementNextCount();
                                return Notification.createOnNext(rxEvent.getData());
                            }
                        })
                        .dematerialize()
                        .map(new Func1>() {

                            @Override
                            public MantisGroup call(byte[] bytes) {
                                ByteBuffer buff = ByteBuffer.wrap((byte[]) bytes);
                                buff.get(); // ignore notification type in key selector
                                int keyLength = buff.getInt();
                                byte[] key = new byte[keyLength];
                                buff.get(key);
                                K keyVal = keyDecoder.decode(key);
                                V value = null;
                                buff = ByteBuffer.wrap((byte[]) bytes);
                                byte notificationType = buff.get();
                                if (notificationType == 1) {
                                    //int keyLength = buff.getInt();
                                    int end = buff.limit();
                                    int dataLength = end - 4 - 1 - keyLength;
                                    byte[] valueBytes = new byte[dataLength];
                                    buff.position(4 + 1 + keyLength);
                                    buff.get(valueBytes, 0, dataLength);
                                    value = valueDecoder.decode(valueBytes);

                                } else {
                                    throw new RuntimeException("Notification encoding not support: " + notificationType);
                                }
                                return new MantisGroup(keyVal, value);
                            }

                        })
                        .doOnEach(new Observer>() {
                            @Override
                            public void onCompleted() {
                                logger.info("RemoteRxEvent, name: " + params.getName() + " onCompleted()");
                            }

                            @Override
                            public void onError(Throwable e) {
                                logger.error("RemoteRxEvent, name: " + params.getName() + " onError()", e);
                            }

                            @Override
                            public void onNext(MantisGroup group) {
                                if (logger.isDebugEnabled()) {
                                    logger.debug("RemoteRxEvent, name: " + params.getName() + " new key: " + group.getKeyValue());
                                }
                            }
                        });

    }


    private static  Observable createTcpConnectionToServer(final ConnectToObservable params,
                                                                 final RemoteUnsubscribe remoteUnsubscribe, final RxMetrics metrics,
                                                                 final Action0 connectionDisconnectCallback, Observable closeTrigger) {

        final Decoder decoder = params.getDecoder();
        loadFastProperties();
        return
                RxNetty.createTcpClient(params.getHost(), params.getPort(), new PipelineConfiguratorComposite>(
                        new PipelineConfigurator>() {
                            @Override
                            public void configureNewPipeline(ChannelPipeline pipeline) {
                                if (enableNettyLogging) {
                                    pipeline.addFirst(new LoggingHandler(LogLevel.ERROR)); // uncomment to enable debug logging
                                }
                                if (enableHeartBeating) {
                                    pipeline.addLast("idleStateHandler", new IdleStateHandler(10, 2, 0));
                                    pipeline.addLast("heartbeat", new HeartbeatHandler());
                                }
                                if (enableCompression) {
                                    pipeline.addLast("gzipInflater", new JdkZlibEncoder(ZlibWrapper.GZIP));
                                    pipeline.addLast("gzipDeflater", new JdkZlibDecoder(ZlibWrapper.GZIP));
                                }
                                pipeline.addLast("frameEncoder", new LengthFieldPrepender(4)); // 4 bytes to encode length
                                pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(maxFrameLength, 0, 4, 0, 4)); // max frame = half MB

                            }
                        }, new BatchedRxEventPipelineConfigurator()))
                        .connect()
                        // send subscription request, get input stream
                        .flatMap(new Func1>, Observable>() {
                            @Override
                            public Observable call(final ObservableConnection> connection) {
                                connection.writeAndFlush(RemoteRxEvent.subscribed(params.getName(), params.getSubscribeParameters())); // send subscribe event to server
                                remoteUnsubscribe.setConnection(connection);
                                return connection.getInput()
                                        .lift(new DropOperator("incoming_" + RemoteObservable.class.getCanonicalName() + "_createTcpConnectionToServer"));
                            }
                        })
                        .doOnCompleted(new Action0() {
                            @Override
                            public void call() {
                                // connection completed
                                logger.warn("Detected connection completed when trying to connect to host: " + params.getHost() + " port: " + params.getPort());
                                connectionDisconnectCallback.call();
                            }
                        })
                        .onErrorResumeNext(new Func1>() {
                            @Override
                            public Observable call(Throwable t1) {
                                logger.warn("Detected connection error when trying to connect to host: " + params.getHost() + " port: " + params.getPort(), t1);
                                connectionDisconnectCallback.call();
                                // complete if error occurs
                                return Observable.empty();
                            }
                        })
                        .takeUntil(closeTrigger)
                        .map(new Func1>() {
                            @Override
                            public Notification call(RemoteRxEvent rxEvent) {
                                if (rxEvent.getType() == RemoteRxEvent.Type.next) {
                                    metrics.incrementNextCount();
                                    return Notification.createOnNext(decoder.decode(rxEvent.getData()));
                                } else if (rxEvent.getType() == RemoteRxEvent.Type.error) {
                                    metrics.incrementErrorCount();
                                    return Notification.createOnError(fromBytesToThrowable(rxEvent.getData()));
                                } else if (rxEvent.getType() == RemoteRxEvent.Type.completed) {
                                    metrics.incrementCompletedCount();
                                    return Notification.createOnCompleted();
                                } else {
                                    throw new RuntimeException("RemoteRxEvent of type: " + rxEvent.getType() + ", not supported.");
                                }
                            }
                        })
                        .dematerialize()
                        .doOnEach(new Observer() {
                            @Override
                            public void onCompleted() {
                                logger.info("RemoteRxEvent: " + params.getName() + " onCompleted()");
                            }

                            @Override
                            public void onError(Throwable e) {
                                logger.error("RemoteRxEvent: " + params.getName() + " onError()", e);
                            }

                            @Override
                            public void onNext(T t) {
                                if (logger.isDebugEnabled()) {
                                    logger.debug("RemoteRxEvent: " + params.getName() + " onNext(): " + t);
                                }
                            }
                        });
    }

    public static  RemoteRxServer serve(int port, final Observable observable, final Encoder encoder) {
        return new RemoteRxServer(configureServerFromParams(null, port, observable, encoder,
                IngressPolicies.allowAll()));
    }

    public static  RemoteRxServer serve(int port, String name, final Observable observable, final Encoder encoder) {
        return new RemoteRxServer(configureServerFromParams(name, port, observable, encoder,
                IngressPolicies.allowAll()));
    }

    private static  RemoteRxServer.Builder configureServerFromParams(String name, int port, Observable observable,
                                                                        Encoder encoder, IngressPolicy ingressPolicy) {
        return new RemoteRxServer
                .Builder()
                .port(port)
                .ingressPolicy(ingressPolicy)
                .addObservable(new ServeObservable.Builder()
                        .name(name)
                        .encoder(encoder)
                        .observable(observable)
                        .subscriptionPerConnection()
                        .build());
    }

    static byte[] fromThrowableToBytes(Throwable t) {

        ByteArrayOutputStream baos = null;
        ObjectOutput out = null;
        try {
            // create a new exception
            // throw away data that may not
            // be serializable
            Constructor con = t.getClass().getConstructor(String.class);
            Throwable newInstance = con.newInstance(t.getMessage());
            newInstance.setStackTrace(t.getStackTrace());
            baos = new ByteArrayOutputStream();
            out = new ObjectOutputStream(baos);
            out.writeObject(newInstance);
        } catch (IOException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
            logger.error("Failed to convert throwable to bytes", e);
            throw new RuntimeException(e);
        } finally {
            try {
                if (out != null) {out.close();}
                if (baos != null) {baos.close();}
            } catch (IOException e1) {
                e1.printStackTrace();
                throw new RuntimeException(e1);
            }
        }
        return baos.toByteArray();
    }

    static Throwable fromBytesToThrowable(byte[] bytes) {
        Throwable t = null;
        ByteArrayInputStream bis = null;
        ObjectInput in = null;
        try {
            bis = new ByteArrayInputStream(bytes);
            in = new ObjectInputStream(bis);
            t = (Throwable) in.readObject();

        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e1) {
            throw new RuntimeException(e1);
        } finally {
            try {
                if (bis != null) {bis.close();}
                if (in != null) {in.close();}
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return t;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy