/* * Copyright 2015 Jacek Marchwicki <jacek.marchwicki@gmail.com> * * 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 com.appunite.rx.operators; import javax.annotation.Nonnull; import javax.annotation.Nullable; import rx.Observable; import rx.Subscriber; /** * Nice error operator is used to print nice stacktrace for your code with some additional info * * Normally when you implement code like this: * <pre> * class YourClass { * void yourMethod() { * Observable.error(new Exception()) * .subscribe(); * } * } * </pre> * * You will get stack trace like: * * <pre> * Caused by rx.exceptions.OnErrorNotImplementedException * rx.Observable$30.onError (Observable.java:7358) * rx.observers.SafeSubscriber._onError (SafeSubscriber.java:154) * rx.observers.SafeSubscriber.onError (SafeSubscriber.java:111) * </pre> * * This does not mean a lot, so here helps {@link NiceErrorOperator#niceErrorOperator()}, so type: * * <pre> * class YourClass { * void yourMethod() { * Observable.error(new Exception()) * .lift(NiceErrorOperator.niceErrorOperator("Your message")) * .subscribe(); * } * } * </pre> * * You will get error like: * <pre> * Caused by NiceRxError Your message * - YourClass.yourMethod (YourClass.java:5) * - YourClassCaller.callerMethod (YourClassCaller.java:710) * - ... * Caused by rx.exceptions.OnErrorNotImplementedException * rx.Observable$30.onError (Observable.java:7358) * rx.observers.SafeSubscriber._onError (SafeSubscriber.java:154) * rx.observers.SafeSubscriber.onError (SafeSubscriber.java:111) * </pre> * * niceErrorOperator leave information about stack trace to place where it was called, here * in line 5 of YourClass.java file. * * Info: com.appunite.rx.android.LifecycleMainObservable#bindLifecycle() already contains handler * */ public class NiceErrorOperator { /** * Attach to error stack trace your message and calling stack trace * * @param message message to add to stacktrace (optional) * @see NiceErrorOperator */ @Nonnull public static <T> Observable.Operator<T, T> niceErrorOperator(@Nullable final String message) { final StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); return niceErrorOperator(message, stackTraceElements); } /** * Attach to error stack trace and message * * @param message message to attach (optional) * @param stackTraceElements stack trace to attach (optional) * @see NiceErrorOperator */ @Nonnull public static <T> Observable.Operator<T, T> niceErrorOperator( @Nullable final String message, @Nullable final StackTraceElement[] stackTraceElements) { return new Observable.Operator<T, T>() { @Override public Subscriber<? super T> call(final Subscriber<? super T> c) { return new Subscriber<T>(c) { @Override public void onCompleted() { c.onCompleted(); } @Override public void onError(Throwable e) { c.onError(new NiceRxError(message, stackTraceElements, e)); } @Override public void onNext(T o) { try { c.onNext(o); } catch (Throwable e) { throw new NiceRxError(message, stackTraceElements, e); } } }; } }; } @Nonnull private static String traceToString(@Nonnull final StackTraceElement[] stackTraceElements) { final StringBuilder sb = new StringBuilder(); for (StackTraceElement stackTraceEl : stackTraceElements) { sb.append("- "); sb.append(stackTraceEl); sb.append("\n"); } return sb.toString(); } @Nonnull private static String getErrorMessage(@Nullable String userMessage, @Nullable StackTraceElement[] stackTraceElements) { if (stackTraceElements != null) { final String trace = traceToString(stackTraceElements); if (userMessage != null) { return userMessage + ": " + trace; } else { return trace; } } else { if (userMessage != null) { return userMessage; } else { return "Unknown message"; } } } /** * Attach calling method stack trace to your rx java error * @see NiceErrorOperator */ @Nonnull public static <T> Observable.Operator<T, T> niceErrorOperator() { final StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); return niceErrorOperator(null, stackTraceElements); } /** * Nicer rx error with attached additional info */ public static class NiceRxError extends RuntimeException { @Nullable private final String userMessage; @Nullable private final StackTraceElement[] stackTraceElements; NiceRxError(@Nullable String userMessage, @Nullable StackTraceElement[] stackTraceElements, @Nullable Throwable cause) { super(getErrorMessage(userMessage, stackTraceElements), cause); this.userMessage = userMessage; this.stackTraceElements = stackTraceElements; } /** * User message * * @return user message */ @Nullable public String userMessage() { return userMessage; } /** * User stack trace * * @return user stack trace */ @Nullable public StackTraceElement[] stackTraceElements() { return stackTraceElements; } } }