From 58284a4508ceef8f26a0990c7831f3c82c910d6d Mon Sep 17 00:00:00 2001 From: Bertrand Renuart Date: Thu, 18 Jun 2015 01:07:12 +0200 Subject: [PATCH] Change Jersey init to avoid classpath scanning issues Scan classpath for Jersey resources with Spring and initialize the Jersey filter with a jaxrs Application instead. (Fixes gh-399, fixes gh-400) --- .../main/asciidoc/spring-cloud-netflix.adoc | 40 ---------- spring-cloud-netflix-eureka-server/pom.xml | 1 - .../server/EurekaServerConfiguration.java | 77 +++++++++++++++++-- 3 files changed, 69 insertions(+), 49 deletions(-) diff --git a/docs/src/main/asciidoc/spring-cloud-netflix.adoc b/docs/src/main/asciidoc/spring-cloud-netflix.adoc index de4c2169..da9a9a3e 100644 --- a/docs/src/main/asciidoc/spring-cloud-netflix.adoc +++ b/docs/src/main/asciidoc/spring-cloud-netflix.adoc @@ -225,46 +225,6 @@ normal Eureka functionality under `/eureka/*`. Eureka background reading: see https://github.com/cfregly/fluxcapacitor/wiki/NetflixOSS-FAQ#eureka-service-discovery-load-balancer[flux capacitor] and https://groups.google.com/forum/?fromgroups#!topic/eureka_netflix/g3p2r7gHnN0[google group discussion]. -[TIP] -==== -You can run the Eureka server as an executable JAR (or WAR) using the -http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#build-tool-plugins[Spring -Boot build tools], but to avoid problems with classpath scanning in -Jersey 1.x you have to tell the build plugins to unpack the jars that -contain JAX-RS resources, e.g. (for Maven) - -.pom.xml -[source,xml,indent=0] ----- - - org.springframework.boot - spring-boot-maven-plugin - - - - com.netflix.eureka - eureka-core - - - com.netflix.eureka - eureka-client - - - - ----- - -or with Gradle - -.build.gradle -[source,java,indent=0] ----- -apply plugin: 'spring-boot' -springBoot { - requiresUnpack = ['com.netflix.eureka:eureka-core','com.netflix.eureka:eureka-client'] -} ----- -==== [TIP] ==== diff --git a/spring-cloud-netflix-eureka-server/pom.xml b/spring-cloud-netflix-eureka-server/pom.xml index 35d40be6..17e7c488 100644 --- a/spring-cloud-netflix-eureka-server/pom.xml +++ b/spring-cloud-netflix-eureka-server/pom.xml @@ -57,7 +57,6 @@ com.sun.jersey jersey-server - runtime com.netflix.eureka diff --git a/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerConfiguration.java b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerConfiguration.java index f30146b4..1264d650 100644 --- a/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerConfiguration.java +++ b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerConfiguration.java @@ -17,21 +17,34 @@ package org.springframework.cloud.netflix.eureka.server; import java.util.Collections; +import java.util.Map; +import java.util.Set; import javax.servlet.Filter; +import javax.ws.rs.Path; +import javax.ws.rs.ext.Provider; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.embedded.FilterRegistrationBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.eureka.EurekaServerConfigBean; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.core.Ordered; +import org.springframework.core.env.Environment; +import org.springframework.core.io.ResourceLoader; +import org.springframework.core.type.filter.AnnotationTypeFilter; +import org.springframework.util.ClassUtils; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.sun.jersey.api.core.DefaultResourceConfig; import com.sun.jersey.spi.container.servlet.ServletContainer; /** @@ -42,27 +55,76 @@ import com.sun.jersey.spi.container.servlet.ServletContainer; @EnableDiscoveryClient @EnableConfigurationProperties(EurekaDashboardProperties.class) public class EurekaServerConfiguration extends WebMvcConfigurerAdapter { - + /** + * List of packages containing Jersey resources required by the Eureka server + */ + private static String[] EUREKA_PACKAGES = new String[] { + "com.netflix.discovery", + "com.netflix.eureka"}; + + @Bean @ConditionalOnProperty(prefix = "eureka.dashboard", name = "enabled", matchIfMissing = true) public EurekaController eurekaController() { return new EurekaController(); } + /** + * Register the Jersey filter + * + * @param eurekaJerseyApp the jersey application + * @return + */ @Bean - public FilterRegistrationBean jersey() { + public FilterRegistrationBean jerseyFilterRegistration(javax.ws.rs.core.Application eurekaJerseyApp) { FilterRegistrationBean bean = new FilterRegistrationBean(); - bean.setFilter(new ServletContainer()); + bean.setFilter(new ServletContainer(eurekaJerseyApp)); bean.setOrder(Ordered.LOWEST_PRECEDENCE); - bean.addInitParameter("com.sun.jersey.config.property.WebPageContentRegex", - EurekaServerConfigBean.DEFAULT_PREFIX + "/(fonts|images|css|js)/.*"); - bean.addInitParameter("com.sun.jersey.config.property.packages", - "com.netflix.discovery;com.netflix.eureka"); bean.setUrlPatterns(Collections .singletonList(EurekaServerConfigBean.DEFAULT_PREFIX + "/*")); + return bean; } + /** + * Construct a Jersey {@link javax.ws.rs.core.Application} with all the resources + * required by the Eureka server. + */ + @Bean + public javax.ws.rs.core.Application jerseyApplication(Environment environment, ResourceLoader resourceLoader) { + + ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false, environment); + + // Filter to include only classes that have a particular annotation. + // + provider.addIncludeFilter(new AnnotationTypeFilter(Path.class)); + provider.addIncludeFilter(new AnnotationTypeFilter(Provider.class)); + + // Find classes in Eureka packages (or subpackages) + // + Set> classes = Sets.newHashSet(); + for(String basePackage: EUREKA_PACKAGES) { + Set beans = provider.findCandidateComponents(basePackage); + for (BeanDefinition bd : beans) { + Class cls = ClassUtils.resolveClassName(bd.getBeanClassName(), resourceLoader.getClassLoader()); + classes.add(cls); + } + } + + // Construct the Jersey ResourceConfig + // + Map propsAndFeatures = Maps.newHashMap(); + propsAndFeatures.put( + // Skip static content used by the webapp + ServletContainer.PROPERTY_WEB_PAGE_CONTENT_REGEX, + EurekaServerConfigBean.DEFAULT_PREFIX + "/(fonts|images|css|js)/.*"); + + DefaultResourceConfig rc = new DefaultResourceConfig(classes); + rc.setPropertiesAndFeatures(propsAndFeatures); + + return rc; + } + @Bean public FilterRegistrationBean traceFilterRegistration( @Qualifier("webRequestLoggingFilter") Filter filter) { @@ -71,5 +133,4 @@ public class EurekaServerConfiguration extends WebMvcConfigurerAdapter { bean.setOrder(Ordered.LOWEST_PRECEDENCE - 10); return bean; } - }