core: split Context into a separate grpc-context artifact.

The Context API is not particularly gRPC-specific, and will be used by
Census as its context propagation mechanism.

Removed all dependencies to make it easy for other libraries to depend
on.
This commit is contained in:
Kun Zhang 2016-09-01 16:00:43 -07:00
parent 58d78dd0aa
commit c4f7f5c4fd
9 changed files with 85 additions and 29 deletions

View File

@ -14,6 +14,7 @@ buildscript {
def subprojects = [ def subprojects = [
project(':grpc-auth'), project(':grpc-auth'),
project(':grpc-core'), project(':grpc-core'),
project(':grpc-context'),
project(':grpc-netty'), project(':grpc-netty'),
project(':grpc-okhttp'), project(':grpc-okhttp'),
project(':grpc-protobuf'), project(':grpc-protobuf'),

14
context/build.gradle Normal file
View File

@ -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"
}

View File

@ -31,9 +31,6 @@
package io.grpc; package io.grpc;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@ -44,8 +41,6 @@ import java.util.concurrent.TimeoutException;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
/** /**
* A context propagation mechanism which can carry scoped-values across API boundaries and between * A context propagation mechanism which can carry scoped-values across API boundaries and between
* threads. Examples of state propagated via context include: * threads. Examples of state propagated via context include:
@ -275,8 +270,8 @@ public class Context {
*/ */
public CancellableContext withDeadline(Deadline deadline, public CancellableContext withDeadline(Deadline deadline,
ScheduledExecutorService scheduler) { ScheduledExecutorService scheduler) {
Preconditions.checkNotNull(deadline, "deadline"); checkNotNull(deadline, "deadline");
Preconditions.checkNotNull(scheduler, "scheduler"); checkNotNull(scheduler, "scheduler");
return new CancellableContext(this, deadline, scheduler); return new CancellableContext(this, deadline, scheduler);
} }
@ -349,7 +344,7 @@ public class Context {
* will still be bound. * will still be bound.
*/ */
public void detach(Context toAttach) { public void detach(Context toAttach) {
Preconditions.checkNotNull(toAttach, "toAttach"); checkNotNull(toAttach, "toAttach");
if (toAttach.attach() != this) { if (toAttach.attach() != this) {
// Log a severe message instead of throwing an exception as the context to attach is assumed // 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 // to be the correct one and the unbalanced state represents a coding mistake in a lower
@ -383,7 +378,6 @@ public class Context {
* <p>The cancellation cause is provided for informational purposes only and implementations * <p>The cancellation cause is provided for informational purposes only and implementations
* should generally assume that it has already been handled and logged properly. * should generally assume that it has already been handled and logged properly.
*/ */
@Nullable
public Throwable cancellationCause() { public Throwable cancellationCause() {
if (parent == null || !cascadesCancellation) { if (parent == null || !cascadesCancellation) {
return null; return null;
@ -396,7 +390,6 @@ public class Context {
* A context may have an associated {@link Deadline} at which it will be automatically cancelled. * 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. * @return A {@link io.grpc.Deadline} or {@code null} if no deadline is set.
*/ */
@Nullable
public Deadline getDeadline() { public Deadline getDeadline() {
return DEADLINE_KEY.get(this); return DEADLINE_KEY.get(this);
} }
@ -406,8 +399,8 @@ public class Context {
*/ */
public void addListener(final CancellationListener cancellationListener, public void addListener(final CancellationListener cancellationListener,
final Executor executor) { final Executor executor) {
Preconditions.checkNotNull(cancellationListener, "cancellationListener"); checkNotNull(cancellationListener, "cancellationListener");
Preconditions.checkNotNull(executor, "executor"); checkNotNull(executor, "executor");
if (canBeCancelled) { if (canBeCancelled) {
ExecutableListener executableListener = ExecutableListener executableListener =
new ExecutableListener(executor, cancellationListener); new ExecutableListener(executor, cancellationListener);
@ -420,7 +413,7 @@ public class Context {
// we can cascade listener notification. // we can cascade listener notification.
listeners = new ArrayList<ExecutableListener>(); listeners = new ArrayList<ExecutableListener>();
listeners.add(executableListener); listeners.add(executableListener);
parent.addListener(parentListener, MoreExecutors.directExecutor()); parent.addListener(parentListener, DirectExecutor.INSTANCE);
} else { } else {
listeners.add(executableListener); listeners.add(executableListener);
} }
@ -689,13 +682,13 @@ public class Context {
} }
/** /**
* Cancel this context and optionally provide a cause for the cancellation. This * Cancel this context and optionally provide a cause (can be {@code null}) for the
* will trigger notification of listeners. * cancellation. This will trigger notification of listeners.
* *
* @return {@code true} if this context cancelled the context and notified listeners, * @return {@code true} if this context cancelled the context and notified listeners,
* {@code false} if the context was already cancelled. * {@code false} if the context was already cancelled.
*/ */
public boolean cancel(@Nullable Throwable cause) { public boolean cancel(Throwable cause) {
boolean triggeredCancel = false; boolean triggeredCancel = false;
synchronized (this) { synchronized (this) {
if (!cancelled) { if (!cancelled) {
@ -721,7 +714,7 @@ public class Context {
* @param toAttach context to make current. * @param toAttach context to make current.
* @param cause of cancellation, can be {@code null}. * @param cause of cancellation, can be {@code null}.
*/ */
public void detachAndCancel(Context toAttach, @Nullable Throwable cause) { public void detachAndCancel(Context toAttach, Throwable cause) {
try { try {
detach(toAttach); detach(toAttach);
} finally { } finally {
@ -745,7 +738,6 @@ public class Context {
return false; return false;
} }
@Nullable
@Override @Override
public Throwable cancellationCause() { public Throwable cancellationCause() {
if (isCancelled()) { if (isCancelled()) {
@ -778,7 +770,7 @@ public class Context {
} }
Key(String name, T defaultValue) { Key(String name, T defaultValue) {
this.name = Preconditions.checkNotNull(name, "name"); this.name = checkNotNull(name, "name");
this.defaultValue = defaultValue; this.defaultValue = defaultValue;
} }
@ -842,4 +834,25 @@ public class Context {
} }
} }
} }
private static <T> 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";
}
}
} }

View File

@ -31,9 +31,6 @@
package io.grpc; package io.grpc;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -58,9 +55,9 @@ public final class Deadline implements Comparable<Deadline> {
return after(duration, units, SYSTEM_TICKER); return after(duration, units, SYSTEM_TICKER);
} }
@VisibleForTesting // For testing
static Deadline after(long duration, TimeUnit units, Ticker ticker) { static Deadline after(long duration, TimeUnit units, Ticker ticker) {
Preconditions.checkNotNull(units, "units"); checkNotNull(units, "units");
return new Deadline(ticker, units.toNanos(duration), true); return new Deadline(ticker, units.toNanos(duration), true);
} }
@ -146,8 +143,8 @@ public final class Deadline implements Comparable<Deadline> {
* @return {@link ScheduledFuture} which can be used to cancel execution of the task * @return {@link ScheduledFuture} which can be used to cancel execution of the task
*/ */
public ScheduledFuture<?> runOnExpiration(Runnable task, ScheduledExecutorService scheduler) { public ScheduledFuture<?> runOnExpiration(Runnable task, ScheduledExecutorService scheduler) {
Preconditions.checkNotNull(task, "task"); checkNotNull(task, "task");
Preconditions.checkNotNull(scheduler, "scheduler"); checkNotNull(scheduler, "scheduler");
return scheduler.schedule(task, deadlineNanos - ticker.read(), TimeUnit.NANOSECONDS); return scheduler.schedule(task, deadlineNanos - ticker.read(), TimeUnit.NANOSECONDS);
} }
@ -179,4 +176,11 @@ public final class Deadline implements Comparable<Deadline> {
return System.nanoTime(); return System.nanoTime();
} }
} }
private static <T> T checkNotNull(T reference, Object errorMessage) {
if (reference == null) {
throw new NullPointerException(String.valueOf(errorMessage));
}
return reference;
}
} }

View File

@ -265,7 +265,7 @@ public class DeadlineTest {
assertEquals("12000 ns from now", d.toString()); assertEquals("12000 ns from now", d.toString());
} }
static class FakeTicker extends Deadline.Ticker { private static class FakeTicker extends Deadline.Ticker {
private long time; private long time;
@Override @Override

View File

@ -7,7 +7,8 @@ description = 'gRPC: Core'
dependencies { dependencies {
compile libraries.guava, compile libraries.guava,
libraries.errorprone, libraries.errorprone,
libraries.jsr305 libraries.jsr305,
project(':grpc-context')
testCompile project(':grpc-testing') testCompile project(':grpc-testing')
} }

View File

@ -51,13 +51,14 @@ import org.junit.runner.RunWith;
import org.junit.runners.JUnit4; import org.junit.runners.JUnit4;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
/** Unit tests for {@link CallOptions}. */ /** Unit tests for {@link CallOptions}. */
@RunWith(JUnit4.class) @RunWith(JUnit4.class)
public class CallOptionsTest { public class CallOptionsTest {
private String sampleAuthority = "authority"; private String sampleAuthority = "authority";
private String sampleCompressor = "compressor"; 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 Deadline sampleDeadline = Deadline.after(1, NANOSECONDS, ticker);
private Key<String> sampleKey = Attributes.Key.of("sample"); private Key<String> sampleKey = Attributes.Key.of("sample");
private Attributes sampleAffinity = Attributes.newBuilder().set(sampleKey, "blah").build(); private Attributes sampleAffinity = Attributes.newBuilder().set(sampleKey, "blah").build();
@ -233,4 +234,24 @@ public class CallOptionsTest {
&& Objects.equal(o1.getAffinity(), o2.getAffinity()) && Objects.equal(o1.getAffinity(), o2.getAffinity())
&& Objects.equal(o1.getCredentials(), o2.getCredentials()); && 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);
}
}
} }

View File

@ -1,5 +1,6 @@
rootProject.name = "grpc" rootProject.name = "grpc"
include ":grpc-core" include ":grpc-core"
include ":grpc-context"
include ":grpc-stub" include ":grpc-stub"
include ":grpc-auth" include ":grpc-auth"
include ":grpc-okhttp" include ":grpc-okhttp"
@ -16,6 +17,7 @@ include ":grpc-services"
include ":grpc-thrift" include ":grpc-thrift"
project(':grpc-core').projectDir = "$rootDir/core" as File 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-stub').projectDir = "$rootDir/stub" as File
project(':grpc-auth').projectDir = "$rootDir/auth" as File project(':grpc-auth').projectDir = "$rootDir/auth" as File
project(':grpc-okhttp').projectDir = "$rootDir/okhttp" as File project(':grpc-okhttp').projectDir = "$rootDir/okhttp" as File