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