@Override public HttpJsonCallContext getEmptyCallContext() { return HttpJsonCallContext.createDefault(); }
@Override public HttpJsonCallContext withTransportChannel(TransportChannel inputChannel) { Preconditions.checkNotNull(inputChannel); if (!(inputChannel instanceof HttpJsonTransportChannel)) { throw new IllegalArgumentException( "Expected HttpJsonTransportChannel, got " + inputChannel.getClass().getName()); } HttpJsonTransportChannel transportChannel = (HttpJsonTransportChannel) inputChannel; return withChannel(transportChannel.getChannel()); }
@Override public HttpJsonCallContext withCredentials(Credentials newCredentials) { return new HttpJsonCallContext( this.channel, this.timeout, this.deadline, newCredentials, this.extraHeaders, this.tracer); }
@Override public ApiFuture<ResponseT> futureCall(RequestT request, ApiCallContext inputContext) { Preconditions.checkNotNull(request); HttpJsonCallContext context = HttpJsonCallContext.createDefault().nullToSelf(inputContext); @Nullable Instant deadline = context.getDeadline(); // Try to convert the timeout into a deadline and use it if it occurs before the actual deadline if (context.getTimeout() != null) { @Nonnull Instant newDeadline = Instant.now().plus(context.getTimeout()); if (deadline == null || newDeadline.isBefore(deadline)) { deadline = newDeadline; } } HttpJsonCallOptions callOptions = HttpJsonCallOptions.newBuilder() .setDeadline(deadline) .setCredentials(context.getCredentials()) .build(); return context.getChannel().issueFutureUnaryCall(callOptions, request, descriptor); }
@Test public void testWithTimeout() { Truth.assertThat(HttpJsonCallContext.createDefault().withTimeout(null).getTimeout()).isNull(); }
@Test public void testMergeWithNullTimeout() { Duration timeout = Duration.ofSeconds(10); HttpJsonCallContext baseContext = HttpJsonCallContext.createDefault().withTimeout(timeout); HttpJsonCallContext defaultOverlay = HttpJsonCallContext.createDefault(); Truth.assertThat(baseContext.merge(defaultOverlay).getTimeout()).isEqualTo(timeout); HttpJsonCallContext explicitNullOverlay = HttpJsonCallContext.createDefault().withTimeout(null); Truth.assertThat(baseContext.merge(explicitNullOverlay).getTimeout()).isEqualTo(timeout); }
@Test public void testTimeoutAfterDeadline() { HttpJsonChannel mockChannel = Mockito.mock(HttpJsonChannel.class); String expectedRequest = "fake"; HttpJsonDirectCallable<String, String> callable = new HttpJsonDirectCallable<>(API_DESCRIPTOR); // Mock the channel that captures the call options ArgumentCaptor<HttpJsonCallOptions> capturedCallOptions = ArgumentCaptor.forClass(HttpJsonCallOptions.class); Mockito.when( mockChannel.issueFutureUnaryCall( capturedCallOptions.capture(), Mockito.anyString(), Mockito.any(ApiMethodDescriptor.class))) .thenReturn(SettableApiFuture.create()); // Compose the call context Instant priorDeadline = Instant.now().plusSeconds(5); Duration timeout = Duration.ofSeconds(10); HttpJsonCallContext callContext = HttpJsonCallContext.createDefault() .withChannel(mockChannel) .withDeadline(priorDeadline) .withTimeout(timeout); callable.futureCall(expectedRequest, callContext); // Verify that the timeout was ignored assertThat(capturedCallOptions.getValue().getDeadline()).isEqualTo(priorDeadline); }
@Override public ApiFuture<ResponseT> futureCall(RequestT request, ApiCallContext inputContext) { HttpJsonCallContext context = HttpJsonCallContext.createDefault().nullToSelf(inputContext); ApiFuture<ResponseT> innerCallFuture = callable.futureCall(request, context); ExceptionTransformingFuture transformingFuture = new ExceptionTransformingFuture(innerCallFuture); ApiFutures.addCallback(innerCallFuture, transformingFuture, directExecutor()); return transformingFuture; }
@Test public void testWithCredentials() { Credentials credentials = Mockito.mock(Credentials.class); HttpJsonCallContext emptyContext = HttpJsonCallContext.createDefault(); Truth.assertThat(emptyContext.getCredentials()).isNull(); HttpJsonCallContext context = emptyContext.withCredentials(credentials); Truth.assertThat(context.getCredentials()).isNotNull(); }
@Test public void testWithTransportChannel() { ManagedHttpJsonChannel channel = Mockito.mock(ManagedHttpJsonChannel.class); HttpJsonCallContext context = HttpJsonCallContext.createDefault() .withTransportChannel( HttpJsonTransportChannel.newBuilder().setManagedChannel(channel).build()); Truth.assertThat(context.getChannel()).isSameAs(channel); }
@Test public void testMergeWithTracer() { ApiTracer explicitTracer = Mockito.mock(ApiTracer.class); HttpJsonCallContext ctxWithExplicitTracer = HttpJsonCallContext.createDefault().withTracer(explicitTracer); HttpJsonCallContext ctxWithDefaultTracer = HttpJsonCallContext.createDefault(); ApiTracer defaultTracer = ctxWithDefaultTracer.getTracer(); // Explicit tracer overrides the default tracer. Truth.assertThat(ctxWithDefaultTracer.merge(ctxWithExplicitTracer).getTracer()) .isSameAs(explicitTracer); // Default tracer does not override an explicit tracer. Truth.assertThat(ctxWithExplicitTracer.merge(ctxWithDefaultTracer).getTracer()) .isSameAs(explicitTracer); // Default tracer does not override another default tracer. Truth.assertThat(ctxWithDefaultTracer.merge(HttpJsonCallContext.createDefault()).getTracer()) .isSameAs(defaultTracer); } }
@Test public void testTimeout() { HttpJsonChannel mockChannel = Mockito.mock(HttpJsonChannel.class); String expectedRequest = "fake"; HttpJsonDirectCallable<String, String> callable = new HttpJsonDirectCallable<>(API_DESCRIPTOR); // Mock the channel that captures the call options ArgumentCaptor<HttpJsonCallOptions> capturedCallOptions = ArgumentCaptor.forClass(HttpJsonCallOptions.class); Mockito.when( mockChannel.issueFutureUnaryCall( capturedCallOptions.capture(), Mockito.anyString(), Mockito.any(ApiMethodDescriptor.class))) .thenReturn(SettableApiFuture.create()); // Compose the call context Duration timeout = Duration.ofSeconds(10); Instant minExpectedDeadline = Instant.now().plus(timeout); HttpJsonCallContext callContext = HttpJsonCallContext.createDefault().withChannel(mockChannel).withTimeout(timeout); callable.futureCall(expectedRequest, callContext); Instant maxExpectedDeadline = Instant.now().plus(timeout); // Verify that the timeout was converted into a deadline assertThat(capturedCallOptions.getValue().getDeadline()).isAtLeast(minExpectedDeadline); assertThat(capturedCallOptions.getValue().getDeadline()).isAtMost(maxExpectedDeadline); }
@Test public void testMergeWrongType() { thrown.expect(IllegalArgumentException.class); HttpJsonCallContext.createDefault().merge(FakeCallContext.createDefault()); }
@Test public void testWithTransportChannelWrongType() { thrown.expect(IllegalArgumentException.class); FakeChannel channel = new FakeChannel(); HttpJsonCallContext.createDefault().withTransportChannel(FakeTransportChannel.create(channel)); }
@Override public ApiFuture<ResponseT> futureCall(RequestT request, ApiCallContext inputContext) { Preconditions.checkNotNull(request); HttpJsonCallContext context = HttpJsonCallContext.createDefault().nullToSelf(inputContext); @Nullable Instant deadline = context.getDeadline(); // Try to convert the timeout into a deadline and use it if it occurs before the actual deadline if (context.getTimeout() != null) { @Nonnull Instant newDeadline = Instant.now().plus(context.getTimeout()); if (deadline == null || newDeadline.isBefore(deadline)) { deadline = newDeadline; } } HttpJsonCallOptions callOptions = HttpJsonCallOptions.newBuilder() .setDeadline(deadline) .setCredentials(context.getCredentials()) .build(); return context.getChannel().issueFutureUnaryCall(callOptions, request, descriptor); }
@Test public void testWithZeroTimeout() { Truth.assertThat( HttpJsonCallContext.createDefault().withTimeout(Duration.ofSeconds(0L)).getTimeout()) .isNull(); }
@Test public void testMergeWithTimeout() { Duration timeout = Duration.ofSeconds(19); HttpJsonCallContext ctx1 = HttpJsonCallContext.createDefault(); HttpJsonCallContext ctx2 = HttpJsonCallContext.createDefault().withTimeout(timeout); Truth.assertThat(ctx1.merge(ctx2).getTimeout()).isEqualTo(timeout); }
@Test public void testTimeoutBeforeDeadline() { HttpJsonChannel mockChannel = Mockito.mock(HttpJsonChannel.class); String expectedRequest = "fake"; HttpJsonDirectCallable<String, String> callable = new HttpJsonDirectCallable<>(API_DESCRIPTOR); // Mock the channel that captures the call options ArgumentCaptor<HttpJsonCallOptions> capturedCallOptions = ArgumentCaptor.forClass(HttpJsonCallOptions.class); Mockito.when( mockChannel.issueFutureUnaryCall( capturedCallOptions.capture(), Mockito.anyString(), Mockito.any(ApiMethodDescriptor.class))) .thenReturn(SettableApiFuture.create()); // Compose the call context Duration timeout = Duration.ofSeconds(10); Instant subsequentDeadline = Instant.now().plusSeconds(15); Instant minExpectedDeadline = Instant.now().plus(timeout); HttpJsonCallContext callContext = HttpJsonCallContext.createDefault() .withChannel(mockChannel) .withDeadline(subsequentDeadline) .withTimeout(timeout); callable.futureCall(expectedRequest, callContext); Instant maxExpectedDeadline = Instant.now().plus(timeout); // Verify that the timeout was converted into a deadline assertThat(capturedCallOptions.getValue().getDeadline()).isAtLeast(minExpectedDeadline); assertThat(capturedCallOptions.getValue().getDeadline()).isAtMost(maxExpectedDeadline); }
@Override public ApiFuture<ResponseT> futureCall(RequestT request, ApiCallContext inputContext) { HttpJsonCallContext context = HttpJsonCallContext.createDefault().nullToSelf(inputContext); ApiFuture<ResponseT> innerCallFuture = callable.futureCall(request, context); ExceptionTransformingFuture transformingFuture = new ExceptionTransformingFuture(innerCallFuture); ApiFutures.addCallback(innerCallFuture, transformingFuture, directExecutor()); return transformingFuture; }
@Test public void testWithNegativeTimeout() { Truth.assertThat( HttpJsonCallContext.createDefault().withTimeout(Duration.ofSeconds(-1L)).getTimeout()) .isNull(); }