Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a way to create a derived client #97

Merged
merged 1 commit into from
Jan 28, 2016

Conversation

trustin
Copy link
Member

@trustin trustin commented Jan 27, 2016

Related: #95

Motivation:

Given that the creation of a new client is relatively an expensive
operation, a user might want to create a client that reuses the
RemoteInvoker and ClientCodec of an existing client while using an
alternative ClientOptions.

A good example is writing an API proxy that forwards a certain set of
HTTP headers such as access tokens, as explained in #95.

Modifications:

  • Add Clients.newDerivedClient() that creates a derived client from an
    existing client created by ClientBuilder or Clients.newClient()
  • Add a test case for Clients.newDerivedClient() to
    ThriftOverHttpClientTest
    • Add HeaderService to assert that the derived client sends an
      expected header
  • Simplify the method comparison of ClientInvocationHandler. It doesn't
    really need to use Method.equals() that performs rigorous comparison
    because Object has no overloaded methods.

Result:

A user can create a derived client at much lower cost, which makes it
easy to implement an API proxy-ish server.

@trustin trustin added this to the 0.9.0.Final milestone Jan 27, 2016
@trustin
Copy link
Member Author

trustin commented Jan 27, 2016

@kumai @inch772 PTAL. testDerivedClient() demonstrates this new feature. Please feel free to let me know if there's even better way to achieve this. :-)

@inch772 We might want to reduce the construction cost of AbstractOptions later. We currently build an IdentityHashMap from scratch every time.

@trustin
Copy link
Member Author

trustin commented Jan 27, 2016

@inch772 I also find that we cannot use ClientOptions as a parameter of newDerivedClient() because a ClientOptions always contains every possible option. Perhaps we should relax this behavior somehow? Let me create a PR for this once this is merged.

@kumai
Copy link
Contributor

kumai commented Jan 28, 2016

@trustin LGTM. Thanks! Although it's a tiny thing, I think the javadoc of ProxyClient should be fixed:

--- a/src/main/java/com/linecorp/armeria/client/ProxiedClient.java
+++ b/src/main/java/com/linecorp/armeria/client/ProxiedClient.java
@@ -19,7 +19,7 @@ package com.linecorp.armeria.client;
 /**
  * An interface that is implemented by all clients created by {@link ClientBuilder} and {@link Clients} to
  * provide the properties required to create a derived client via
- * {@link Clients#newClient(Object, ClientOptionValue[])}.
+ * {@link Clients#newDerivedClient(Object, ClientOptionValue[])}.
  */
 @FunctionalInterface
 @SuppressWarnings("DollarSignInName")

throw new IllegalArgumentException("not a client: " + client);
}

final ClientInvocationHandler parent =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can avoid the weird name mangling with something like

ProxiedClient.getClass().getMethod("handler").invoke(client);

The method.getDeclaredClass() == ProxiedClient.class check in the handler will be true for this call and should be false for any other handler() method.

Since this is a reflective Java proxy, I don't think the performance will be any different for the .invoke() call vs this invocation.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I found

(ClientInvocationHandler) Proxy.getInvocationHandler(proxy)

From what I can tell, it'll work and remove ProxiedClass completely.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@anuraaga Ah, nice find. will fix.

@trustin trustin force-pushed the per_request_options branch 3 times, most recently from 6847aa9 to a3d29d7 Compare January 28, 2016 07:53
@trustin
Copy link
Member Author

trustin commented Jan 28, 2016

@kumai @anuraaga Thanks! I have updated the PR to address the comments. Please feel free to merge.

Related: line#95

Motivation:

Given that the creation of a new client is relatively an expensive
operation, a user might want to create a client that reuses the
RemoteInvoker and ClientCodec of an existing client while using an
alternative ClientOptions.

A good example is writing an API proxy that forwards a certain set of
HTTP headers such as access tokens, as explained in line#95.

Modifications:

- Add Clients.newDerivedClient() that creates a derived client from an
  existing client created by ClientBuilder or Clients.newClient()
- Add a test case for Clients.newDerivedClient() to
  ThriftOverHttpClientTest
  - Add HeaderService to assert that the derived client sends an
    expected header
- Simplify the method comparison of ClientInvocationHandler. It doesn't
  really need to use Method.equals() that performs rigorous comparison
  because Object has no overloaded methods.

Result:

A user can create a derived client at much lower cost, which makes it
easy to implement an API proxy-ish server.
@anuraaga
Copy link
Collaborator

LGTM - so glad that weird method mangling is gone!

kumai added a commit that referenced this pull request Jan 28, 2016
Add a way to create a derived client
@kumai kumai merged commit fc55196 into line:master Jan 28, 2016
@trustin trustin deleted the per_request_options branch January 28, 2016 09:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants