From f4e7bc4807d97cd6ef41c44c340e1e1b9c048f0a Mon Sep 17 00:00:00 2001 From: Artem Zinnatullin Date: Tue, 19 Jul 2016 15:34:30 +0300 Subject: [PATCH] 1.x: Do not hide original exception with RxJavaHooks.enableAssemblyTracking() --- .../AssemblyStackTraceException.java | 52 ----------- .../operators/OnSubscribeOnAssembly.java | 47 ++++++---- .../OnSubscribeOnAssemblyCompletable.java | 16 ++-- .../OnSubscribeOnAssemblySingle.java | 16 ++-- src/test/java/rx/plugins/RxJavaHooksTest.java | 87 +++++++++++++++---- 5 files changed, 112 insertions(+), 106 deletions(-) delete mode 100644 src/main/java/rx/exceptions/AssemblyStackTraceException.java diff --git a/src/main/java/rx/exceptions/AssemblyStackTraceException.java b/src/main/java/rx/exceptions/AssemblyStackTraceException.java deleted file mode 100644 index 752e8aad87..0000000000 --- a/src/main/java/rx/exceptions/AssemblyStackTraceException.java +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright 2016 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 rx.exceptions; - -import rx.annotations.Experimental; - -/** - * A RuntimeException that is stackless but holds onto a textual - * stacktrace from tracking the assembly location of operators. - */ -@Experimental -public final class AssemblyStackTraceException extends RuntimeException { - - /** */ - private static final long serialVersionUID = 2038859767182585852L; - - /** - * Constructs an AssemblyStackTraceException with the given message and - * a cause. - * @param message the message - * @param cause the cause - */ - public AssemblyStackTraceException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Constructs an AssemblyStackTraceException with the given message. - * @param message the message - */ - public AssemblyStackTraceException(String message) { - super(message); - } - - @Override - public synchronized Throwable fillInStackTrace() { // NOPMD - return this; - } -} diff --git a/src/main/java/rx/internal/operators/OnSubscribeOnAssembly.java b/src/main/java/rx/internal/operators/OnSubscribeOnAssembly.java index 11c7bcff0e..8cab50798d 100644 --- a/src/main/java/rx/internal/operators/OnSubscribeOnAssembly.java +++ b/src/main/java/rx/internal/operators/OnSubscribeOnAssembly.java @@ -15,9 +15,10 @@ */ package rx.internal.operators; +import java.util.ArrayList; +import java.util.List; import rx.Observable.OnSubscribe; import rx.Subscriber; -import rx.exceptions.AssemblyStackTraceException; /** * Captures the current stack when it is instantiated, makes @@ -30,7 +31,7 @@ public final class OnSubscribeOnAssembly implements OnSubscribe { final OnSubscribe source; - final String stacktrace; + final StackTraceElement[] assemblyStacktrace; /** * If set to true, the creation of PublisherOnAssembly will capture the raw @@ -40,15 +41,14 @@ public final class OnSubscribeOnAssembly implements OnSubscribe { public OnSubscribeOnAssembly(OnSubscribe source) { this.source = source; - this.stacktrace = createStacktrace(); + this.assemblyStacktrace = assemblyStacktrace(); } - static String createStacktrace() { - StackTraceElement[] stes = Thread.currentThread().getStackTrace(); + static StackTraceElement[] assemblyStacktrace() { + final StackTraceElement[] in = new Exception().getStackTrace(); + final List out = new ArrayList(in.length); - StringBuilder sb = new StringBuilder("Assembly trace:"); - - for (StackTraceElement e : stes) { + for (StackTraceElement e : in) { String row = e.toString(); if (!fullStackTrace) { if (e.getLineNumber() <= 1) { @@ -85,27 +85,41 @@ static String createStacktrace() { continue; } } - sb.append("\n at ").append(row); + out.add(e); } - - return sb.append("\nOriginal exception:").toString(); + + return out.toArray(new StackTraceElement[out.size()]); + } + + static Throwable addAssembly(Throwable original, StackTraceElement[] assemblyStacktrace) { + final StackTraceElement[] originalStacktrace = original.getStackTrace(); + final StackTraceElement[] resultingStacktrace = new StackTraceElement[originalStacktrace.length + assemblyStacktrace.length]; + + System.arraycopy(originalStacktrace, 0, resultingStacktrace, 0, originalStacktrace.length); + + System.arraycopy(assemblyStacktrace, 0, resultingStacktrace, + originalStacktrace.length, assemblyStacktrace.length); + + original.setStackTrace(resultingStacktrace); + + return original; } @Override public void call(Subscriber t) { - source.call(new OnAssemblySubscriber(t, stacktrace)); + source.call(new OnAssemblySubscriber(t, assemblyStacktrace)); } static final class OnAssemblySubscriber extends Subscriber { final Subscriber actual; - final String stacktrace; + final StackTraceElement[] assemblyStacktrace; - public OnAssemblySubscriber(Subscriber actual, String stacktrace) { + public OnAssemblySubscriber(Subscriber actual, StackTraceElement[] assemblyStacktrace) { super(actual); this.actual = actual; - this.stacktrace = stacktrace; + this.assemblyStacktrace = assemblyStacktrace; } @Override @@ -115,8 +129,7 @@ public void onCompleted() { @Override public void onError(Throwable e) { - e = new AssemblyStackTraceException(stacktrace, e); - actual.onError(e); + actual.onError(addAssembly(e, assemblyStacktrace)); } @Override diff --git a/src/main/java/rx/internal/operators/OnSubscribeOnAssemblyCompletable.java b/src/main/java/rx/internal/operators/OnSubscribeOnAssemblyCompletable.java index da5a144045..483b04d69f 100644 --- a/src/main/java/rx/internal/operators/OnSubscribeOnAssemblyCompletable.java +++ b/src/main/java/rx/internal/operators/OnSubscribeOnAssemblyCompletable.java @@ -17,7 +17,6 @@ import rx.*; import rx.Completable.CompletableSubscriber; -import rx.exceptions.AssemblyStackTraceException; /** * Captures the current stack when it is instantiated, makes @@ -30,7 +29,7 @@ public final class OnSubscribeOnAssemblyCompletable implements Completable.Co final Completable.CompletableOnSubscribe source; - final String stacktrace; + final StackTraceElement[] assemblyStacktrace; /** * If set to true, the creation of PublisherOnAssembly will capture the raw @@ -40,23 +39,23 @@ public final class OnSubscribeOnAssemblyCompletable implements Completable.Co public OnSubscribeOnAssemblyCompletable(Completable.CompletableOnSubscribe source) { this.source = source; - this.stacktrace = OnSubscribeOnAssembly.createStacktrace(); + this.assemblyStacktrace = OnSubscribeOnAssembly.assemblyStacktrace(); } @Override public void call(Completable.CompletableSubscriber t) { - source.call(new OnAssemblyCompletableSubscriber(t, stacktrace)); + source.call(new OnAssemblyCompletableSubscriber(t, assemblyStacktrace)); } static final class OnAssemblyCompletableSubscriber implements CompletableSubscriber { final CompletableSubscriber actual; - final String stacktrace; + final StackTraceElement[] assemblyStacktrace; - public OnAssemblyCompletableSubscriber(CompletableSubscriber actual, String stacktrace) { + public OnAssemblyCompletableSubscriber(CompletableSubscriber actual, StackTraceElement[] assemblyStacktrace) { this.actual = actual; - this.stacktrace = stacktrace; + this.assemblyStacktrace = assemblyStacktrace; } @Override @@ -71,8 +70,7 @@ public void onCompleted() { @Override public void onError(Throwable e) { - e = new AssemblyStackTraceException(stacktrace, e); - actual.onError(e); + actual.onError(OnSubscribeOnAssembly.addAssembly(e, assemblyStacktrace)); } } } diff --git a/src/main/java/rx/internal/operators/OnSubscribeOnAssemblySingle.java b/src/main/java/rx/internal/operators/OnSubscribeOnAssemblySingle.java index aeed623a7b..6eeca62cb8 100644 --- a/src/main/java/rx/internal/operators/OnSubscribeOnAssemblySingle.java +++ b/src/main/java/rx/internal/operators/OnSubscribeOnAssemblySingle.java @@ -16,7 +16,6 @@ package rx.internal.operators; import rx.*; -import rx.exceptions.AssemblyStackTraceException; /** * Captures the current stack when it is instantiated, makes @@ -29,7 +28,7 @@ public final class OnSubscribeOnAssemblySingle implements Single.OnSubscribe< final Single.OnSubscribe source; - final String stacktrace; + final StackTraceElement[] assemblyStacktrace; /** * If set to true, the creation of PublisherOnAssembly will capture the raw @@ -39,30 +38,29 @@ public final class OnSubscribeOnAssemblySingle implements Single.OnSubscribe< public OnSubscribeOnAssemblySingle(Single.OnSubscribe source) { this.source = source; - this.stacktrace = OnSubscribeOnAssembly.createStacktrace(); + this.assemblyStacktrace = OnSubscribeOnAssembly.assemblyStacktrace(); } @Override public void call(SingleSubscriber t) { - source.call(new OnAssemblySingleSubscriber(t, stacktrace)); + source.call(new OnAssemblySingleSubscriber(t, assemblyStacktrace)); } static final class OnAssemblySingleSubscriber extends SingleSubscriber { final SingleSubscriber actual; - final String stacktrace; + final StackTraceElement[] assemblyStacktrace; - public OnAssemblySingleSubscriber(SingleSubscriber actual, String stacktrace) { + public OnAssemblySingleSubscriber(SingleSubscriber actual, StackTraceElement[] assemblyStacktrace) { this.actual = actual; - this.stacktrace = stacktrace; + this.assemblyStacktrace = assemblyStacktrace; actual.add(this); } @Override public void onError(Throwable e) { - e = new AssemblyStackTraceException(stacktrace, e); - actual.onError(e); + actual.onError(OnSubscribeOnAssembly.addAssembly(e, assemblyStacktrace)); } @Override diff --git a/src/test/java/rx/plugins/RxJavaHooksTest.java b/src/test/java/rx/plugins/RxJavaHooksTest.java index 9209228124..57d0545f25 100644 --- a/src/test/java/rx/plugins/RxJavaHooksTest.java +++ b/src/test/java/rx/plugins/RxJavaHooksTest.java @@ -50,7 +50,17 @@ public Integer call(Integer t) { } }); } - + + static boolean stacktraceContainsString(Throwable throwable, String expectedString) { + for (StackTraceElement element : throwable.getStackTrace()) { + if (element.toString().contains(expectedString)) { + return true; + } + } + + return false; + } + @Test public void constructorShouldBePrivate() { TestUtil.checkUtilityClass(RxJavaHooks.class); @@ -64,16 +74,14 @@ public void assemblyTrackingObservable() { createObservable().subscribe(ts); - ts.assertError(AssemblyStackTraceException.class); + ts.assertError(TestException.class); Throwable ex = ts.getOnErrorEvents().get(0); - assertTrue("" + ex.getCause(), ex.getCause() instanceof TestException); - - assertTrue("" + ex, ex instanceof AssemblyStackTraceException); - - assertTrue(ex.getMessage(), ex.getMessage().contains("createObservable")); - + assertFalse(ex.getCause() instanceof TestException); + + assertTrue(stacktraceContainsString(ex, "createObservable")); + RxJavaHooks.clearAssemblyTracking(); ts = TestSubscriber.create(); @@ -85,6 +93,21 @@ public void assemblyTrackingObservable() { RxJavaHooks.resetAssemblyTracking(); } } + + @Test + public void noAssemblyTrackingObservable() { + TestSubscriber ts = TestSubscriber.create(); + + createObservable().subscribe(ts); + + ts.assertError(TestException.class); + + Throwable ex = ts.getOnErrorEvents().get(0); + + assertFalse(ex.getCause() instanceof TestException); + + assertFalse(stacktraceContainsString(ex, "createObservable")); + } static Single createSingle() { return Single.just(1).map(new Func1() { @@ -103,15 +126,13 @@ public void assemblyTrackingSingle() { createSingle().subscribe(ts); - ts.assertError(AssemblyStackTraceException.class); + ts.assertError(TestException.class); Throwable ex = ts.getOnErrorEvents().get(0); - assertTrue("" + ex, ex instanceof AssemblyStackTraceException); + assertFalse(ex.getCause() instanceof TestException); - assertTrue("" + ex.getCause(), ex.getCause() instanceof TestException); - - assertTrue(ex.getMessage(), ex.getMessage().contains("createSingle")); + assertTrue(stacktraceContainsString(ex, "createSingle")); RxJavaHooks.clearAssemblyTracking(); @@ -124,7 +145,22 @@ public void assemblyTrackingSingle() { RxJavaHooks.resetAssemblyTracking(); } } - + + @Test + public void noAssemblyTrackingSingle() { + TestSubscriber ts = TestSubscriber.create(); + + createSingle().subscribe(ts); + + ts.assertError(TestException.class); + + Throwable ex = ts.getOnErrorEvents().get(0); + + assertFalse(ex.getCause() instanceof TestException); + + assertFalse(stacktraceContainsString(ex, "createSingle")); + } + static Completable createCompletable() { return Completable.error(new Func0() { @Override @@ -142,15 +178,13 @@ public void assemblyTrackingCompletable() { createCompletable().subscribe(ts); - ts.assertError(AssemblyStackTraceException.class); + ts.assertError(TestException.class); Throwable ex = ts.getOnErrorEvents().get(0); - assertTrue("" + ex, ex instanceof AssemblyStackTraceException); - - assertTrue("" + ex.getCause(), ex.getCause() instanceof TestException); + assertFalse(ex.getCause() instanceof TestException); - assertTrue(ex.getMessage(), ex.getMessage().contains("createCompletable")); + assertTrue(stacktraceContainsString(ex, "createCompletable")); RxJavaHooks.clearAssemblyTracking(); @@ -164,6 +198,21 @@ public void assemblyTrackingCompletable() { RxJavaHooks.resetAssemblyTracking(); } } + + @Test + public void noAssemblyTracking() { + TestSubscriber ts = TestSubscriber.create(); + + createCompletable().subscribe(ts); + + ts.assertError(TestException.class); + + Throwable ex = ts.getOnErrorEvents().get(0); + + assertFalse(ex.getCause() instanceof TestException); + + assertFalse(stacktraceContainsString(ex, "createCompletable")); + } @SuppressWarnings("rawtypes") @Test