diff --git a/spring-boot-autoconfigure/pom.xml b/spring-boot-autoconfigure/pom.xml
index 1493a6a1a60..4500ac787c9 100755
--- a/spring-boot-autoconfigure/pom.xml
+++ b/spring-boot-autoconfigure/pom.xml
@@ -130,6 +130,11 @@
jersey-spring3
true
+
+ org.glassfish.jersey.media
+ jersey-media-json-jackson
+ true
+
commons-dbcp
commons-dbcp
diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfiguration.java
index 3c30ecdb71d..163b6691462 100644
--- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfiguration.java
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfiguration.java
@@ -26,15 +26,21 @@ import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.Provider;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletContainer;
import org.glassfish.jersey.servlet.ServletProperties;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@@ -43,6 +49,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.boot.context.embedded.RegistrationBean;
@@ -73,6 +80,7 @@ import org.springframework.web.filter.RequestContextFilter;
@ConditionalOnWebApplication
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@AutoConfigureBefore(DispatcherServletAutoConfiguration.class)
+@AutoConfigureAfter(JacksonAutoConfiguration.class)
@EnableConfigurationProperties(JerseyProperties.class)
public class JerseyAutoConfiguration implements ServletContextAware {
@@ -84,10 +92,24 @@ public class JerseyAutoConfiguration implements ServletContextAware {
@Autowired
private ResourceConfig config;
+ @Autowired(required = false)
+ private ResourceConfigCustomizer customizer;
+
private String path;
@PostConstruct
public void path() {
+ resolveApplicationPath();
+ applyCustomConfig();
+ }
+
+ private void applyCustomConfig() {
+ if (this.customizer != null) {
+ this.customizer.customize(this.config);
+ }
+ }
+
+ private void resolveApplicationPath() {
if (StringUtils.hasLength(this.jersey.getApplicationPath())) {
this.path = parseApplicationPath(this.jersey.getApplicationPath());
}
@@ -193,6 +215,36 @@ public class JerseyAutoConfiguration implements ServletContextAware {
// will try and register a ContextLoaderListener which we don't need
servletContext.setInitParameter("contextConfigLocation", "");
}
+ }
+
+ @ConditionalOnClass(JacksonFeature.class)
+ @Configuration
+ static class ObjectMapperResourceConfigCustomizer {
+
+ @Bean
+ public ResourceConfigCustomizer resourceConfigCustomizer() {
+ return new ResourceConfigCustomizer() {
+ @Override
+ public void customize(ResourceConfig config) {
+ config.register(JacksonFeature.class);
+ config.register(ObjectMapperContextResolver.class);
+ }
+ };
+ }
+
+ @Provider
+ static class ObjectMapperContextResolver
+ implements ContextResolver {
+
+ @Autowired
+ private ObjectMapper objectMapper;
+
+ @Override
+ public ObjectMapper getContext(Class> type) {
+ return this.objectMapper;
+ }
+
+ }
}
diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/ResourceConfigCustomizer.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/ResourceConfigCustomizer.java
new file mode 100644
index 00000000000..030d8912d8b
--- /dev/null
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/ResourceConfigCustomizer.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2012-2016 the original author or authors.
+ *
+ * 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 org.springframework.boot.autoconfigure.jersey;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Callback for customizing the Jersey {@link Configuration}.
+ *
+ * @author Eddú Meléndez
+ * @since 1.4.0
+ * @see JerseyAutoConfiguration
+ */
+public interface ResourceConfigCustomizer {
+
+ void customize(ResourceConfig config);
+
+}
diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationCustomObjectMapperProviderTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationCustomObjectMapperProviderTests.java
new file mode 100644
index 00000000000..b581f589f52
--- /dev/null
+++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationCustomObjectMapperProviderTests.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2012-2016 the original author or authors.
+ *
+ * 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 org.springframework.boot.autoconfigure.jersey;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
+import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
+import org.springframework.boot.autoconfigure.jersey.JerseyAutoConfigurationObjectMapperProviderTests.Application;
+import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration;
+import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration;
+import org.springframework.boot.test.IntegrationTest;
+import org.springframework.boot.test.SpringApplicationConfiguration;
+import org.springframework.boot.test.TestRestTemplate;
+import org.springframework.context.annotation.Import;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.web.client.RestTemplate;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Eddú Meléndez
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringApplicationConfiguration(Application.class)
+@IntegrationTest({ "server.port=0", "spring.jackson.serialization-inclusion=non_null" })
+@WebAppConfiguration
+public class JerseyAutoConfigurationCustomObjectMapperProviderTests {
+
+ @Value("${local.server.port}")
+ private int port;
+
+ private RestTemplate restTemplate = new TestRestTemplate();
+
+ @Test
+ public void contextLoads() {
+ ResponseEntity response = this.restTemplate.getForEntity(
+ "http://localhost:" + this.port + "/rest/message", String.class);
+ assertEquals(HttpStatus.OK, response.getStatusCode());
+ assertEquals("{\"subject\":\"Jersey\"}", response.getBody());
+ }
+
+ @MinimalWebConfiguration
+ @ApplicationPath("/rest")
+ @Path("/message")
+ public static class Application extends ResourceConfig {
+
+ @GET
+ public Message message() {
+ return new Message("Jersey", null);
+ }
+
+ public Application() {
+ register(Application.class);
+ }
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+ }
+
+ public static class Message {
+
+ private String subject;
+
+ private String body;
+
+ public Message() {
+
+ }
+
+ public Message(String subject, String body) {
+ this.subject = subject;
+ this.body = body;
+ }
+
+ public String getSubject() {
+ return this.subject;
+ }
+
+ public void setSubject(String subject) {
+ this.subject = subject;
+ }
+
+ public String getBody() {
+ return this.body;
+ }
+
+ public void setBody(String body) {
+ this.body = body;
+ }
+ }
+
+ @Target(ElementType.TYPE)
+ @Retention(RetentionPolicy.RUNTIME)
+ @Documented
+ @Import({ EmbeddedServletContainerAutoConfiguration.class,
+ JacksonAutoConfiguration.class, ServerPropertiesAutoConfiguration.class,
+ JerseyAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class })
+ protected @interface MinimalWebConfiguration {
+ }
+
+}
diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationObjectMapperProviderTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationObjectMapperProviderTests.java
new file mode 100644
index 00000000000..44c49fe378f
--- /dev/null
+++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfigurationObjectMapperProviderTests.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2012-2016 the original author or authors.
+ *
+ * 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 org.springframework.boot.autoconfigure.jersey;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
+import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
+import org.springframework.boot.autoconfigure.jersey.JerseyAutoConfigurationObjectMapperProviderTests.Application;
+import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration;
+import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration;
+import org.springframework.boot.test.IntegrationTest;
+import org.springframework.boot.test.SpringApplicationConfiguration;
+import org.springframework.boot.test.TestRestTemplate;
+import org.springframework.context.annotation.Import;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.web.client.RestTemplate;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Eddú Meléndez
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringApplicationConfiguration(Application.class)
+@IntegrationTest("server.port=0")
+@WebAppConfiguration
+public class JerseyAutoConfigurationObjectMapperProviderTests {
+
+ @Value("${local.server.port}")
+ private int port;
+
+ private RestTemplate restTemplate = new TestRestTemplate();
+
+ @Test
+ public void contextLoads() {
+ ResponseEntity response = this.restTemplate.getForEntity(
+ "http://localhost:" + this.port + "/rest/message", String.class);
+ assertEquals(HttpStatus.OK, response.getStatusCode());
+ assertEquals("{\"subject\":\"Jersey\",\"body\":null}", response.getBody());
+ }
+
+ @MinimalWebConfiguration
+ @ApplicationPath("/rest")
+ @Path("/message")
+ public static class Application extends ResourceConfig {
+
+ @GET
+ public Message message() {
+ return new Message("Jersey", null);
+ }
+
+ public Application() {
+ register(Application.class);
+ }
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+ }
+
+ public static class Message {
+
+ private String subject;
+
+ private String body;
+
+ public Message() {
+
+ }
+
+ public Message(String subject, String body) {
+ this.subject = subject;
+ this.body = body;
+ }
+
+ public String getSubject() {
+ return this.subject;
+ }
+
+ public void setSubject(String subject) {
+ this.subject = subject;
+ }
+
+ public String getBody() {
+ return this.body;
+ }
+
+ public void setBody(String body) {
+ this.body = body;
+ }
+ }
+
+ @Target(ElementType.TYPE)
+ @Retention(RetentionPolicy.RUNTIME)
+ @Documented
+ @Import({ EmbeddedServletContainerAutoConfiguration.class,
+ JacksonAutoConfiguration.class, ServerPropertiesAutoConfiguration.class,
+ JerseyAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class })
+ protected @interface MinimalWebConfiguration {
+ }
+
+}