001package co.codewizards.cloudstore.core.dto;
002
003import static java.util.Objects.*;
004
005import java.lang.reflect.Constructor;
006import java.lang.reflect.InvocationTargetException;
007
008public final class RemoteExceptionUtil {
009
010        private RemoteExceptionUtil() { }
011
012        public static void throwOriginalExceptionIfPossible(final Error error) {
013                Throwable throwable = toThrowable(error);
014
015                if (throwable instanceof RuntimeException)
016                        throw (RuntimeException) throwable;
017
018                if (throwable instanceof java.lang.Error)
019                        throw (java.lang.Error) throwable;
020
021                throw new RuntimeException(throwable);
022        }
023
024        private static Throwable toThrowable(Error error) {
025                return toThrowable(error, false);
026        }
027
028        private static Throwable toThrowable(Error error, boolean nested) {
029                if (error == null)
030                        return null;
031
032                Throwable cause = toThrowable(error.getCause(), true);
033
034                Class<?> clazz;
035                try {
036                        clazz = Class.forName(error.getClassName());
037                } catch (final ClassNotFoundException e) {
038                        return new RemoteException(error, true).initCause(cause);
039                }
040                if (!Throwable.class.isAssignableFrom(clazz))
041                        return new RemoteException(error, true).initCause(cause);
042
043                @SuppressWarnings("unchecked")
044                final Class<? extends Throwable> clasz = (Class<? extends Throwable>) clazz;
045
046                Throwable throwable = null;
047
048                // trying XyzException(String message, Throwable cause)
049                if (throwable == null)
050                        throwable = getObjectOrNull(clasz, new Class<?>[] { String.class, Throwable.class }, error.getMessage(), cause);
051
052                // trying XyzException(String message)
053                if (throwable == null)
054                        throwable = getObjectOrNull(clasz, new Class<?>[] { String.class }, error.getMessage());
055
056                // trying XyzException(Throwable cause)
057                if (throwable == null)
058                        throwable = getObjectOrNull(clasz, new Class<?>[] { Throwable.class }, cause);
059
060                // trying XyzException()
061                if (throwable == null)
062                        throwable = getObjectOrNull(clasz, null);
063
064                // LAST: falling back to RemoteException
065                if (throwable == null)
066                        throwable = new RemoteException(error, true);
067
068                try {
069                        throwable.initCause(cause);
070                } catch (final Exception x) {
071                        // This happens, if either the cause was already set in an appropriate constructor (see above)
072                        // or the concrete Throwable does not support it. If we were unable to set the cause we want,
073                        // we better use a RemoteException and not the original one.
074                        if (throwable.getCause() != cause)
075                                return new RemoteException(error, true).initCause(cause);
076                }
077                initStackTrace(throwable, error, nested);
078                return throwable;
079        }
080
081        private static void initStackTrace(Throwable throwable, Error error, boolean replaceOriginalStackTrace) {
082                requireNonNull(throwable, "throwable");
083                requireNonNull(error, "error");
084
085                int idx = -1;
086                StackTraceElement[] origStackTrace = replaceOriginalStackTrace ? new StackTraceElement[0] : throwable.getStackTrace();
087                StackTraceElement[] stackTrace = new StackTraceElement[origStackTrace.length + error.getStackTraceElements().size()];
088
089                for (ErrorStackTraceElement errorStackTraceElement : error.getStackTraceElements()) {
090                        stackTrace[++idx] = new StackTraceElement(
091                                        errorStackTraceElement.getClassName(),
092                                        errorStackTraceElement.getMethodName(),
093                                        errorStackTraceElement.getFileName(),
094                                        errorStackTraceElement.getLineNumber()
095                                        );
096                }
097
098                for (StackTraceElement stackTraceElement : origStackTrace) {
099                        stackTrace[++idx] = stackTraceElement;
100                }
101
102                throwable.setStackTrace(stackTrace);
103        }
104
105        private static <T> T getObjectOrNull(final Class<T> clazz, Class<?>[] argumentTypes, final Object ... arguments) {
106                T result = null;
107                if (argumentTypes == null)
108                        argumentTypes = new Class<?> [0];
109
110                if (argumentTypes.length == 0) {
111                        try {
112                                result = clazz.newInstance();
113                        } catch (final InstantiationException e) {
114                                return null;
115                        } catch (final IllegalAccessException e) {
116                                return null;
117                        }
118                }
119
120                if (result == null) {
121                        Constructor<T> constructor;
122                        try {
123                                constructor = clazz.getConstructor(argumentTypes);
124                        } catch (final NoSuchMethodException e) {
125                                return null;
126                        } catch (final SecurityException e) {
127                                return null;
128                        }
129
130                        try {
131                                result = constructor.newInstance(arguments);
132                        } catch (final InstantiationException e) {
133                                return null;
134                        } catch (final IllegalAccessException e) {
135                                return null;
136                        } catch (final IllegalArgumentException e) {
137                                return null;
138                        } catch (final InvocationTargetException e) {
139                                return null;
140                        }
141                }
142
143                return result;
144        }
145}