diff --git a/spring-boot-devtools/pom.xml b/spring-boot-devtools/pom.xml index 838c1af0b03..927b5323b94 100644 --- a/spring-boot-devtools/pom.xml +++ b/spring-boot-devtools/pom.xml @@ -127,6 +127,11 @@ spring-websocket test + + org.springframework.hateoas + spring-hateoas + test + org.apache.tomcat.embed tomcat-embed-websocket diff --git a/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/HateoasObjenesisCacheDisabler.java b/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/HateoasObjenesisCacheDisabler.java index eea57cfdc65..23a2714c9b9 100644 --- a/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/HateoasObjenesisCacheDisabler.java +++ b/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/HateoasObjenesisCacheDisabler.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2017 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. @@ -20,7 +20,9 @@ import java.lang.reflect.Field; import javax.annotation.PostConstruct; -import org.springframework.objenesis.ObjenesisStd; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; @@ -35,6 +37,9 @@ import org.springframework.util.ReflectionUtils; */ class HateoasObjenesisCacheDisabler { + private static final Log logger = LogFactory + .getLog(HateoasObjenesisCacheDisabler.class); + private static boolean cacheDisabled; @PostConstruct @@ -45,20 +50,36 @@ class HateoasObjenesisCacheDisabler { } } - private void doDisableCaching() { + void doDisableCaching() { try { Class type = ClassUtils.forName( "org.springframework.hateoas.core.DummyInvocationUtils", getClass().getClassLoader()); - Field objenesis = ReflectionUtils.findField(type, "OBJENESIS"); - if (objenesis != null) { - ReflectionUtils.makeAccessible(objenesis); - ReflectionUtils.setField(objenesis, null, new ObjenesisStd(false)); - } + removeObjenesisCache(type); } catch (Exception ex) { // Assume that Spring HATEOAS is not on the classpath and continue } } + private void removeObjenesisCache(Class dummyInvocationUtils) { + try { + Field objenesisField = ReflectionUtils.findField(dummyInvocationUtils, + "OBJENESIS"); + if (objenesisField != null) { + ReflectionUtils.makeAccessible(objenesisField); + Object objenesis = ReflectionUtils.getField(objenesisField, null); + Field cacheField = ReflectionUtils.findField(objenesis.getClass(), + "cache"); + ReflectionUtils.makeAccessible(cacheField); + ReflectionUtils.setField(cacheField, objenesis, null); + } + } + catch (Exception ex) { + logger.warn( + "Failed to disable Spring HATEOAS's Objenesis cache. ClassCastExceptions may occur", + ex); + } + } + } diff --git a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/HateoasObjenesisCacheDisablerTests.java b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/HateoasObjenesisCacheDisablerTests.java new file mode 100644 index 00000000000..f0066aa41d4 --- /dev/null +++ b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/HateoasObjenesisCacheDisablerTests.java @@ -0,0 +1,67 @@ +/* + * Copyright 2012-2017 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.devtools.autoconfigure; + +import java.util.concurrent.ConcurrentHashMap; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import org.springframework.hateoas.core.DummyInvocationUtils; +import org.springframework.objenesis.ObjenesisStd; +import org.springframework.objenesis.instantiator.ObjectInstantiator; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link HateoasObjenesisCacheDisabler}. + * + * @author Andy Wilkinson + */ +public class HateoasObjenesisCacheDisablerTests { + + private ObjenesisStd objenesis; + + @Before + @After + public void resetCacheField() { + this.objenesis = (ObjenesisStd) ReflectionTestUtils + .getField(DummyInvocationUtils.class, "OBJENESIS"); + ReflectionTestUtils.setField(this.objenesis, "cache", + new ConcurrentHashMap>()); + } + + @Test + public void cacheIsEnabledByDefault() { + assertThat(this.objenesis.getInstantiatorOf(TestObject.class)) + .isSameAs(this.objenesis.getInstantiatorOf(TestObject.class)); + } + + @Test + public void cacheIsDisabled() { + new HateoasObjenesisCacheDisabler().doDisableCaching(); + assertThat(this.objenesis.getInstantiatorOf(TestObject.class)) + .isNotSameAs(this.objenesis.getInstantiatorOf(TestObject.class)); + } + + private static class TestObject { + + } + +}