Skip to content
This repository has been archived by the owner on Sep 28, 2021. It is now read-only.

Commit

Permalink
Merge pull request #33 from rouzwawi/master
Browse files Browse the repository at this point in the history
Document ApolloEnvironmentModule decoration api
  • Loading branch information
rouzwawi committed Nov 18, 2015
2 parents b2da15b + 4762998 commit e5e5be8
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.google.common.base.Strings;

import com.spotify.apollo.RequestContext;
import com.spotify.apollo.request.EndpointRunnableFactory;
import com.spotify.apollo.request.OngoingRequest;

import org.slf4j.Logger;
Expand All @@ -33,15 +34,24 @@
import static com.spotify.apollo.Response.forStatus;
import static com.spotify.apollo.Status.INTERNAL_SERVER_ERROR;

public class EndpointInvocationHandler {
public class EndpointInvocationHandler implements EndpointRunnableFactory {

private static final Logger LOG = LoggerFactory.getLogger(EndpointInvocationHandler.class);

@Override
public Runnable create(
OngoingRequest ongoingRequest,
RequestContext requestContext,
Endpoint endpoint) {

return () -> handle(ongoingRequest, requestContext, endpoint);
}

/**
* Fires off the request processing asynchronously - that is, this method is likely to return
* before the request processing finishes.
*/
public void handle(OngoingRequest ongoingRequest, RequestContext requestContext, Endpoint endpoint) {
void handle(OngoingRequest ongoingRequest, RequestContext requestContext, Endpoint endpoint) {
try {
endpoint.invoke(requestContext)
.whenComplete((message, throwable) -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
*/
package com.spotify.apollo.request;

import com.spotify.apollo.environment.IncomingRequestAwareClient;
import com.spotify.apollo.dispatch.Endpoint;
import com.spotify.apollo.dispatch.EndpointInvocationHandler;
import com.spotify.apollo.environment.IncomingRequestAwareClient;
import com.spotify.apollo.meta.IncomingCallsGatherer;
import com.spotify.apollo.route.ApplicationRouter;

Expand All @@ -30,9 +30,6 @@
*/
public final class Handlers {

private static final EndpointInvocationHandler ENDPOINT_INVOCATION_HANDLER =
new EndpointInvocationHandler();

private Handlers() {
}

Expand All @@ -54,8 +51,7 @@ public static RequestRunnableFactory requestRunnableFactory(
}

public static EndpointRunnableFactory endpointRunnableFactory() {
return (request, requestContext, endpoint) ->
() -> ENDPOINT_INVOCATION_HANDLER.handle(request, requestContext, endpoint);
return new EndpointInvocationHandler();
}

public static EndpointRunnableFactory withTracking(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@
import com.spotify.apollo.Response;
import com.spotify.apollo.request.OngoingRequest;

import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand Down Expand Up @@ -55,7 +52,6 @@ public class EndpointInvocationHandlerTest {
EndpointInvocationHandler handler;

@Mock private OngoingRequest ongoingRequest;
private Request requestMessage;

@Mock private RequestContext requestContext;
@Mock private Endpoint endpoint;
Expand All @@ -69,10 +65,9 @@ public class EndpointInvocationHandlerTest {
public void setUp() throws Exception {
handler = new EndpointInvocationHandler();

requestMessage = Request.forUri("http://foo/bar").withService("nameless-registry");
Request requestMessage = Request.forUri("http://foo/bar").withService("nameless-registry");

when(ongoingRequest.request()).thenReturn(requestMessage);

when(requestContext.request()).thenReturn(requestMessage);
future = new CompletableFuture<>();
}
Expand Down Expand Up @@ -171,18 +166,4 @@ public void shouldRespondWithDetailMessageForSyncExceptionsToNonClientCallers()
assertThat(messageArgumentCaptor.getValue().status().reasonPhrase(),
containsString(exception.getMessage()));
}

private Matcher<String> nullOrEmpty() {
return new BaseMatcher<String>() {
@Override
public boolean matches(Object item) {
return item == null || (item instanceof String) && ((String) item).isEmpty();
}

@Override
public void describeTo(Description description) {
description.appendText("a null or empty string");
}
};
}
}
60 changes: 60 additions & 0 deletions apollo-environment/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,63 @@ public static void main(String[] args) {

For a runnable example, see [`MinimalRunner`]
(../apollo-http-service/src/test/java/com/spotify/apollo/httpservice/MinimalRunner.java)


## Extending incoming/outgoing request handling

`ApolloEnvironmentModule` has a few extension points that allow 3rd party modules to
decorate internal components involved in incoming/outgoing request handling.


### IncomingRequestAwareClient
One important aspect of the Apollo `Client` is that it does not come with any protocol
support out of the box. Instead, support for different protocols should be added by
modules. These modules do so by injecting themselves into the `IncomingRequestAwareClient`
decoration chain.

The decoration chain looks like:

1. `OutgoingCallsGatheringClient`
1. `ServiceSettingClient`
1. `[IncomingRequestAwareClient]* <- Set<ClientDecorator>`
1. `NoopClient`


### RequestRunnableFactory

1. `[RequestRunnableFactory]* <- Set<RequestRunnableFactoryDecorator>`
1. `RequestRunnableImpl`


### EndpointRunnableFactory

1. `TrackingEndpointRunnableFactory`
1. `[EndpointRunnableFactory]* <- Set<EndpointRunnableFactoryDecorator>`
1. `EndpointInvocationHandler`


### RequestHandler
This is what is ultimately created from the `ApolloEnvironmentModule`. It will use
the `RequestRunnableFactory`, `EndpointRunnableFactory` and `IncomingRequestAwareClient`
decoration chains that were constructed above. See `RequestHandlerImpl` for how they are
used, but in short terms it's something like:

```java
RequestHandler requestHandler = ongoingRequest ->
rrf.create(ongoingRequest).run((ongoingRequest, match) ->
erf.create(ongoingRequest, requestContext, endpoint).run());
```


### Injecting decorators
To contribute to any of the sets of decorators mentioned above, use Guice Multibinder.

Here's an example of how a `ClientDecorator` is injected:

```java
@Override
protected void configure() {
Multibinder.newSetBinder(binder(), ClientDecorator.class)
.addBinding().toProvider(HttpClientDecoratorProvider.class);
}
```

0 comments on commit e5e5be8

Please sign in to comment.