Skip to content

Commit

Permalink
Updates to jersey2 compatibility:
Browse files Browse the repository at this point in the history
- add missing client side filters
- clean up interfaces to be standardising on javax.wx.rs
- added README
  • Loading branch information
qiangdavidliu committed Aug 24, 2016
1 parent f2671f0 commit 944378a
Show file tree
Hide file tree
Showing 24 changed files with 326 additions and 429 deletions.
3 changes: 1 addition & 2 deletions eureka-client-jersey2/README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
This is work in progress. DiscoveryClient has hard dependency on Jersey 1.x, and it must be removed first
before Jersey 2.x transport can be injected.
Please note that this jersey2 compatible Eureka client (eureka-client-jersey2) is created and maintained by the community. Netflix does not currently use this library internally.
9 changes: 8 additions & 1 deletion eureka-client-jersey2/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
apply plugin: 'nebula.test-jar'

configurations.all {
// jersey1
exclude group: 'com.sun.jersey'
exclude group: 'com.sun.jersey.contribs'
exclude module: 'jsr311-api'
Expand All @@ -28,7 +29,13 @@ dependencies {
compile 'org.glassfish.jersey.connectors:jersey-apache-connector:2.23.1'
compile "javax.mail:mail:1.4.7"

testCompile project(':eureka-test-utils')
testCompile (project(':eureka-test-utils')) {
// exclude all transitives to avoid bringing in jersey1 eureka-core
transitive = false
}
// bring in jersey2 compatible eureka-core-jersey2 directly
testCompile project(':eureka-core-jersey2')

testCompile "junit:junit:${junit_version}"
testCompile "org.mockito:mockito-core:${mockitoVersion}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.netflix.discovery.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Scopes;
import com.netflix.appinfo.ApplicationInfoManager;
import com.netflix.appinfo.EurekaInstanceConfig;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.appinfo.providers.CloudInstanceConfigProvider;
import com.netflix.appinfo.providers.EurekaConfigBasedInstanceInfoProvider;
import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs;
import com.netflix.discovery.DiscoveryClient;
import com.netflix.discovery.EurekaClient;
import com.netflix.discovery.EurekaClientConfig;
import com.netflix.discovery.Jersey2DiscoveryClientOptionalArgs;
import com.netflix.discovery.providers.DefaultEurekaClientConfigProvider;
import com.netflix.discovery.shared.transport.jersey.TransportClientFactories;
import com.netflix.discovery.shared.transport.jersey2.Jersey2TransportClientFactories;

/**
* @author David Liu
*/
public final class Jersey2EurekaModule extends AbstractModule {
@Override
protected void configure() {
// need to eagerly initialize
bind(ApplicationInfoManager.class).asEagerSingleton();

//
// override these in additional modules if necessary with Modules.override()
//

bind(EurekaInstanceConfig.class).toProvider(CloudInstanceConfigProvider.class).in(Scopes.SINGLETON);
bind(EurekaClientConfig.class).toProvider(DefaultEurekaClientConfigProvider.class).in(Scopes.SINGLETON);

// this is the self instanceInfo used for registration purposes
bind(InstanceInfo.class).toProvider(EurekaConfigBasedInstanceInfoProvider.class).in(Scopes.SINGLETON);

bind(EurekaClient.class).to(DiscoveryClient.class).in(Scopes.SINGLETON);

// jersey2 support bindings
bind(AbstractDiscoveryClientOptionalArgs.class).to(Jersey2DiscoveryClientOptionalArgs.class).in(Scopes.SINGLETON);
}

@Provides
public TransportClientFactories getTransportClientFactories() {
return Jersey2TransportClientFactories.getInstance();
}

@Override
public boolean equals(Object obj) {
return obj != null && getClass().equals(obj.getClass());
}

@Override
public int hashCode() {
return getClass().hashCode();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@

package com.netflix.discovery.shared.transport.jersey2;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation.Builder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;


import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
Expand All @@ -31,13 +33,8 @@
import java.util.Map;
import java.util.Map.Entry;

import javax.ws.rs.client.Invocation.Builder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.HttpHeaders;

import com.netflix.appinfo.InstanceInfo;
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
import com.netflix.discovery.DiscoveryClient;
import com.netflix.discovery.shared.Application;
import com.netflix.discovery.shared.Applications;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
Expand All @@ -57,17 +54,15 @@ public abstract class AbstractJersey2EurekaHttpClient implements EurekaHttpClien

private static final Logger logger = LoggerFactory.getLogger(AbstractJersey2EurekaHttpClient.class);

protected final EurekaJersey2Client jerseyClient;
protected final Client jerseyClient;
protected final String serviceUrl;
private final boolean allowRedirect;
private final String userName;
private final String password;

protected AbstractJersey2EurekaHttpClient(EurekaJersey2Client jerseyClient, String serviceUrl, boolean allowRedirect) {
public AbstractJersey2EurekaHttpClient(Client jerseyClient, String serviceUrl) {
this.jerseyClient = jerseyClient;
this.serviceUrl = serviceUrl;
this.allowRedirect = allowRedirect;


// Jersey2 does not read credentials from the URI. We extract it here and enable authentication feature.
String localUserName = null;
String localPassword = null;
Expand All @@ -91,18 +86,17 @@ public EurekaHttpResponse<Void> register(InstanceInfo info) {
String urlPath = "apps/" + info.getAppName();
Response response = null;
try {
Builder resourceBuilder = jerseyClient.getClient().target(serviceUrl).path(urlPath).request();
Builder resourceBuilder = jerseyClient.target(serviceUrl).path(urlPath).request();
addExtraProperties(resourceBuilder);
addExtraHeaders(resourceBuilder);
response = resourceBuilder
.header(HttpHeaders.ACCEPT_ENCODING, "gzip")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_TYPE)
.accept(MediaType.APPLICATION_JSON)
.post(Entity.json(info));
.acceptEncoding("gzip")
.post(Entity.entity(info, MediaType.APPLICATION_JSON_TYPE));
return anEurekaHttpResponse(response.getStatus()).headers(headersOf(response)).build();
} finally {
if (logger.isDebugEnabled()) {
logger.debug("Jersey HTTP POST {}/{} with instance {}; statusCode={}", serviceUrl, urlPath, info.getId(),
logger.debug("Jersey2 HTTP POST {}/{} with instance {}; statusCode={}", serviceUrl, urlPath, info.getId(),
response == null ? "N/A" : response.getStatus());
}
if (response != null) {
Expand All @@ -116,14 +110,14 @@ public EurekaHttpResponse<Void> cancel(String appName, String id) {
String urlPath = "apps/" + appName + '/' + id;
Response response = null;
try {
Builder resourceBuilder = jerseyClient.getClient().target(serviceUrl).path(urlPath).request();
Builder resourceBuilder = jerseyClient.target(serviceUrl).path(urlPath).request();
addExtraProperties(resourceBuilder);
addExtraHeaders(resourceBuilder);
response = resourceBuilder.delete();
return anEurekaHttpResponse(response.getStatus()).headers(headersOf(response)).build();
} finally {
if (logger.isDebugEnabled()) {
logger.debug("Jersey HTTP DELETE {}/{}; statusCode={}", serviceUrl, urlPath, response == null ? "N/A" : response.getStatus());
logger.debug("Jersey2 HTTP DELETE {}/{}; statusCode={}", serviceUrl, urlPath, response == null ? "N/A" : response.getStatus());
}
if (response != null) {
response.close();
Expand All @@ -136,7 +130,7 @@ public EurekaHttpResponse<InstanceInfo> sendHeartBeat(String appName, String id,
String urlPath = "apps/" + appName + '/' + id;
Response response = null;
try {
WebTarget webResource = jerseyClient.getClient().target(serviceUrl)
WebTarget webResource = jerseyClient.target(serviceUrl)
.path(urlPath)
.queryParam("status", info.getStatus().toString())
.queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString());
Expand All @@ -147,14 +141,15 @@ public EurekaHttpResponse<InstanceInfo> sendHeartBeat(String appName, String id,
addExtraProperties(requestBuilder);
addExtraHeaders(requestBuilder);
response = requestBuilder.put(Entity.entity("{}", MediaType.APPLICATION_JSON_TYPE)); // Jersey2 refuses to handle PUT with no body
requestBuilder.accept(MediaType.APPLICATION_JSON_TYPE);
EurekaHttpResponseBuilder<InstanceInfo> eurekaResponseBuilder = anEurekaHttpResponse(response.getStatus(), InstanceInfo.class).headers(headersOf(response));
if (response.hasEntity()) {
eurekaResponseBuilder.entity(response.readEntity(InstanceInfo.class));
}
return eurekaResponseBuilder.build();
} finally {
if (logger.isDebugEnabled()) {
logger.debug("Jersey HTTP PUT {}/{}; statusCode={}", serviceUrl, urlPath, response == null ? "N/A" : response.getStatus());
logger.debug("Jersey2 HTTP PUT {}/{}; statusCode={}", serviceUrl, urlPath, response == null ? "N/A" : response.getStatus());
}
if (response != null) {
response.close();
Expand All @@ -167,18 +162,18 @@ public EurekaHttpResponse<Void> statusUpdate(String appName, String id, Instance
String urlPath = "apps/" + appName + '/' + id + "/status";
Response response = null;
try {
Builder requestBuilder = jerseyClient.getClient().target(serviceUrl)
Builder requestBuilder = jerseyClient.target(serviceUrl)
.path(urlPath)
.queryParam("value", newStatus.name())
.queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString())
.request();
addExtraProperties(requestBuilder);
addExtraHeaders(requestBuilder);
response = requestBuilder.put(Entity.text(""));
response = requestBuilder.put(Entity.entity("{}", MediaType.APPLICATION_JSON_TYPE)); // Jersey2 refuses to handle PUT with no body
return anEurekaHttpResponse(response.getStatus()).headers(headersOf(response)).build();
} finally {
if (logger.isDebugEnabled()) {
logger.debug("Jersey HTTP PUT {}/{}; statusCode={}", serviceUrl, urlPath, response == null ? "N/A" : response.getStatus());
logger.debug("Jersey2 HTTP PUT {}/{}; statusCode={}", serviceUrl, urlPath, response == null ? "N/A" : response.getStatus());
}
if (response != null) {
response.close();
Expand All @@ -191,7 +186,7 @@ public EurekaHttpResponse<Void> deleteStatusOverride(String appName, String id,
String urlPath = "apps/" + appName + '/' + id + "/status";
Response response = null;
try {
Builder requestBuilder = jerseyClient.getClient().target(serviceUrl)
Builder requestBuilder = jerseyClient.target(serviceUrl)
.path(urlPath)
.queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString())
.request();
Expand All @@ -201,7 +196,7 @@ public EurekaHttpResponse<Void> deleteStatusOverride(String appName, String id,
return anEurekaHttpResponse(response.getStatus()).headers(headersOf(response)).build();
} finally {
if (logger.isDebugEnabled()) {
logger.debug("Jersey HTTP DELETE {}/{}; statusCode={}", serviceUrl, urlPath, response == null ? "N/A" : response.getStatus());
logger.debug("Jersey2 HTTP DELETE {}/{}; statusCode={}", serviceUrl, urlPath, response == null ? "N/A" : response.getStatus());
}
if (response != null) {
response.close();
Expand All @@ -228,13 +223,13 @@ public EurekaHttpResponse<Applications> getVip(String vipAddress, String... regi
public EurekaHttpResponse<Applications> getSecureVip(String secureVipAddress, String... regions) {
return getApplicationsInternal("svips/" + secureVipAddress, regions);
}

@Override
public EurekaHttpResponse<Application> getApplication(String appName) {
String urlPath = "apps/" + appName;
Response response = null;
try {
Builder requestBuilder = jerseyClient.getClient().target(serviceUrl).path(urlPath).request();
Builder requestBuilder = jerseyClient.target(serviceUrl).path(urlPath).request();
addExtraProperties(requestBuilder);
addExtraHeaders(requestBuilder);
response = requestBuilder.accept(MediaType.APPLICATION_JSON_TYPE).get();
Expand All @@ -256,9 +251,8 @@ public EurekaHttpResponse<Application> getApplication(String appName) {

private EurekaHttpResponse<Applications> getApplicationsInternal(String urlPath, String[] regions) {
Response response = null;
String regionsParamValue = null;
try {
WebTarget webTarget = jerseyClient.getClient().target(serviceUrl).path(urlPath);
WebTarget webTarget = jerseyClient.target(serviceUrl).path(urlPath);
if (regions != null && regions.length > 0) {
webTarget = webTarget.queryParam("regions", StringUtil.join(regions));
}
Expand Down Expand Up @@ -295,7 +289,7 @@ public EurekaHttpResponse<InstanceInfo> getInstance(String appName, String id) {
private EurekaHttpResponse<InstanceInfo> getInstanceInternal(String urlPath) {
Response response = null;
try {
Builder requestBuilder = jerseyClient.getClient().target(serviceUrl).path(urlPath).request();
Builder requestBuilder = jerseyClient.target(serviceUrl).path(urlPath).request();
addExtraProperties(requestBuilder);
addExtraHeaders(requestBuilder);
response = requestBuilder.accept(MediaType.APPLICATION_JSON_TYPE).get();
Expand All @@ -317,7 +311,6 @@ private EurekaHttpResponse<InstanceInfo> getInstanceInternal(String urlPath) {

@Override
public void shutdown() {
jerseyClient.destroyResources();
}

protected void addExtraProperties(Builder webResource) {
Expand All @@ -327,11 +320,7 @@ protected void addExtraProperties(Builder webResource) {
}
}

protected void addExtraHeaders(Builder webResource) {
if (allowRedirect) {
webResource.header(DiscoveryClient.HTTP_X_DISCOVERY_ALLOW_REDIRECT, "true");
}
}
protected abstract void addExtraHeaders(Builder webResource);

private static Map<String, String> headersOf(Response response) {
MultivaluedMap<String, String> jerseyHeaders = response.getStringHeaders();
Expand Down
Loading

0 comments on commit 944378a

Please sign in to comment.