diff --git a/eureka-client-jersey2/README.md b/eureka-client-jersey2/README.md index 0e1e4521e..de93fa745 100644 --- a/eureka-client-jersey2/README.md +++ b/eureka-client-jersey2/README.md @@ -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. \ No newline at end of file +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. \ No newline at end of file diff --git a/eureka-client-jersey2/build.gradle b/eureka-client-jersey2/build.gradle index 1fa6a46cd..c6bfc1aea 100644 --- a/eureka-client-jersey2/build.gradle +++ b/eureka-client-jersey2/build.gradle @@ -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' @@ -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}" } diff --git a/eureka-client-jersey2/src/main/java/com/netflix/discovery/guice/Jersey2EurekaModule.java b/eureka-client-jersey2/src/main/java/com/netflix/discovery/guice/Jersey2EurekaModule.java new file mode 100644 index 000000000..15776bc1c --- /dev/null +++ b/eureka-client-jersey2/src/main/java/com/netflix/discovery/guice/Jersey2EurekaModule.java @@ -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(); + } +} diff --git a/eureka-client-jersey2/src/main/java/com/netflix/discovery/shared/transport/jersey2/AbstractJersey2EurekaHttpClient.java b/eureka-client-jersey2/src/main/java/com/netflix/discovery/shared/transport/jersey2/AbstractJersey2EurekaHttpClient.java index 517911128..28f5b2b59 100644 --- a/eureka-client-jersey2/src/main/java/com/netflix/discovery/shared/transport/jersey2/AbstractJersey2EurekaHttpClient.java +++ b/eureka-client-jersey2/src/main/java/com/netflix/discovery/shared/transport/jersey2/AbstractJersey2EurekaHttpClient.java @@ -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; @@ -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; @@ -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; @@ -91,18 +86,17 @@ public EurekaHttpResponse 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) { @@ -116,14 +110,14 @@ public EurekaHttpResponse 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(); @@ -136,7 +130,7 @@ public EurekaHttpResponse 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()); @@ -147,6 +141,7 @@ public EurekaHttpResponse 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 eurekaResponseBuilder = anEurekaHttpResponse(response.getStatus(), InstanceInfo.class).headers(headersOf(response)); if (response.hasEntity()) { eurekaResponseBuilder.entity(response.readEntity(InstanceInfo.class)); @@ -154,7 +149,7 @@ public EurekaHttpResponse sendHeartBeat(String appName, String id, 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(); @@ -167,18 +162,18 @@ public EurekaHttpResponse 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(); @@ -191,7 +186,7 @@ public EurekaHttpResponse 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(); @@ -201,7 +196,7 @@ public EurekaHttpResponse 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(); @@ -228,13 +223,13 @@ public EurekaHttpResponse getVip(String vipAddress, String... regi public EurekaHttpResponse getSecureVip(String secureVipAddress, String... regions) { return getApplicationsInternal("svips/" + secureVipAddress, regions); } - + @Override public EurekaHttpResponse 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(); @@ -256,9 +251,8 @@ public EurekaHttpResponse getApplication(String appName) { private EurekaHttpResponse 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)); } @@ -295,7 +289,7 @@ public EurekaHttpResponse getInstance(String appName, String id) { private EurekaHttpResponse 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(); @@ -317,7 +311,6 @@ private EurekaHttpResponse getInstanceInternal(String urlPath) { @Override public void shutdown() { - jerseyClient.destroyResources(); } protected void addExtraProperties(Builder webResource) { @@ -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 headersOf(Response response) { MultivaluedMap jerseyHeaders = response.getStringHeaders(); diff --git a/eureka-client-jersey2/src/main/java/com/netflix/discovery/shared/transport/jersey2/Jersey2ApplicationClient.java b/eureka-client-jersey2/src/main/java/com/netflix/discovery/shared/transport/jersey2/Jersey2ApplicationClient.java index f431a53e4..0aa00a0a3 100644 --- a/eureka-client-jersey2/src/main/java/com/netflix/discovery/shared/transport/jersey2/Jersey2ApplicationClient.java +++ b/eureka-client-jersey2/src/main/java/com/netflix/discovery/shared/transport/jersey2/Jersey2ApplicationClient.java @@ -1,345 +1,29 @@ -/* - * Copyright 2015 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.netflix.discovery.shared.transport.jersey2; -import javax.ws.rs.client.Entity; -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; -import java.util.HashMap; -import java.util.List; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.Invocation.Builder; import java.util.Map; -import java.util.Map.Entry; - -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; -import com.netflix.discovery.shared.transport.EurekaHttpResponse; -import com.netflix.discovery.shared.transport.EurekaHttpResponse.EurekaHttpResponseBuilder; -import com.netflix.discovery.util.StringUtil; -import org.glassfish.jersey.client.JerseyClient; -import org.glassfish.jersey.client.JerseyInvocation.Builder; -import org.glassfish.jersey.client.JerseyWebTarget; -import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import static com.netflix.discovery.shared.transport.EurekaHttpResponse.anEurekaHttpResponse; /** - * @author Tomasz Bak + * A version of Jersey2 {@link com.netflix.discovery.shared.transport.EurekaHttpClient} to be used by applications. + * + * @author David Liu */ -public class Jersey2ApplicationClient implements EurekaHttpClient { - - private static final Logger logger = LoggerFactory.getLogger(Jersey2ApplicationClient.class); - - private final JerseyClient jerseyClient; - private final String serviceUrl; - private final boolean allowRedirect; - private final String userName; - private final String password; - - public Jersey2ApplicationClient(JerseyClient jerseyClient, String serviceUrl, boolean allowRedirect) { - 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; - try { - URI serviceURI = new URI(serviceUrl); - if (serviceURI.getUserInfo() != null) { - String[] credentials = serviceURI.getUserInfo().split(":"); - if (credentials.length == 2) { - localUserName = credentials[0]; - localPassword = credentials[1]; - } - } - } catch (URISyntaxException ignore) { - } - this.userName = localUserName; - this.password = localPassword; - } - - @Override - public EurekaHttpResponse register(InstanceInfo info) { - String urlPath = "apps/" + info.getAppName(); - Response response = null; - try { - Builder resourceBuilder = jerseyClient.target(serviceUrl).path(urlPath).request(); - addExtraProperties(resourceBuilder); - addExtraHeaders(resourceBuilder); - response = resourceBuilder - .accept(MediaType.APPLICATION_JSON) - .acceptEncoding("gzip") - .post(Entity.entity(info, MediaType.APPLICATION_JSON_TYPE)); - return anEurekaHttpResponse(response.getStatus()).headers(headersOf(response)).build(); - } finally { - if (logger.isDebugEnabled()) { - logger.debug("Jersey2 HTTP POST {}/{} with instance {}; statusCode={}", serviceUrl, urlPath, info.getId(), - response == null ? "N/A" : response.getStatus()); - } - if (response != null) { - response.close(); - } - } - } - - @Override - public EurekaHttpResponse cancel(String appName, String id) { - String urlPath = "apps/" + appName + '/' + id; - Response response = null; - try { - 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()); - } - if (response != null) { - response.close(); - } - } - } - - @Override - public EurekaHttpResponse sendHeartBeat(String appName, String id, InstanceInfo info, InstanceStatus overriddenStatus) { - String urlPath = "apps/" + appName + '/' + id; - Response response = null; - try { - JerseyWebTarget webResource = jerseyClient.target(serviceUrl) - .path(urlPath) - .queryParam("status", info.getStatus().toString()) - .queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString()); - if (overriddenStatus != null) { - webResource = webResource.queryParam("overriddenstatus", overriddenStatus.name()); - } - Builder requestBuilder = webResource.request(); - 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 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()); - } - if (response != null) { - response.close(); - } - } - } +public class Jersey2ApplicationClient extends AbstractJersey2EurekaHttpClient { - @Override - public EurekaHttpResponse statusUpdate(String appName, String id, InstanceStatus newStatus, InstanceInfo info) { - String urlPath = "apps/" + appName + '/' + id + "/status"; - Response response = null; - try { - 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.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()); - } - if (response != null) { - response.close(); - } - } - } - - @Override - public EurekaHttpResponse deleteStatusOverride(String appName, String id, InstanceInfo info) { - String urlPath = "apps/" + appName + '/' + id + "/status"; - Response response = null; - try { - Builder requestBuilder = jerseyClient.target(serviceUrl) - .path(urlPath) - .queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString()) - .request(); - addExtraProperties(requestBuilder); - addExtraHeaders(requestBuilder); - response = requestBuilder.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()); - } - if (response != null) { - response.close(); - } - } - } + private final Map additionalHeaders; - @Override - public EurekaHttpResponse getApplications(String... regions) { - return getApplicationsInternal("apps/", regions); + public Jersey2ApplicationClient(Client jerseyClient, String serviceUrl, Map additionalHeaders) { + super(jerseyClient, serviceUrl); + this.additionalHeaders = additionalHeaders; } @Override - public EurekaHttpResponse getDelta(String... regions) { - return getApplicationsInternal("apps/delta", regions); - } - - @Override - public EurekaHttpResponse getVip(String vipAddress, String... regions) { - return getApplicationsInternal("vips/" + vipAddress, regions); - } - - @Override - public EurekaHttpResponse getSecureVip(String secureVipAddress, String... regions) { - return getApplicationsInternal("svips/" + secureVipAddress, regions); - } - - @Override - public EurekaHttpResponse getApplication(String appName) { - String urlPath = "apps/" + appName; - Response response = null; - try { - Builder requestBuilder = jerseyClient.target(serviceUrl).path(urlPath).request(); - addExtraProperties(requestBuilder); - addExtraHeaders(requestBuilder); - response = requestBuilder.accept(MediaType.APPLICATION_JSON_TYPE).get(); - - Application application = null; - if (response.getStatus() == Status.OK.getStatusCode() && response.hasEntity()) { - application = response.readEntity(Application.class); - } - return anEurekaHttpResponse(response.getStatus(), application).headers(headersOf(response)).build(); - } finally { - if (logger.isDebugEnabled()) { - logger.debug("Jersey HTTP GET {}/{}; statusCode={}", serviceUrl, urlPath, response == null ? "N/A" : response.getStatus()); - } - if (response != null) { - response.close(); - } - } - } - - private EurekaHttpResponse getApplicationsInternal(String urlPath, String[] regions) { - Response response = null; - try { - JerseyWebTarget webTarget = jerseyClient.target(serviceUrl).path(urlPath); - if (regions != null && regions.length > 0) { - webTarget = webTarget.queryParam("regions", StringUtil.join(regions)); - } - Builder requestBuilder = webTarget.request(); - addExtraProperties(requestBuilder); - addExtraHeaders(requestBuilder); - response = requestBuilder.accept(MediaType.APPLICATION_JSON_TYPE).get(); - - Applications applications = null; - if (response.getStatus() == Status.OK.getStatusCode() && response.hasEntity()) { - applications = response.readEntity(Applications.class); - } - return anEurekaHttpResponse(response.getStatus(), applications).headers(headersOf(response)).build(); - } finally { - if (logger.isDebugEnabled()) { - logger.debug("Jersey HTTP GET {}/{}; statusCode={}", serviceUrl, urlPath, response == null ? "N/A" : response.getStatus()); - } - if (response != null) { - response.close(); - } - } - } - - @Override - public EurekaHttpResponse getInstance(String id) { - return getInstanceInternal("instances/" + id); - } - - @Override - public EurekaHttpResponse getInstance(String appName, String id) { - return getInstanceInternal("apps/" + appName + '/' + id); - } - - private EurekaHttpResponse getInstanceInternal(String urlPath) { - Response response = null; - try { - Builder requestBuilder = jerseyClient.target(serviceUrl).path(urlPath).request(); - addExtraProperties(requestBuilder); - addExtraHeaders(requestBuilder); - response = requestBuilder.accept(MediaType.APPLICATION_JSON_TYPE).get(); - - InstanceInfo infoFromPeer = null; - if (response.getStatus() == Status.OK.getStatusCode() && response.hasEntity()) { - infoFromPeer = response.readEntity(InstanceInfo.class); - } - return anEurekaHttpResponse(response.getStatus(), infoFromPeer).headers(headersOf(response)).build(); - } finally { - if (logger.isDebugEnabled()) { - logger.debug("Jersey HTTP GET {}/{}; statusCode={}", serviceUrl, urlPath, response == null ? "N/A" : response.getStatus()); - } - if (response != null) { - response.close(); - } - } - } - - @Override - public void shutdown() { - } - - protected void addExtraProperties(Builder webResource) { - if (userName != null) { - webResource.property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_USERNAME, userName) - .property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_PASSWORD, password); - } - } - protected void addExtraHeaders(Builder webResource) { - if (allowRedirect) { - webResource.header(DiscoveryClient.HTTP_X_DISCOVERY_ALLOW_REDIRECT, "true"); - } - } - - private static Map headersOf(Response response) { - MultivaluedMap jerseyHeaders = response.getStringHeaders(); - if (jerseyHeaders == null || jerseyHeaders.isEmpty()) { - return Collections.emptyMap(); - } - Map headers = new HashMap<>(); - for (Entry> entry : jerseyHeaders.entrySet()) { - if (!entry.getValue().isEmpty()) { - headers.put(entry.getKey(), entry.getValue().get(0)); + if (additionalHeaders != null) { + for (String key : additionalHeaders.keySet()) { + webResource.header(key, additionalHeaders.get(key)); } } - return headers; } } diff --git a/eureka-client-jersey2/src/main/java/com/netflix/discovery/shared/transport/jersey2/Jersey2ApplicationClientFactory.java b/eureka-client-jersey2/src/main/java/com/netflix/discovery/shared/transport/jersey2/Jersey2ApplicationClientFactory.java index 51c1f8b47..189046dfe 100644 --- a/eureka-client-jersey2/src/main/java/com/netflix/discovery/shared/transport/jersey2/Jersey2ApplicationClientFactory.java +++ b/eureka-client-jersey2/src/main/java/com/netflix/discovery/shared/transport/jersey2/Jersey2ApplicationClientFactory.java @@ -17,6 +17,7 @@ package com.netflix.discovery.shared.transport.jersey2; import javax.net.ssl.SSLContext; +import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.ClientRequestContext; import javax.ws.rs.client.ClientRequestFilter; @@ -27,9 +28,13 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import com.netflix.appinfo.AbstractEurekaIdentity; +import com.netflix.appinfo.EurekaAccept; +import com.netflix.appinfo.EurekaClientIdentity; import com.netflix.appinfo.InstanceInfo; import com.netflix.discovery.EurekaClientConfig; import com.netflix.discovery.provider.DiscoveryJerseyProvider; @@ -40,29 +45,29 @@ import org.glassfish.jersey.client.ClientConfig; import org.glassfish.jersey.client.ClientProperties; import org.glassfish.jersey.client.JerseyClient; +import org.glassfish.jersey.message.GZipEncoder; import static com.netflix.discovery.util.DiscoveryBuildInfo.buildVersion; -import com.netflix.discovery.shared.transport.jersey.JerseyEurekaHttpClientFactory; -import com.netflix.discovery.shared.transport.jersey.JerseyEurekaHttpClientFactory.JerseyEurekaHttpClientFactoryBuilder; /** * @author Tomasz Bak */ public class Jersey2ApplicationClientFactory implements TransportClientFactory { + public static final String HTTP_X_DISCOVERY_ALLOW_REDIRECT = "X-Discovery-AllowRedirect"; private static final String KEY_STORE_TYPE = "JKS"; - private final JerseyClient jersey2Client; - private final boolean allowRedirect; + private final Client jersey2Client; + private final Map additionalHeaders; - public Jersey2ApplicationClientFactory(JerseyClient jersey2Client, boolean allowRedirect) { + public Jersey2ApplicationClientFactory(Client jersey2Client, Map additionalHeaders) { this.jersey2Client = jersey2Client; - this.allowRedirect = allowRedirect; + this.additionalHeaders = additionalHeaders; } @Override public EurekaHttpClient newClient(EurekaEndpoint endpoint) { - return new Jersey2ApplicationClient(jersey2Client, endpoint.getServiceUrl(), allowRedirect); + return new Jersey2ApplicationClient(jersey2Client, endpoint.getServiceUrl(), additionalHeaders); } @Override @@ -146,9 +151,26 @@ public void filter(ClientRequestContext requestContext) { clientConfig.property(ClientProperties.CONNECT_TIMEOUT, connectionTimeout); clientBuilder.withConfig(clientConfig); + + // Add gzip content encoding support + clientBuilder.register(new GZipEncoder()); + + // always enable client identity headers + String ip = myInstanceInfo == null ? null : myInstanceInfo.getIPAddr(); + final AbstractEurekaIdentity identity = clientIdentity == null ? new EurekaClientIdentity(ip) : clientIdentity; + clientBuilder.register(new Jersey2EurekaIdentityHeaderFilter(identity)); + JerseyClient jersey2Client = (JerseyClient) clientBuilder.build(); - return new Jersey2ApplicationClientFactory(jersey2Client, this.allowRedirect); + Map additionalHeaders = new HashMap<>(); + if (allowRedirect) { + additionalHeaders.put(HTTP_X_DISCOVERY_ALLOW_REDIRECT, "true"); + } + if (EurekaAccept.compact == eurekaAccept) { + additionalHeaders.put(EurekaAccept.HTTP_X_EUREKA_ACCEPT, eurekaAccept.name()); + } + + return new Jersey2ApplicationClientFactory(jersey2Client, additionalHeaders); } private void addSSLConfiguration(ClientBuilder clientBuilder) { diff --git a/eureka-client-jersey2/src/main/java/com/netflix/discovery/shared/transport/jersey2/Jersey2EurekaIdentityHeaderFilter.java b/eureka-client-jersey2/src/main/java/com/netflix/discovery/shared/transport/jersey2/Jersey2EurekaIdentityHeaderFilter.java new file mode 100644 index 000000000..0c809dd1a --- /dev/null +++ b/eureka-client-jersey2/src/main/java/com/netflix/discovery/shared/transport/jersey2/Jersey2EurekaIdentityHeaderFilter.java @@ -0,0 +1,29 @@ +package com.netflix.discovery.shared.transport.jersey2; + +import com.netflix.appinfo.AbstractEurekaIdentity; + +import javax.ws.rs.client.ClientRequestContext; +import javax.ws.rs.client.ClientRequestFilter; +import java.io.IOException; + +public class Jersey2EurekaIdentityHeaderFilter implements ClientRequestFilter { + + private final AbstractEurekaIdentity authInfo; + + public Jersey2EurekaIdentityHeaderFilter(AbstractEurekaIdentity authInfo) { + this.authInfo = authInfo; + } + + @Override + public void filter(ClientRequestContext requestContext) throws IOException { + if (authInfo != null) { + + requestContext.getHeaders().putSingle(AbstractEurekaIdentity.AUTH_NAME_HEADER_KEY, authInfo.getName()); + requestContext.getHeaders().putSingle(AbstractEurekaIdentity.AUTH_VERSION_HEADER_KEY, authInfo.getVersion()); + + if (authInfo.getId() != null) { + requestContext.getHeaders().putSingle(AbstractEurekaIdentity.AUTH_ID_HEADER_KEY, authInfo.getId()); + } + } + } +} diff --git a/eureka-client-jersey2/src/test/java/com/netflix/discovery/guice/Jersey2EurekaModuleTest.java b/eureka-client-jersey2/src/test/java/com/netflix/discovery/guice/Jersey2EurekaModuleTest.java new file mode 100644 index 000000000..1474e2540 --- /dev/null +++ b/eureka-client-jersey2/src/test/java/com/netflix/discovery/guice/Jersey2EurekaModuleTest.java @@ -0,0 +1,85 @@ +package com.netflix.discovery.guice; + +import com.google.inject.AbstractModule; +import com.google.inject.Binding; +import com.google.inject.Key; +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.MyDataCenterInstanceConfigProvider; +import com.netflix.config.ConfigurationManager; +import com.netflix.discovery.DiscoveryClient; +import com.netflix.discovery.DiscoveryManager; +import com.netflix.discovery.EurekaClient; +import com.netflix.discovery.EurekaClientConfig; +import com.netflix.discovery.shared.transport.jersey.TransportClientFactories; +import com.netflix.discovery.shared.transport.jersey2.Jersey2TransportClientFactories; +import com.netflix.governator.InjectorBuilder; +import com.netflix.governator.LifecycleInjector; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +/** + * @author David Liu + */ +public class Jersey2EurekaModuleTest { + + private LifecycleInjector injector; + + @Before + public void setUp() throws Exception { + ConfigurationManager.getConfigInstance().setProperty("eureka.region", "default"); + ConfigurationManager.getConfigInstance().setProperty("eureka.shouldFetchRegistry", "false"); + ConfigurationManager.getConfigInstance().setProperty("eureka.registration.enabled", "false"); + ConfigurationManager.getConfigInstance().setProperty("eureka.serviceUrl.default", "http://localhost:8080/eureka/v2"); + + injector = InjectorBuilder + .fromModule(new Jersey2EurekaModule()) + .overrideWith(new AbstractModule() { + @Override + protected void configure() { + // the default impl of EurekaInstanceConfig is CloudInstanceConfig, which we only want in an AWS + // environment. Here we override that by binding MyDataCenterInstanceConfig to EurekaInstanceConfig. + bind(EurekaInstanceConfig.class).toProvider(MyDataCenterInstanceConfigProvider.class).in(Scopes.SINGLETON); + } + }) + .createInjector(); + } + + @After + public void tearDown() { + if (injector != null) { + injector.shutdown(); + } + ConfigurationManager.getConfigInstance().clear(); + } + + @SuppressWarnings("deprecation") + @Test + public void testDI() { + InstanceInfo instanceInfo = injector.getInstance(InstanceInfo.class); + Assert.assertEquals(ApplicationInfoManager.getInstance().getInfo(), instanceInfo); + + EurekaClient eurekaClient = injector.getInstance(EurekaClient.class); + DiscoveryClient discoveryClient = injector.getInstance(DiscoveryClient.class); + + Assert.assertEquals(DiscoveryManager.getInstance().getEurekaClient(), eurekaClient); + Assert.assertEquals(DiscoveryManager.getInstance().getDiscoveryClient(), discoveryClient); + Assert.assertEquals(eurekaClient, discoveryClient); + + EurekaClientConfig eurekaClientConfig = injector.getInstance(EurekaClientConfig.class); + Assert.assertEquals(DiscoveryManager.getInstance().getEurekaClientConfig(), eurekaClientConfig); + + EurekaInstanceConfig eurekaInstanceConfig = injector.getInstance(EurekaInstanceConfig.class); + Assert.assertEquals(DiscoveryManager.getInstance().getEurekaInstanceConfig(), eurekaInstanceConfig); + + Binding binding = injector.getExistingBinding(Key.get(TransportClientFactories.class)); + Assert.assertNotNull(binding); // has a binding for jersey2 + + TransportClientFactories transportClientFactories = injector.getInstance(TransportClientFactories.class); + Assert.assertTrue(transportClientFactories instanceof Jersey2TransportClientFactories); + } +} diff --git a/eureka-client-jersey2/src/test/java/com/netflix/discovery/shared/transport/jersey2/Jersey2ApplicationClientTest.java b/eureka-client-jersey2/src/test/java/com/netflix/discovery/shared/transport/jersey2/AbstractJersey2EurekaHttpClientTest.java similarity index 86% rename from eureka-client-jersey2/src/test/java/com/netflix/discovery/shared/transport/jersey2/Jersey2ApplicationClientTest.java rename to eureka-client-jersey2/src/test/java/com/netflix/discovery/shared/transport/jersey2/AbstractJersey2EurekaHttpClientTest.java index 7a4d44a5b..edfcee279 100644 --- a/eureka-client-jersey2/src/test/java/com/netflix/discovery/shared/transport/jersey2/Jersey2ApplicationClientTest.java +++ b/eureka-client-jersey2/src/test/java/com/netflix/discovery/shared/transport/jersey2/AbstractJersey2EurekaHttpClientTest.java @@ -29,9 +29,9 @@ /** * @author Tomasz Bak */ -public class Jersey2ApplicationClientTest extends EurekaHttpClientCompatibilityTestSuite { +public class AbstractJersey2EurekaHttpClientTest extends EurekaHttpClientCompatibilityTestSuite { - private Jersey2ApplicationClient jersey2HttpClient; + private AbstractJersey2EurekaHttpClient jersey2HttpClient; @Override @After @@ -49,7 +49,7 @@ protected EurekaHttpClient getEurekaHttpClient(URI serviceURI) { factoryBuilder.withFeature(HttpAuthenticationFeature.basicBuilder().build()); } TransportClientFactory clientFactory = factoryBuilder.build(); - jersey2HttpClient = (Jersey2ApplicationClient) clientFactory.newClient(new DefaultEndpoint(serviceURI.toString())); + jersey2HttpClient = (AbstractJersey2EurekaHttpClient) clientFactory.newClient(new DefaultEndpoint(serviceURI.toString())); return jersey2HttpClient; } } \ No newline at end of file diff --git a/eureka-client/build.gradle b/eureka-client/build.gradle index c82b121a1..8c51262d7 100644 --- a/eureka-client/build.gradle +++ b/eureka-client/build.gradle @@ -1,6 +1,11 @@ apply plugin: 'nebula.test-jar' apply plugin: 'provided-base' +configurations.all { + // jersey2 + exclude group: 'org.glassfish.jersey' +} + dependencies { compile "com.netflix.netflix-commons:netflix-eventbus:0.3.0" compile 'com.thoughtworks.xstream:xstream:1.4.2' diff --git a/eureka-client/src/main/java/com/netflix/discovery/AbstractDiscoveryClientOptionalArgs.java b/eureka-client/src/main/java/com/netflix/discovery/AbstractDiscoveryClientOptionalArgs.java index 1033e675a..a170fe128 100644 --- a/eureka-client/src/main/java/com/netflix/discovery/AbstractDiscoveryClientOptionalArgs.java +++ b/eureka-client/src/main/java/com/netflix/discovery/AbstractDiscoveryClientOptionalArgs.java @@ -83,6 +83,7 @@ public TransportClientFactories getTransportClientFactories() { return transportClientFactories; } + @Inject(optional = true) public void setTransportClientFactories(TransportClientFactories transportClientFactories) { this.transportClientFactories = transportClientFactories; } diff --git a/eureka-client/src/main/java/com/netflix/discovery/DiscoveryClient.java b/eureka-client/src/main/java/com/netflix/discovery/DiscoveryClient.java index c57f7a6de..2e90ae596 100644 --- a/eureka-client/src/main/java/com/netflix/discovery/DiscoveryClient.java +++ b/eureka-client/src/main/java/com/netflix/discovery/DiscoveryClient.java @@ -48,6 +48,8 @@ import javax.inject.Singleton; import javax.ws.rs.core.Response.Status; +import com.netflix.discovery.shared.transport.jersey.Jersey1DiscoveryClientOptionalArgs; +import com.netflix.discovery.shared.transport.jersey.Jersey1TransportClientFactories; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -75,13 +77,11 @@ import com.netflix.discovery.shared.transport.TransportClientFactory; import com.netflix.discovery.shared.transport.jersey.EurekaJerseyClient; import com.netflix.discovery.shared.transport.jersey.TransportClientFactories; -import com.netflix.discovery.shared.transport.jersey.TransportClientFactoriesProvider; import com.netflix.discovery.util.ThresholdLevelsMetric; import com.netflix.servo.annotations.DataSourceType; import com.netflix.servo.monitor.Counter; import com.netflix.servo.monitor.Monitors; import com.netflix.servo.monitor.Stopwatch; -import com.sun.jersey.api.client.filter.ClientFilter; /** * The class that is instrumental for interactions with Eureka Server. @@ -218,6 +218,10 @@ void shutdown() { } } + public static class DiscoveryClientOptionalArgs extends Jersey1DiscoveryClientOptionalArgs { + + } + /** * Assumes applicationInfoManager is already initialized * @@ -234,6 +238,14 @@ public DiscoveryClient(InstanceInfo myInfo, EurekaClientConfig config) { * @deprecated use constructor that takes ApplicationInfoManager instead of InstanceInfo directly */ @Deprecated + public DiscoveryClient(InstanceInfo myInfo, EurekaClientConfig config, DiscoveryClientOptionalArgs args) { + this(ApplicationInfoManager.getInstance(), config, args); + } + + /** + * @deprecated use constructor that takes ApplicationInfoManager instead of InstanceInfo directly + */ + @Deprecated public DiscoveryClient(InstanceInfo myInfo, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args) { this(ApplicationInfoManager.getInstance(), config, args); } @@ -242,6 +254,14 @@ public DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClie this(applicationInfoManager, config, null); } + /** + * @deprecated use the version that take {@link com.netflix.discovery.AbstractDiscoveryClientOptionalArgs} instead + */ + @Deprecated + public DiscoveryClient(ApplicationInfoManager applicationInfoManager, final EurekaClientConfig config, DiscoveryClientOptionalArgs args) { + this(applicationInfoManager, config, (AbstractDiscoveryClientOptionalArgs) args); + } + public DiscoveryClient(ApplicationInfoManager applicationInfoManager, final EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args) { this(applicationInfoManager, config, args, new Provider() { private volatile BackupRegistry backupRegistryInstance; @@ -423,12 +443,15 @@ private void scheduleServerEndpointTask(EurekaTransport eurekaTransport, // Ignore the raw types warnings since the client filter interface changed between jersey 1/2 @SuppressWarnings("rawtypes") - TransportClientFactories transportClientFactories = new TransportClientFactoriesProvider(argsTransportClientFactories).get(); + TransportClientFactories transportClientFactories = argsTransportClientFactories == null + ? new Jersey1TransportClientFactories() + : argsTransportClientFactories; +// TransportClientFactories transportClientFactories = new TransportClientFactoriesProvider(argsTransportClientFactories).get(); // If the transport factory was not supplied with args, assume they are using jersey 1 for passivity eurekaTransport.transportClientFactory = providedJerseyClient == null - ? transportClientFactories.newTransportClientFactory(clientConfig, (Collection) additionalFilters, applicationInfoManager.getInfo()) - : transportClientFactories.newTransportClientFactory((Collection) additionalFilters, providedJerseyClient); + ? transportClientFactories.newTransportClientFactory(clientConfig, additionalFilters, applicationInfoManager.getInfo()) + : transportClientFactories.newTransportClientFactory(additionalFilters, providedJerseyClient); ApplicationsResolver.ApplicationsSource applicationsSource = new ApplicationsResolver.ApplicationsSource() { @Override diff --git a/eureka-client/src/main/java/com/netflix/discovery/guice/EurekaModule.java b/eureka-client/src/main/java/com/netflix/discovery/guice/EurekaModule.java index f7da4f3ce..18e461b2b 100644 --- a/eureka-client/src/main/java/com/netflix/discovery/guice/EurekaModule.java +++ b/eureka-client/src/main/java/com/netflix/discovery/guice/EurekaModule.java @@ -12,7 +12,7 @@ import com.netflix.discovery.EurekaClient; import com.netflix.discovery.EurekaClientConfig; import com.netflix.discovery.providers.DefaultEurekaClientConfigProvider; -import com.netflix.discovery.shared.transport.jersey.DiscoveryClientOptionalArgs; +import com.netflix.discovery.shared.transport.jersey.Jersey1DiscoveryClientOptionalArgs; /** * @author David Liu @@ -34,9 +34,9 @@ protected void configure() { bind(InstanceInfo.class).toProvider(EurekaConfigBasedInstanceInfoProvider.class).in(Scopes.SINGLETON); bind(EurekaClient.class).to(DiscoveryClient.class).in(Scopes.SINGLETON); - + // Default to the jersey1 discovery client optional args - bind(AbstractDiscoveryClientOptionalArgs.class).to(DiscoveryClientOptionalArgs.class).in(Scopes.SINGLETON); + bind(AbstractDiscoveryClientOptionalArgs.class).to(Jersey1DiscoveryClientOptionalArgs.class).in(Scopes.SINGLETON); } @Override diff --git a/eureka-client/src/main/java/com/netflix/discovery/shared/transport/decorator/RetryableEurekaHttpClient.java b/eureka-client/src/main/java/com/netflix/discovery/shared/transport/decorator/RetryableEurekaHttpClient.java index c471b29e1..adf759201 100644 --- a/eureka-client/src/main/java/com/netflix/discovery/shared/transport/decorator/RetryableEurekaHttpClient.java +++ b/eureka-client/src/main/java/com/netflix/discovery/shared/transport/decorator/RetryableEurekaHttpClient.java @@ -126,7 +126,8 @@ protected EurekaHttpResponse execute(RequestExecutor requestExecutor) } logger.warn("Request execution failure with status code {}; retrying on another server if available", response.getStatusCode()); } catch (Exception e) { - logger.warn("Request execution failure", e.getMessage()); // just log message as the underlying client should log the stacktrace + logger.warn("Request execution failure", e.getMessage()); + e.printStackTrace(); } // Connection error or 5xx from the server that must be retried on another server diff --git a/eureka-client/src/main/java/com/netflix/discovery/shared/transport/jersey/DiscoveryClientOptionalArgs.java b/eureka-client/src/main/java/com/netflix/discovery/shared/transport/jersey/Jersey1DiscoveryClientOptionalArgs.java similarity index 71% rename from eureka-client/src/main/java/com/netflix/discovery/shared/transport/jersey/DiscoveryClientOptionalArgs.java rename to eureka-client/src/main/java/com/netflix/discovery/shared/transport/jersey/Jersey1DiscoveryClientOptionalArgs.java index e731a8530..e985aceb6 100644 --- a/eureka-client/src/main/java/com/netflix/discovery/shared/transport/jersey/DiscoveryClientOptionalArgs.java +++ b/eureka-client/src/main/java/com/netflix/discovery/shared/transport/jersey/Jersey1DiscoveryClientOptionalArgs.java @@ -8,6 +8,6 @@ * * @author Matt Nelson */ -public class DiscoveryClientOptionalArgs extends AbstractDiscoveryClientOptionalArgs { +public class Jersey1DiscoveryClientOptionalArgs extends AbstractDiscoveryClientOptionalArgs { } diff --git a/eureka-client/src/main/java/com/netflix/discovery/shared/transport/jersey/JerseyApplicationClient.java b/eureka-client/src/main/java/com/netflix/discovery/shared/transport/jersey/JerseyApplicationClient.java index 7f3f41a97..8fed3f431 100644 --- a/eureka-client/src/main/java/com/netflix/discovery/shared/transport/jersey/JerseyApplicationClient.java +++ b/eureka-client/src/main/java/com/netflix/discovery/shared/transport/jersey/JerseyApplicationClient.java @@ -23,7 +23,7 @@ import java.util.Map; /** - * A version of Jersey {@link EurekaHttpClient} to be used by applications. + * A version of Jersey1 {@link EurekaHttpClient} to be used by applications. * * @author Tomasz Bak */ diff --git a/eureka-client/src/main/java/com/netflix/discovery/shared/transport/jersey/TransportClientFactoriesProvider.java b/eureka-client/src/main/java/com/netflix/discovery/shared/transport/jersey/TransportClientFactoriesProvider.java deleted file mode 100644 index eb625c87c..000000000 --- a/eureka-client/src/main/java/com/netflix/discovery/shared/transport/jersey/TransportClientFactoriesProvider.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.netflix.discovery.shared.transport.jersey; - -import javax.inject.Provider; - -public class TransportClientFactoriesProvider implements Provider> { - - private final TransportClientFactories transportClientFactories; - - public TransportClientFactoriesProvider(TransportClientFactories transportClientFactories) { - if (transportClientFactories == null) { - // Default to the jersey1 transport client factory - this.transportClientFactories = new Jersey1TransportClientFactories(); - } else { - this.transportClientFactories = transportClientFactories; - } - } - - @Override - public TransportClientFactories get() { - return transportClientFactories; - } -} diff --git a/eureka-client/src/test/java/com/netflix/discovery/EurekaClientLifecycleTest.java b/eureka-client/src/test/java/com/netflix/discovery/EurekaClientLifecycleTest.java index 9a734f07e..85b8626ef 100644 --- a/eureka-client/src/test/java/com/netflix/discovery/EurekaClientLifecycleTest.java +++ b/eureka-client/src/test/java/com/netflix/discovery/EurekaClientLifecycleTest.java @@ -16,7 +16,7 @@ import com.netflix.discovery.shared.transport.EurekaHttpClient; import com.netflix.discovery.shared.transport.EurekaHttpResponse; import com.netflix.discovery.shared.transport.SimpleEurekaHttpServer; -import com.netflix.discovery.shared.transport.jersey.DiscoveryClientOptionalArgs; +import com.netflix.discovery.shared.transport.jersey.Jersey1DiscoveryClientOptionalArgs; import com.netflix.discovery.util.InstanceInfoGenerator; import com.netflix.governator.guice.LifecycleInjector; import com.netflix.governator.lifecycle.LifecycleManager; @@ -84,7 +84,7 @@ public void testEurekaClientLifecycle() throws Exception { protected void configure() { bind(EurekaInstanceConfig.class).to(LocalEurekaInstanceConfig.class); bind(EurekaClientConfig.class).to(LocalEurekaClientConfig.class); - bind(AbstractDiscoveryClientOptionalArgs.class).to(DiscoveryClientOptionalArgs.class).in(Scopes.SINGLETON); + bind(AbstractDiscoveryClientOptionalArgs.class).to(Jersey1DiscoveryClientOptionalArgs.class).in(Scopes.SINGLETON); } } ) @@ -120,7 +120,7 @@ protected void configure() { bind(EurekaInstanceConfig.class).to(LocalEurekaInstanceConfig.class); bind(EurekaClientConfig.class).to(BadServerEurekaClientConfig.class); bind(BackupRegistry.class).toInstance(backupRegistry); - bind(AbstractDiscoveryClientOptionalArgs.class).to(DiscoveryClientOptionalArgs.class).in(Scopes.SINGLETON); + bind(AbstractDiscoveryClientOptionalArgs.class).to(Jersey1DiscoveryClientOptionalArgs.class).in(Scopes.SINGLETON); } } ) diff --git a/eureka-client/src/test/java/com/netflix/discovery/DiscoveryClientOptionalArgsTest.java b/eureka-client/src/test/java/com/netflix/discovery/Jersey1DiscoveryClientOptionalArgsTest.java similarity index 83% rename from eureka-client/src/test/java/com/netflix/discovery/DiscoveryClientOptionalArgsTest.java rename to eureka-client/src/test/java/com/netflix/discovery/Jersey1DiscoveryClientOptionalArgsTest.java index 39ec66792..59da1b716 100644 --- a/eureka-client/src/test/java/com/netflix/discovery/DiscoveryClientOptionalArgsTest.java +++ b/eureka-client/src/test/java/com/netflix/discovery/Jersey1DiscoveryClientOptionalArgsTest.java @@ -2,23 +2,23 @@ import javax.inject.Provider; +import com.netflix.discovery.shared.transport.jersey.Jersey1DiscoveryClientOptionalArgs; import org.junit.Before; import org.junit.Test; import com.netflix.appinfo.HealthCheckCallback; import com.netflix.appinfo.HealthCheckHandler; -import com.netflix.discovery.shared.transport.jersey.DiscoveryClientOptionalArgs; /** * @author Matt Nelson */ -public class DiscoveryClientOptionalArgsTest { +public class Jersey1DiscoveryClientOptionalArgsTest { - private DiscoveryClientOptionalArgs args; + private Jersey1DiscoveryClientOptionalArgs args; @Before public void before() { - args = new DiscoveryClientOptionalArgs(); + args = new Jersey1DiscoveryClientOptionalArgs(); } @Test diff --git a/eureka-client/src/test/java/com/netflix/discovery/guice/EurekaModuleTest.java b/eureka-client/src/test/java/com/netflix/discovery/guice/EurekaModuleTest.java index bf9cdc918..185490d3c 100644 --- a/eureka-client/src/test/java/com/netflix/discovery/guice/EurekaModuleTest.java +++ b/eureka-client/src/test/java/com/netflix/discovery/guice/EurekaModuleTest.java @@ -1,7 +1,8 @@ package com.netflix.discovery.guice; import com.google.inject.AbstractModule; -import com.google.inject.Injector; +import com.google.inject.Binding; +import com.google.inject.Key; import com.google.inject.Scopes; import com.netflix.appinfo.ApplicationInfoManager; import com.netflix.appinfo.EurekaInstanceConfig; @@ -12,6 +13,7 @@ import com.netflix.discovery.DiscoveryManager; import com.netflix.discovery.EurekaClient; import com.netflix.discovery.EurekaClientConfig; +import com.netflix.discovery.shared.transport.jersey.TransportClientFactories; import com.netflix.governator.InjectorBuilder; import com.netflix.governator.LifecycleInjector; import org.junit.After; @@ -72,5 +74,8 @@ public void testDI() { EurekaInstanceConfig eurekaInstanceConfig = injector.getInstance(EurekaInstanceConfig.class); Assert.assertEquals(DiscoveryManager.getInstance().getEurekaInstanceConfig(), eurekaInstanceConfig); + + Binding binding = injector.getExistingBinding(Key.get(TransportClientFactories.class)); + Assert.assertNull(binding); // no bindings so defaulting to default of jersey1 } } diff --git a/eureka-core-jersey2/README.md b/eureka-core-jersey2/README.md new file mode 100644 index 000000000..2689bd510 --- /dev/null +++ b/eureka-core-jersey2/README.md @@ -0,0 +1 @@ +Please note that this jersey2 compatible Eureka core (eureka-core-jersey2) is created and maintained by the community. Netflix does not currently use this library internally. \ No newline at end of file diff --git a/eureka-core-jersey2/build.gradle b/eureka-core-jersey2/build.gradle index 1eddab41f..b33c59912 100644 --- a/eureka-core-jersey2/build.gradle +++ b/eureka-core-jersey2/build.gradle @@ -10,7 +10,13 @@ dependencies { compile 'org.glassfish.jersey.core:jersey-client:2.23.1' compile 'org.glassfish.jersey.connectors:jersey-apache-connector:2.23.1' - 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.mock-server:mockserver-netty:${mockserverVersion}" testCompile "org.mockito:mockito-core:${mockitoVersion}" diff --git a/eureka-core-jersey2/src/main/java/com/netflix/eureka/transport/Jersey2ReplicationClient.java b/eureka-core-jersey2/src/main/java/com/netflix/eureka/transport/Jersey2ReplicationClient.java index ffe827e8a..a762f93dc 100644 --- a/eureka-core-jersey2/src/main/java/com/netflix/eureka/transport/Jersey2ReplicationClient.java +++ b/eureka-core-jersey2/src/main/java/com/netflix/eureka/transport/Jersey2ReplicationClient.java @@ -41,8 +41,11 @@ public class Jersey2ReplicationClient extends AbstractJersey2EurekaHttpClient im private static final Logger logger = LoggerFactory.getLogger(Jersey2ReplicationClient.class); - public Jersey2ReplicationClient(EurekaJersey2Client jerseyClient, String serviceUrl) { - super(jerseyClient, serviceUrl, true); + private final EurekaJersey2Client eurekaJersey2Client; + + public Jersey2ReplicationClient(EurekaJersey2Client eurekaJersey2Client, String serviceUrl) { + super(eurekaJersey2Client.getClient(), serviceUrl); + this.eurekaJersey2Client = eurekaJersey2Client; } @Override @@ -59,7 +62,7 @@ public EurekaHttpResponse 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()); @@ -89,7 +92,7 @@ public EurekaHttpResponse statusUpdate(String asgName, ASGStatus newStatus Response response = null; try { String urlPath = "asg/" + asgName + "/status"; - response = jerseyClient.getClient().target(serviceUrl) + response = jerseyClient.target(serviceUrl) .path(urlPath) .queryParam("value", newStatus.name()) .request() @@ -107,7 +110,7 @@ public EurekaHttpResponse statusUpdate(String asgName, ASGStatus newStatus public EurekaHttpResponse submitBatchUpdates(ReplicationList replicationList) { Response response = null; try { - response = jerseyClient.getClient().target(serviceUrl) + response = jerseyClient.target(serviceUrl) .path(PeerEurekaNode.BATCH_URL_PATH) .request(MediaType.APPLICATION_JSON_TYPE) .post(Entity.json(replicationList)); @@ -126,7 +129,7 @@ public EurekaHttpResponse submitBatchUpdates(Replicatio @Override public void shutdown() { super.shutdown(); - jerseyClient.destroyResources(); + eurekaJersey2Client.destroyResources(); } public static Jersey2ReplicationClient createReplicationClient(EurekaServerConfig config, ServerCodecs serverCodecs, String serviceUrl) { diff --git a/eureka-test-utils/src/main/java/com/netflix/discovery/junit/resource/DiscoveryClientResource.java b/eureka-test-utils/src/main/java/com/netflix/discovery/junit/resource/DiscoveryClientResource.java index a89b4d6f3..62c2d3dc4 100644 --- a/eureka-test-utils/src/main/java/com/netflix/discovery/junit/resource/DiscoveryClientResource.java +++ b/eureka-test-utils/src/main/java/com/netflix/discovery/junit/resource/DiscoveryClientResource.java @@ -26,7 +26,7 @@ import com.netflix.discovery.EurekaClient; import com.netflix.discovery.EurekaClientConfig; import com.netflix.discovery.shared.transport.SimpleEurekaHttpServer; -import com.netflix.discovery.shared.transport.jersey.DiscoveryClientOptionalArgs; +import com.netflix.discovery.shared.transport.jersey.Jersey1DiscoveryClientOptionalArgs; import com.netflix.eventbus.impl.EventBusImpl; import com.netflix.eventbus.spi.EventBus; import com.netflix.eventbus.spi.InvalidSubscriberException; @@ -96,7 +96,7 @@ public EurekaClient getClient() { applicationInfoManager = createApplicationManager(); EurekaClientConfig clientConfig = createEurekaClientConfig(); - DiscoveryClientOptionalArgs optionalArgs = new DiscoveryClientOptionalArgs(); + Jersey1DiscoveryClientOptionalArgs optionalArgs = new Jersey1DiscoveryClientOptionalArgs(); eventBus = new EventBusImpl(); optionalArgs.setEventBus(eventBus);