diff --git a/all/build.gradle b/all/build.gradle index a84d9eeda5a..dd32d9cce20 100644 --- a/all/build.gradle +++ b/all/build.gradle @@ -14,6 +14,7 @@ buildscript { def subprojects = [ project(':grpc-auth'), project(':grpc-core'), + project(':grpc-context'), project(':grpc-netty'), project(':grpc-okhttp'), project(':grpc-protobuf'), diff --git a/context/build.gradle b/context/build.gradle new file mode 100644 index 00000000000..0566f6a0f31 --- /dev/null +++ b/context/build.gradle @@ -0,0 +1,14 @@ +plugins { + id "be.insaneprogramming.gradle.animalsniffer" version "1.4.0" +} + +description = 'gRPC: Context' + +dependencies { + testCompile project(':grpc-testing') +} + +// Configure the animal sniffer plugin +animalsniffer { + signature = "org.codehaus.mojo.signature:java16:+@signature" +} diff --git a/core/src/main/java/io/grpc/Context.java b/context/src/main/java/io/grpc/Context.java similarity index 96% rename from core/src/main/java/io/grpc/Context.java rename to context/src/main/java/io/grpc/Context.java index 810396f91e5..60e060d26ca 100644 --- a/core/src/main/java/io/grpc/Context.java +++ b/context/src/main/java/io/grpc/Context.java @@ -31,9 +31,6 @@ package io.grpc; -import com.google.common.base.Preconditions; -import com.google.common.util.concurrent.MoreExecutors; - import java.util.ArrayList; import java.util.concurrent.Callable; import java.util.concurrent.Executor; @@ -44,8 +41,6 @@ import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.Nullable; - /** * A context propagation mechanism which can carry scoped-values across API boundaries and between * threads. Examples of state propagated via context include: @@ -275,8 +270,8 @@ public CancellableContext withDeadlineAfter(long duration, TimeUnit unit, */ public CancellableContext withDeadline(Deadline deadline, ScheduledExecutorService scheduler) { - Preconditions.checkNotNull(deadline, "deadline"); - Preconditions.checkNotNull(scheduler, "scheduler"); + checkNotNull(deadline, "deadline"); + checkNotNull(scheduler, "scheduler"); return new CancellableContext(this, deadline, scheduler); } @@ -349,7 +344,7 @@ public Context attach() { * will still be bound. */ public void detach(Context toAttach) { - Preconditions.checkNotNull(toAttach); + checkNotNull(toAttach, "toAttach"); if (toAttach.attach() != this) { // Log a severe message instead of throwing an exception as the context to attach is assumed // to be the correct one and the unbalanced state represents a coding mistake in a lower @@ -383,7 +378,6 @@ public boolean isCancelled() { *

The cancellation cause is provided for informational purposes only and implementations * should generally assume that it has already been handled and logged properly. */ - @Nullable public Throwable cancellationCause() { if (parent == null || !cascadesCancellation) { return null; @@ -396,7 +390,6 @@ public Throwable cancellationCause() { * A context may have an associated {@link Deadline} at which it will be automatically cancelled. * @return A {@link io.grpc.Deadline} or {@code null} if no deadline is set. */ - @Nullable public Deadline getDeadline() { return DEADLINE_KEY.get(this); } @@ -406,8 +399,8 @@ public Deadline getDeadline() { */ public void addListener(final CancellationListener cancellationListener, final Executor executor) { - Preconditions.checkNotNull(cancellationListener); - Preconditions.checkNotNull(executor); + checkNotNull(cancellationListener, "cancellationListener"); + checkNotNull(executor, "executor"); if (canBeCancelled) { ExecutableListener executableListener = new ExecutableListener(executor, cancellationListener); @@ -420,7 +413,7 @@ public void addListener(final CancellationListener cancellationListener, // we can cascade listener notification. listeners = new ArrayList(); listeners.add(executableListener); - parent.addListener(parentListener, MoreExecutors.directExecutor()); + parent.addListener(parentListener, DirectExecutor.INSTANCE); } else { listeners.add(executableListener); } @@ -685,13 +678,13 @@ public boolean isCurrent() { } /** - * Cancel this context and optionally provide a cause for the cancellation. This - * will trigger notification of listeners. + * Cancel this context and optionally provide a cause (can be {@code null}) for the + * cancellation. This will trigger notification of listeners. * * @return {@code true} if this context cancelled the context and notified listeners, * {@code false} if the context was already cancelled. */ - public boolean cancel(@Nullable Throwable cause) { + public boolean cancel(Throwable cause) { boolean triggeredCancel = false; synchronized (this) { if (!cancelled) { @@ -717,7 +710,7 @@ public boolean cancel(@Nullable Throwable cause) { * @param toAttach context to make current. * @param cause of cancellation, can be {@code null}. */ - public void detachAndCancel(Context toAttach, @Nullable Throwable cause) { + public void detachAndCancel(Context toAttach, Throwable cause) { try { detach(toAttach); } finally { @@ -741,7 +734,6 @@ public boolean isCancelled() { return false; } - @Nullable @Override public Throwable cancellationCause() { if (isCancelled()) { @@ -774,7 +766,7 @@ public static class Key { } Key(String name, T defaultValue) { - this.name = Preconditions.checkNotNull(name); + this.name = checkNotNull(name, "name"); this.defaultValue = defaultValue; } @@ -838,4 +830,25 @@ public void cancelled(Context context) { } } } + + private static T checkNotNull(T reference, Object errorMessage) { + if (reference == null) { + throw new NullPointerException(String.valueOf(errorMessage)); + } + return reference; + } + + private enum DirectExecutor implements Executor { + INSTANCE; + + @Override + public void execute(Runnable command) { + command.run(); + } + + @Override + public String toString() { + return "Context.DirectExecutor"; + } + } } diff --git a/core/src/main/java/io/grpc/Deadline.java b/context/src/main/java/io/grpc/Deadline.java similarity index 83% rename from core/src/main/java/io/grpc/Deadline.java rename to context/src/main/java/io/grpc/Deadline.java index d23a8cf509b..3514ddf1a96 100644 --- a/core/src/main/java/io/grpc/Deadline.java +++ b/context/src/main/java/io/grpc/Deadline.java @@ -31,14 +31,11 @@ package io.grpc; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Preconditions; - -import io.grpc.internal.LogExceptionRunnable; - import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; /** * An absolute deadline in system time. @@ -60,9 +57,9 @@ public static Deadline after(long duration, TimeUnit units) { return after(duration, units, SYSTEM_TICKER); } - @VisibleForTesting + // For testing static Deadline after(long duration, TimeUnit units, Ticker ticker) { - Preconditions.checkNotNull(units); + checkNotNull(units, "units"); return new Deadline(ticker, units.toNanos(duration), true); } @@ -148,8 +145,8 @@ public long timeRemaining(TimeUnit unit) { * @return {@link ScheduledFuture} which can be used to cancel execution of the task */ public ScheduledFuture runOnExpiration(Runnable task, ScheduledExecutorService scheduler) { - Preconditions.checkNotNull(task, "task"); - Preconditions.checkNotNull(scheduler, "scheduler"); + checkNotNull(task, "task"); + checkNotNull(scheduler, "scheduler"); return scheduler.schedule(new LogExceptionRunnable(task), deadlineNanos - ticker.read(), TimeUnit.NANOSECONDS); } @@ -182,4 +179,42 @@ public long read() { return System.nanoTime(); } } + + private static T checkNotNull(T reference, Object errorMessage) { + if (reference == null) { + throw new NullPointerException(String.valueOf(errorMessage)); + } + return reference; + } + + private static class LogExceptionRunnable implements Runnable { + private static final Logger log = Logger.getLogger(LogExceptionRunnable.class.getName()); + + private final Runnable task; + + public LogExceptionRunnable(Runnable task) { + this.task = checkNotNull(task, "task"); + } + + @Override + public void run() { + try { + task.run(); + } catch (Throwable t) { + log.log(Level.SEVERE, "Exception while executing runnable " + task, t); + if (t instanceof RuntimeException) { + throw (RuntimeException) t; + } else if (t instanceof Error) { + throw (Error) t; + } else { + throw new RuntimeException(t); + } + } + } + + @Override + public String toString() { + return "Deadline.LogExceptionRunnable(" + task + ")"; + } + } } diff --git a/core/src/test/java/io/grpc/ContextTest.java b/context/src/test/java/io/grpc/ContextTest.java similarity index 100% rename from core/src/test/java/io/grpc/ContextTest.java rename to context/src/test/java/io/grpc/ContextTest.java diff --git a/core/src/test/java/io/grpc/DeadlineTest.java b/context/src/test/java/io/grpc/DeadlineTest.java similarity index 99% rename from core/src/test/java/io/grpc/DeadlineTest.java rename to context/src/test/java/io/grpc/DeadlineTest.java index 2df9959d32e..f85edf18cd3 100644 --- a/core/src/test/java/io/grpc/DeadlineTest.java +++ b/context/src/test/java/io/grpc/DeadlineTest.java @@ -265,7 +265,7 @@ public void toString_before() { assertEquals("12000 ns from now", d.toString()); } - static class FakeTicker extends Deadline.Ticker { + private static class FakeTicker extends Deadline.Ticker { private long time; @Override diff --git a/core/build.gradle b/core/build.gradle index f7ccc82a75c..d851e38f58e 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -6,7 +6,8 @@ description = 'gRPC: Core' dependencies { compile libraries.guava, - libraries.jsr305 + libraries.jsr305, + project(':grpc-context') testCompile project(':grpc-testing') } diff --git a/core/src/test/java/io/grpc/CallOptionsTest.java b/core/src/test/java/io/grpc/CallOptionsTest.java index 40b46356c4e..c295d8088c4 100644 --- a/core/src/test/java/io/grpc/CallOptionsTest.java +++ b/core/src/test/java/io/grpc/CallOptionsTest.java @@ -51,13 +51,14 @@ import org.junit.runners.JUnit4; import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; /** Unit tests for {@link CallOptions}. */ @RunWith(JUnit4.class) public class CallOptionsTest { private String sampleAuthority = "authority"; private String sampleCompressor = "compressor"; - private Deadline.Ticker ticker = new DeadlineTest.FakeTicker(); + private Deadline.Ticker ticker = new FakeTicker(); private Deadline sampleDeadline = Deadline.after(1, NANOSECONDS, ticker); private Key sampleKey = Attributes.Key.of("sample"); private Attributes sampleAffinity = Attributes.newBuilder().set(sampleKey, "blah").build(); @@ -233,4 +234,24 @@ private static boolean equal(CallOptions o1, CallOptions o2) { && Objects.equal(o1.getAffinity(), o2.getAffinity()) && Objects.equal(o1.getCredentials(), o2.getCredentials()); } + + private static class FakeTicker extends Deadline.Ticker { + private long time; + + @Override + public long read() { + return time; + } + + public void reset(long time) { + this.time = time; + } + + public void increment(long period, TimeUnit unit) { + if (period < 0) { + throw new IllegalArgumentException(); + } + this.time += unit.toNanos(period); + } + } } diff --git a/settings.gradle b/settings.gradle index 42496c2eeb3..b991a930b60 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,6 @@ rootProject.name = "grpc" include ":grpc-core" +include ":grpc-context" include ":grpc-stub" include ":grpc-auth" include ":grpc-okhttp" @@ -15,6 +16,7 @@ include ":grpc-benchmarks" include ":grpc-services" project(':grpc-core').projectDir = "$rootDir/core" as File +project(':grpc-context').projectDir = "$rootDir/context" as File project(':grpc-stub').projectDir = "$rootDir/stub" as File project(':grpc-auth').projectDir = "$rootDir/auth" as File project(':grpc-okhttp').projectDir = "$rootDir/okhttp" as File