From 897017dca58db96670210bc6ff742fb9ae5d933e Mon Sep 17 00:00:00 2001 From: Dmitry Katsubo Date: Wed, 15 Feb 2012 16:31:30 +0100 Subject: [PATCH 1/7] Initial implementation of JacksonObjectMapperBeanFactory (SPR-9125). --- .../JacksonObjectMapperBeanFactory.java | 201 ++++++++++++++++++ .../JacksonObjectMapperBeanFactoryTest.java | 144 +++++++++++++ 2 files changed, 345 insertions(+) create mode 100644 spring-web/src/main/java/org/springframework/web/context/support/JacksonObjectMapperBeanFactory.java create mode 100644 spring-web/src/test/java/org/springframework/web/context/support/JacksonObjectMapperBeanFactoryTest.java diff --git a/spring-web/src/main/java/org/springframework/web/context/support/JacksonObjectMapperBeanFactory.java b/spring-web/src/main/java/org/springframework/web/context/support/JacksonObjectMapperBeanFactory.java new file mode 100644 index 000000000000..d4d9cf09d86d --- /dev/null +++ b/spring-web/src/main/java/org/springframework/web/context/support/JacksonObjectMapperBeanFactory.java @@ -0,0 +1,201 @@ +/* + * Copyright 2002-2007 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.web.context.support; + +import java.util.HashMap; +import java.util.Map; + +import org.codehaus.jackson.JsonGenerator; +import org.codehaus.jackson.JsonParser; +import org.codehaus.jackson.map.AnnotationIntrospector; +import org.codehaus.jackson.map.DeserializationConfig; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.map.SerializationConfig; +import org.springframework.beans.FatalBeanException; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; + +/** + * The bean configurator for Jackson {@link ObjectMapper}. Allows to enable/disable certain features for Jackson mapper. + * Examples of usage: + * + *
+ * <mvc:annotation-driven>
+ * 	<mvc:message-converters>
+ * 		<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
+ * 			<property name="objectMapper">
+ * 				<bean class="org.springframework.web.context.support.JacksonObjectMapperBeanFactory"
+ * 					p:autoDetectFields="false"
+ * 					p:autoDetectGettersSetters="false"
+ * 					p:annotationIntrospector-ref="jaxbAnnotationIntrospector" />
+ * 			</property>
+ * 		</bean>
+ * 	</mvc:message-converters>
+ * </mvc:annotation-driven>
+ * 
+ * + *
+ * <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
+ * 	<property name="objectMapper">
+ * 		<bean class="org.springframework.web.context.support.JacksonObjectMapperBeanFactory">
+ * 			<property name="featuresToEnable">
+ * 				<array>
+ * 					<util:constant static-field="org.codehaus.jackson.map.SerializationConfig$Feature.AUTO_DETECT_FIELDS"/>
+ * 					<util:constant static-field="org.codehaus.jackson.map.SerializationConfig$Feature.AUTO_DETECT_GETTERS"/>
+ * 				</array>
+ * 			</property>
+ * 			<property name="featuresToDisable">
+ * 				<array>
+ * 					<util:constant static-field="org.codehaus.jackson.map.SerializationConfig$Feature.AUTO_DETECT_FIELDS"/>
+ * 					<util:constant static-field="org.codehaus.jackson.map.SerializationConfig$Feature.AUTO_DETECT_GETTERS"/>
+ * 				</array>
+ * 			</property>
+ * 		</bean>
+ * 	</property>
+ * </bean>
+ * 
+ * + * @author Dmitry Katsubo + */ +public class JacksonObjectMapperBeanFactory implements FactoryBean, InitializingBean { + + private ObjectMapper objectMapper; + + private Map features = new HashMap(); + + private AnnotationIntrospector annotationIntrospector; + + /** + * @see org.springframework.beans.factory.FactoryBean#getObject() + */ + public ObjectMapper getObject() { + return objectMapper; + } + + /** + * @see org.springframework.beans.factory.FactoryBean#getObjectType() + */ + public Class getObjectType() { + return ObjectMapper.class; + } + + /** + * @see org.springframework.beans.factory.FactoryBean#isSingleton() + */ + public boolean isSingleton() { + return true; + } + + public void setObjectMapper(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + /** + * Define annotationIntrospector for both serializer and deserializer. + */ + public void setAnnotationIntrospector(AnnotationIntrospector annotationIntrospector) { + this.annotationIntrospector = annotationIntrospector; + } + + /** + * Generic setter for features to be enabled. + */ + public void setFeaturesToEnable(Object[] featuresToEnable) { + if (featuresToEnable == null) { + throw new FatalBeanException("featuresToEnable property should not be null"); + } + + for (Object feature : featuresToEnable) { + features.put(feature, Boolean.TRUE); + } + } + + /** + * Generic setter for features to be disabled. + */ + public void setFeaturesToDisable(Object[] featuresToDisable) { + if (featuresToDisable == null) { + throw new FatalBeanException("featuresToDisable property should not be null"); + } + + for (Object feature : featuresToDisable) { + features.put(feature, Boolean.FALSE); + } + } + + /** + * Shortcut for AUTO_DETECT_FIELDS option. + */ + public void setAutoDetectFields(boolean autoDetectFields) { + features.put(DeserializationConfig.Feature.AUTO_DETECT_FIELDS, Boolean.valueOf(autoDetectFields)); + features.put(SerializationConfig.Feature.AUTO_DETECT_FIELDS, Boolean.valueOf(autoDetectFields)); + } + + /** + * Shortcut for AUTO_DETECT_GETTERS / AUTO_DETECT_SETTERS option. + */ + public void setAutoDetectGettersSetters(boolean autoDetectGettersSetters) { + features.put(DeserializationConfig.Feature.AUTO_DETECT_SETTERS, Boolean.valueOf(autoDetectGettersSetters)); + features.put(SerializationConfig.Feature.AUTO_DETECT_GETTERS, Boolean.valueOf(autoDetectGettersSetters)); + } + + /** + * Shortcut for FAIL_ON_EMPTY_BEANS option. + */ + public void setFailOnEmptyBeans(boolean failOnEmptyBeans) { + features.put(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, Boolean.valueOf(failOnEmptyBeans)); + } + + /** + * Shortcut for INDENT_OUTPUT option. + */ + public void setIndentOutput(boolean indentOutput) { + features.put(SerializationConfig.Feature.INDENT_OUTPUT, Boolean.valueOf(indentOutput)); + } + + /** + * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() + */ + public void afterPropertiesSet() throws FatalBeanException { + if (objectMapper == null) { + objectMapper = new ObjectMapper(); + } + + if (annotationIntrospector != null) { + objectMapper.getSerializationConfig().setAnnotationIntrospector(annotationIntrospector); + objectMapper.getDeserializationConfig().setAnnotationIntrospector(annotationIntrospector); + } + + for (Map.Entry entry : features.entrySet()) { + setFeatureEnabled(entry.getKey(), entry.getValue().booleanValue()); + } + } + + private void setFeatureEnabled(Object feature, boolean enabled) { + if (feature instanceof DeserializationConfig.Feature) { + objectMapper.configure((DeserializationConfig.Feature) feature, enabled); + } else if (feature instanceof SerializationConfig.Feature) { + objectMapper.configure((SerializationConfig.Feature) feature, enabled); + } else if (feature instanceof JsonParser.Feature) { + objectMapper.configure((JsonParser.Feature) feature, enabled); + } else if (feature instanceof JsonGenerator.Feature) { + objectMapper.configure((JsonGenerator.Feature) feature, enabled); + } else { + throw new FatalBeanException("Unknown feature class " + feature.getClass().getName()); + } + } +} diff --git a/spring-web/src/test/java/org/springframework/web/context/support/JacksonObjectMapperBeanFactoryTest.java b/spring-web/src/test/java/org/springframework/web/context/support/JacksonObjectMapperBeanFactoryTest.java new file mode 100644 index 000000000000..1a93c636134e --- /dev/null +++ b/spring-web/src/test/java/org/springframework/web/context/support/JacksonObjectMapperBeanFactoryTest.java @@ -0,0 +1,144 @@ +/* + * Copyright 2002-2009 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.web.context.support; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import org.codehaus.jackson.JsonGenerator; +import org.codehaus.jackson.JsonParser; +import org.codehaus.jackson.map.DeserializationConfig; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.map.SerializationConfig; +import org.codehaus.jackson.map.introspect.NopAnnotationIntrospector; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.FatalBeanException; + +/** + * Test cases for {@link JacksonObjectMapperBeanFactory} class. + * + * @author Dmitry Katsubo + */ +public class JacksonObjectMapperBeanFactoryTest { + + private JacksonObjectMapperBeanFactory beanFactory; + + @Before + public void setUp() { + beanFactory = new JacksonObjectMapperBeanFactory(); + } + + @Test + public void testChecks() { + try { + beanFactory.setFeaturesToEnable(null); + fail("FatalBeanException should be thrown"); + } catch (FatalBeanException e) { + assertNotNull(e); + // should be thrown + } + + beanFactory.setFeaturesToEnable(new Object[0]); + + try { + beanFactory.setFeaturesToDisable(null); + fail("FatalBeanException should be thrown"); + } catch (FatalBeanException e) { + assertNotNull(e); + // should be thrown + } + + beanFactory.setFeaturesToDisable(new Object[0]); + } + + @Test(expected = FatalBeanException.class) + public void testUnknownFeature() { + beanFactory.setFeaturesToEnable(new Object[] { Boolean.TRUE }); + beanFactory.afterPropertiesSet(); + fail("FatalBeanException should be thrown"); + } + + @Test + public void testCommonSetters() { + beanFactory.setAutoDetectFields(false); + beanFactory.setAutoDetectGettersSetters(false); + beanFactory.setFailOnEmptyBeans(false); + beanFactory.setIndentOutput(true); + + beanFactory.afterPropertiesSet(); + + ObjectMapper objectMapper = beanFactory.getObject(); + + assertFalse(objectMapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.AUTO_DETECT_FIELDS)); + assertFalse(objectMapper.getDeserializationConfig().isEnabled(DeserializationConfig.Feature.AUTO_DETECT_FIELDS)); + + assertFalse(objectMapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.AUTO_DETECT_GETTERS)); + assertFalse(objectMapper.getDeserializationConfig() + .isEnabled(DeserializationConfig.Feature.AUTO_DETECT_SETTERS)); + + assertFalse(objectMapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS)); + + assertTrue(objectMapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.INDENT_OUTPUT)); + } + + @Test + public void testSimpleFlow() { + beanFactory.afterPropertiesSet(); + assertNotNull(beanFactory.getObject()); + assertTrue(beanFactory.isSingleton()); + assertEquals(ObjectMapper.class, beanFactory.getObjectType()); + } + + @Test + public void testCompleteFlow() { + NopAnnotationIntrospector annotationIntrospector = new NopAnnotationIntrospector(); + ObjectMapper objectMapper = new ObjectMapper(); + + assertTrue(beanFactory.isSingleton()); + assertEquals(ObjectMapper.class, beanFactory.getObjectType()); + + beanFactory.setObjectMapper(objectMapper); + beanFactory.setAnnotationIntrospector(annotationIntrospector); + beanFactory.setFeaturesToEnable(new Object[] { SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, + DeserializationConfig.Feature.USE_ANNOTATIONS, JsonParser.Feature.ALLOW_SINGLE_QUOTES, + JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS }); + beanFactory.setFeaturesToDisable(new Object[] { SerializationConfig.Feature.AUTO_DETECT_GETTERS, + DeserializationConfig.Feature.AUTO_DETECT_FIELDS, JsonParser.Feature.AUTO_CLOSE_SOURCE, + JsonGenerator.Feature.QUOTE_FIELD_NAMES }); + + beanFactory.afterPropertiesSet(); + + assertTrue(objectMapper == beanFactory.getObject()); + + assertTrue(annotationIntrospector == objectMapper.getSerializationConfig().getAnnotationIntrospector()); + assertTrue(annotationIntrospector == objectMapper.getDeserializationConfig().getAnnotationIntrospector()); + + assertTrue(objectMapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS)); + assertTrue(objectMapper.getDeserializationConfig().isEnabled(DeserializationConfig.Feature.USE_ANNOTATIONS)); + assertTrue(objectMapper.getJsonFactory().isEnabled(JsonParser.Feature.ALLOW_SINGLE_QUOTES)); + assertTrue(objectMapper.getJsonFactory().isEnabled(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS)); + + assertFalse(objectMapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.AUTO_DETECT_GETTERS)); + assertFalse(objectMapper.getDeserializationConfig().isEnabled(DeserializationConfig.Feature.AUTO_DETECT_FIELDS)); + assertFalse(objectMapper.getJsonFactory().isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE)); + assertFalse(objectMapper.getJsonFactory().isEnabled(JsonGenerator.Feature.QUOTE_FIELD_NAMES)); + } +} From e40d1e68fac08a3b3bb1ac7ea129a3959f19407b Mon Sep 17 00:00:00 2001 From: Dmitry Katsubo Date: Fri, 17 Feb 2012 16:38:49 +0100 Subject: [PATCH 2/7] Initial implementation of bean factory to simplify the creation of EasyMock beans (SPR-9130). --- .../context/support/EasyMockBeanFactory.java | 175 ++++++++++++++++++ .../support/EasyMockBeanFactoryTest.java | 117 ++++++++++++ 2 files changed, 292 insertions(+) create mode 100644 spring-test/src/main/java/org/springframework/test/context/support/EasyMockBeanFactory.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/support/EasyMockBeanFactoryTest.java diff --git a/spring-test/src/main/java/org/springframework/test/context/support/EasyMockBeanFactory.java b/spring-test/src/main/java/org/springframework/test/context/support/EasyMockBeanFactory.java new file mode 100644 index 000000000000..e005f8a4d4b5 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/context/support/EasyMockBeanFactory.java @@ -0,0 +1,175 @@ +/* + * Copyright 2002-2009 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.test.context.support; + +import org.easymock.EasyMock; +import org.easymock.IMocksControl; +import org.springframework.beans.BeansException; +import org.springframework.beans.FatalBeanException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.util.Assert; + +/** + * The bean factory for EasyMock mocks. Assumes that spring context has a bean with name "mocksControl" which will be + * used to create mocks; otherwise is customizable via property or factory will fallback to creating a separate control + * per mock. Example of usage: + * + *
+ * <bean name="myService" class="org.springframework.test.context.support.EasyMockBeanFactory"
+ * 	p:mockInterface="org.company.api.MyService"
+ * />
+ * 
+ * <bean name="myDao" class="org.springframework.test.context.support.EasyMockBeanFactory"
+ * 	p:mockInterface="org.company.api.Dao"
+ * 	p:mocksControl-ref="mocksCtrl"
+ * />
+ * 
+ * + * Then test class may look like: + * + *
+ * import static org.junit.Assert.assertEquals;
+ * import static org.junit.Assert.assertNull;
+ * 
+ * import javax.annotation.Resource;
+ * 
+ * import org.easymock.EasyMock;
+ * import org.easymock.IMocksControl;
+ * import org.junit.Before;
+ * import org.junit.Test;
+ * import org.junit.runner.RunWith;
+ * import org.springframework.beans.factory.annotation.Autowired;
+ * import org.springframework.test.context.ContextConfiguration;
+ * import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+ * 
+ * @RunWith(SpringJUnit4ClassRunner.class)
+ * @ContextConfiguration("classpath:/org/company/test-context.xml")
+ * public class MyIntegrationTest {
+ * 	@Autowired
+ * 	private MyService myService;
+ * 
+ * 	// This is a mock:
+ * 	@Autowired
+ * 	private Dao dao;
+ * 
+ * 	@Resource
+ * 	protected IMocksControl mocksControl;
+ * 
+ * 	@Before
+ * 	public void reset() {
+ * 		mocksControl.reset();
+ * 	}
+ * 
+ * 	@Test
+ * 	public void testGetPublicationNoSearchHit() {
+ * 		EasyMock.expect(dao.getItemByKey(999)).andReturn(null);
+ * 
+ * 		mocksControl.replay();
+ * 
+ * 		assertNull(myService.getOrCreateItem());
+ * 
+ * 		mocksControl.verify();
+ * 	}
+ * }
+ * 
+ * + * @author Dmitry Katsubo + */ +public class EasyMockBeanFactory implements FactoryBean, InitializingBean, BeanFactoryAware { + + private IMocksControl mocksControl; + + private Class mockClass; + + private T mock; + + private BeanFactory beanFactory; + + static final String DEFAULT_MOCKS_CONTROL_BEAN_NAME = "mocksControl"; + + /** + * @see org.springframework.beans.factory.FactoryBean#getObject() + */ + public T getObject() { + return mock; + } + + /** + * @see org.springframework.beans.factory.FactoryBean#getObjectType() + */ + public Class getObjectType() { + return mockClass; + } + + /** + * @see org.springframework.beans.factory.FactoryBean#isSingleton() + */ + public boolean isSingleton() { + return true; + } + + /** + * Sets {@link IMocksControl} on this factory. This is necessary when you want to share the same + * {@link IMocksControl} among all mocks. + */ + public void setMocksControl(IMocksControl mocksControl) { + this.mocksControl = mocksControl; + } + + /** + * Set the class to be mocked. + */ + public void setMockClass(Class mockClass) { + this.mockClass = mockClass; + } + + /** + * The same as {@link #setMockClass(Class)} but provided for better expressibility. + */ + public void setMockInterface(Class mockClass) { + this.mockClass = mockClass; + } + + /** + * @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory) + */ + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } + + /** + * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() + */ + public void afterPropertiesSet() throws FatalBeanException { + Assert.notNull(mockClass, "The class/interface to mock must be defined"); + + if (mocksControl == null) { + // Check the presence of a bean with default name: + mocksControl = beanFactory.getBean(DEFAULT_MOCKS_CONTROL_BEAN_NAME, IMocksControl.class); + + if (mocksControl == null) { + // Fallback to separate control per mock: + mocksControl = EasyMock.createControl(); + } + } + + mock = mocksControl.createMock(mockClass); + } +} diff --git a/spring-test/src/test/java/org/springframework/test/context/support/EasyMockBeanFactoryTest.java b/spring-test/src/test/java/org/springframework/test/context/support/EasyMockBeanFactoryTest.java new file mode 100644 index 000000000000..5f8dac0eea8d --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/support/EasyMockBeanFactoryTest.java @@ -0,0 +1,117 @@ +/* + * Copyright 2002-2009 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.test.context.support; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.Closeable; +import java.lang.reflect.Proxy; + +import org.easymock.EasyMock; +import org.easymock.IMocksControl; +import org.easymock.internal.ObjectMethodsFilter; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.BeanFactory; + +/** + * Test cases for {@link EasyMockBeanFactory} class. + * + * @author Dmitry Katsubo + */ +public class EasyMockBeanFactoryTest { + + private EasyMockBeanFactory easyMockBeanFactory; + + private BeanFactory beanFactory; + + @Before + public void setUp() { + easyMockBeanFactory = new EasyMockBeanFactory(); + + beanFactory = EasyMock.createStrictMock(BeanFactory.class); + } + + @Test(expected = IllegalArgumentException.class) + public void testAssertions() { + easyMockBeanFactory.afterPropertiesSet(); + } + + @Test + public void testFlowWithCustomMocksControl() { + IMocksControl control = EasyMock.createControl(); + + easyMockBeanFactory.setMockInterface(Closeable.class); + easyMockBeanFactory.setMocksControl(control); + easyMockBeanFactory.afterPropertiesSet(); + + assertTrue(easyMockBeanFactory.isSingleton()); + assertEquals(Closeable.class, easyMockBeanFactory.getObjectType()); + + // Ignore compiler warning, as after type erasure the return type of getObject() is java.lang.Object. + assertTrue(easyMockBeanFactory.getObject() instanceof Closeable); + + assertTrue(control == ((ObjectMethodsFilter) Proxy.getInvocationHandler(easyMockBeanFactory.getObject())) + .getDelegate().getControl()); + } + + @Test + public void testFlowWithMocksControlInContext() { + IMocksControl control = EasyMock.createControl(); + + EasyMock.expect(beanFactory.getBean(EasyMockBeanFactory.DEFAULT_MOCKS_CONTROL_BEAN_NAME, IMocksControl.class)) + .andReturn(control); + + EasyMock.replay(beanFactory); + + easyMockBeanFactory.setMockInterface(Closeable.class); + easyMockBeanFactory.setBeanFactory(beanFactory); + easyMockBeanFactory.afterPropertiesSet(); + + assertTrue(easyMockBeanFactory.isSingleton()); + assertEquals(Closeable.class, easyMockBeanFactory.getObjectType()); + + // Ignore compiler warning, as after type erasure the return type of getObject() is java.lang.Object. + assertTrue(easyMockBeanFactory.getObject() instanceof Closeable); + + assertTrue(control == ((ObjectMethodsFilter) Proxy.getInvocationHandler(easyMockBeanFactory.getObject())) + .getDelegate().getControl()); + + EasyMock.verify(beanFactory); + } + + @Test + public void testFlowWithNoMocksControlInContext() { + EasyMock.expect(beanFactory.getBean(EasyMockBeanFactory.DEFAULT_MOCKS_CONTROL_BEAN_NAME, IMocksControl.class)) + .andReturn(null); + + EasyMock.replay(beanFactory); + + easyMockBeanFactory.setMockClass(Closeable.class); + easyMockBeanFactory.setBeanFactory(beanFactory); + easyMockBeanFactory.afterPropertiesSet(); + + assertTrue(easyMockBeanFactory.isSingleton()); + assertEquals(Closeable.class, easyMockBeanFactory.getObjectType()); + + // Ignore compiler warning, as after type erasure the return type of getObject() is java.lang.Object. + assertTrue(easyMockBeanFactory.getObject() instanceof Closeable); + + EasyMock.verify(beanFactory); + } +} From 712af3990403ab762053f7be34990522e34a4408 Mon Sep 17 00:00:00 2001 From: Dmitry Katsubo Date: Wed, 15 Feb 2012 16:31:30 +0100 Subject: [PATCH 3/7] Initial implementation of JacksonObjectMapperBeanFactory (SPR-9125). --- .../JacksonObjectMapperBeanFactory.java | 201 ++++++++++++++++++ .../JacksonObjectMapperBeanFactoryTest.java | 144 +++++++++++++ 2 files changed, 345 insertions(+) create mode 100644 spring-web/src/main/java/org/springframework/web/context/support/JacksonObjectMapperBeanFactory.java create mode 100644 spring-web/src/test/java/org/springframework/web/context/support/JacksonObjectMapperBeanFactoryTest.java diff --git a/spring-web/src/main/java/org/springframework/web/context/support/JacksonObjectMapperBeanFactory.java b/spring-web/src/main/java/org/springframework/web/context/support/JacksonObjectMapperBeanFactory.java new file mode 100644 index 000000000000..d4d9cf09d86d --- /dev/null +++ b/spring-web/src/main/java/org/springframework/web/context/support/JacksonObjectMapperBeanFactory.java @@ -0,0 +1,201 @@ +/* + * Copyright 2002-2007 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.web.context.support; + +import java.util.HashMap; +import java.util.Map; + +import org.codehaus.jackson.JsonGenerator; +import org.codehaus.jackson.JsonParser; +import org.codehaus.jackson.map.AnnotationIntrospector; +import org.codehaus.jackson.map.DeserializationConfig; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.map.SerializationConfig; +import org.springframework.beans.FatalBeanException; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; + +/** + * The bean configurator for Jackson {@link ObjectMapper}. Allows to enable/disable certain features for Jackson mapper. + * Examples of usage: + * + *
+ * <mvc:annotation-driven>
+ * 	<mvc:message-converters>
+ * 		<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
+ * 			<property name="objectMapper">
+ * 				<bean class="org.springframework.web.context.support.JacksonObjectMapperBeanFactory"
+ * 					p:autoDetectFields="false"
+ * 					p:autoDetectGettersSetters="false"
+ * 					p:annotationIntrospector-ref="jaxbAnnotationIntrospector" />
+ * 			</property>
+ * 		</bean>
+ * 	</mvc:message-converters>
+ * </mvc:annotation-driven>
+ * 
+ * + *
+ * <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
+ * 	<property name="objectMapper">
+ * 		<bean class="org.springframework.web.context.support.JacksonObjectMapperBeanFactory">
+ * 			<property name="featuresToEnable">
+ * 				<array>
+ * 					<util:constant static-field="org.codehaus.jackson.map.SerializationConfig$Feature.AUTO_DETECT_FIELDS"/>
+ * 					<util:constant static-field="org.codehaus.jackson.map.SerializationConfig$Feature.AUTO_DETECT_GETTERS"/>
+ * 				</array>
+ * 			</property>
+ * 			<property name="featuresToDisable">
+ * 				<array>
+ * 					<util:constant static-field="org.codehaus.jackson.map.SerializationConfig$Feature.AUTO_DETECT_FIELDS"/>
+ * 					<util:constant static-field="org.codehaus.jackson.map.SerializationConfig$Feature.AUTO_DETECT_GETTERS"/>
+ * 				</array>
+ * 			</property>
+ * 		</bean>
+ * 	</property>
+ * </bean>
+ * 
+ * + * @author Dmitry Katsubo + */ +public class JacksonObjectMapperBeanFactory implements FactoryBean, InitializingBean { + + private ObjectMapper objectMapper; + + private Map features = new HashMap(); + + private AnnotationIntrospector annotationIntrospector; + + /** + * @see org.springframework.beans.factory.FactoryBean#getObject() + */ + public ObjectMapper getObject() { + return objectMapper; + } + + /** + * @see org.springframework.beans.factory.FactoryBean#getObjectType() + */ + public Class getObjectType() { + return ObjectMapper.class; + } + + /** + * @see org.springframework.beans.factory.FactoryBean#isSingleton() + */ + public boolean isSingleton() { + return true; + } + + public void setObjectMapper(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + /** + * Define annotationIntrospector for both serializer and deserializer. + */ + public void setAnnotationIntrospector(AnnotationIntrospector annotationIntrospector) { + this.annotationIntrospector = annotationIntrospector; + } + + /** + * Generic setter for features to be enabled. + */ + public void setFeaturesToEnable(Object[] featuresToEnable) { + if (featuresToEnable == null) { + throw new FatalBeanException("featuresToEnable property should not be null"); + } + + for (Object feature : featuresToEnable) { + features.put(feature, Boolean.TRUE); + } + } + + /** + * Generic setter for features to be disabled. + */ + public void setFeaturesToDisable(Object[] featuresToDisable) { + if (featuresToDisable == null) { + throw new FatalBeanException("featuresToDisable property should not be null"); + } + + for (Object feature : featuresToDisable) { + features.put(feature, Boolean.FALSE); + } + } + + /** + * Shortcut for AUTO_DETECT_FIELDS option. + */ + public void setAutoDetectFields(boolean autoDetectFields) { + features.put(DeserializationConfig.Feature.AUTO_DETECT_FIELDS, Boolean.valueOf(autoDetectFields)); + features.put(SerializationConfig.Feature.AUTO_DETECT_FIELDS, Boolean.valueOf(autoDetectFields)); + } + + /** + * Shortcut for AUTO_DETECT_GETTERS / AUTO_DETECT_SETTERS option. + */ + public void setAutoDetectGettersSetters(boolean autoDetectGettersSetters) { + features.put(DeserializationConfig.Feature.AUTO_DETECT_SETTERS, Boolean.valueOf(autoDetectGettersSetters)); + features.put(SerializationConfig.Feature.AUTO_DETECT_GETTERS, Boolean.valueOf(autoDetectGettersSetters)); + } + + /** + * Shortcut for FAIL_ON_EMPTY_BEANS option. + */ + public void setFailOnEmptyBeans(boolean failOnEmptyBeans) { + features.put(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, Boolean.valueOf(failOnEmptyBeans)); + } + + /** + * Shortcut for INDENT_OUTPUT option. + */ + public void setIndentOutput(boolean indentOutput) { + features.put(SerializationConfig.Feature.INDENT_OUTPUT, Boolean.valueOf(indentOutput)); + } + + /** + * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() + */ + public void afterPropertiesSet() throws FatalBeanException { + if (objectMapper == null) { + objectMapper = new ObjectMapper(); + } + + if (annotationIntrospector != null) { + objectMapper.getSerializationConfig().setAnnotationIntrospector(annotationIntrospector); + objectMapper.getDeserializationConfig().setAnnotationIntrospector(annotationIntrospector); + } + + for (Map.Entry entry : features.entrySet()) { + setFeatureEnabled(entry.getKey(), entry.getValue().booleanValue()); + } + } + + private void setFeatureEnabled(Object feature, boolean enabled) { + if (feature instanceof DeserializationConfig.Feature) { + objectMapper.configure((DeserializationConfig.Feature) feature, enabled); + } else if (feature instanceof SerializationConfig.Feature) { + objectMapper.configure((SerializationConfig.Feature) feature, enabled); + } else if (feature instanceof JsonParser.Feature) { + objectMapper.configure((JsonParser.Feature) feature, enabled); + } else if (feature instanceof JsonGenerator.Feature) { + objectMapper.configure((JsonGenerator.Feature) feature, enabled); + } else { + throw new FatalBeanException("Unknown feature class " + feature.getClass().getName()); + } + } +} diff --git a/spring-web/src/test/java/org/springframework/web/context/support/JacksonObjectMapperBeanFactoryTest.java b/spring-web/src/test/java/org/springframework/web/context/support/JacksonObjectMapperBeanFactoryTest.java new file mode 100644 index 000000000000..1a93c636134e --- /dev/null +++ b/spring-web/src/test/java/org/springframework/web/context/support/JacksonObjectMapperBeanFactoryTest.java @@ -0,0 +1,144 @@ +/* + * Copyright 2002-2009 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.web.context.support; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import org.codehaus.jackson.JsonGenerator; +import org.codehaus.jackson.JsonParser; +import org.codehaus.jackson.map.DeserializationConfig; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.map.SerializationConfig; +import org.codehaus.jackson.map.introspect.NopAnnotationIntrospector; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.FatalBeanException; + +/** + * Test cases for {@link JacksonObjectMapperBeanFactory} class. + * + * @author Dmitry Katsubo + */ +public class JacksonObjectMapperBeanFactoryTest { + + private JacksonObjectMapperBeanFactory beanFactory; + + @Before + public void setUp() { + beanFactory = new JacksonObjectMapperBeanFactory(); + } + + @Test + public void testChecks() { + try { + beanFactory.setFeaturesToEnable(null); + fail("FatalBeanException should be thrown"); + } catch (FatalBeanException e) { + assertNotNull(e); + // should be thrown + } + + beanFactory.setFeaturesToEnable(new Object[0]); + + try { + beanFactory.setFeaturesToDisable(null); + fail("FatalBeanException should be thrown"); + } catch (FatalBeanException e) { + assertNotNull(e); + // should be thrown + } + + beanFactory.setFeaturesToDisable(new Object[0]); + } + + @Test(expected = FatalBeanException.class) + public void testUnknownFeature() { + beanFactory.setFeaturesToEnable(new Object[] { Boolean.TRUE }); + beanFactory.afterPropertiesSet(); + fail("FatalBeanException should be thrown"); + } + + @Test + public void testCommonSetters() { + beanFactory.setAutoDetectFields(false); + beanFactory.setAutoDetectGettersSetters(false); + beanFactory.setFailOnEmptyBeans(false); + beanFactory.setIndentOutput(true); + + beanFactory.afterPropertiesSet(); + + ObjectMapper objectMapper = beanFactory.getObject(); + + assertFalse(objectMapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.AUTO_DETECT_FIELDS)); + assertFalse(objectMapper.getDeserializationConfig().isEnabled(DeserializationConfig.Feature.AUTO_DETECT_FIELDS)); + + assertFalse(objectMapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.AUTO_DETECT_GETTERS)); + assertFalse(objectMapper.getDeserializationConfig() + .isEnabled(DeserializationConfig.Feature.AUTO_DETECT_SETTERS)); + + assertFalse(objectMapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS)); + + assertTrue(objectMapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.INDENT_OUTPUT)); + } + + @Test + public void testSimpleFlow() { + beanFactory.afterPropertiesSet(); + assertNotNull(beanFactory.getObject()); + assertTrue(beanFactory.isSingleton()); + assertEquals(ObjectMapper.class, beanFactory.getObjectType()); + } + + @Test + public void testCompleteFlow() { + NopAnnotationIntrospector annotationIntrospector = new NopAnnotationIntrospector(); + ObjectMapper objectMapper = new ObjectMapper(); + + assertTrue(beanFactory.isSingleton()); + assertEquals(ObjectMapper.class, beanFactory.getObjectType()); + + beanFactory.setObjectMapper(objectMapper); + beanFactory.setAnnotationIntrospector(annotationIntrospector); + beanFactory.setFeaturesToEnable(new Object[] { SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, + DeserializationConfig.Feature.USE_ANNOTATIONS, JsonParser.Feature.ALLOW_SINGLE_QUOTES, + JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS }); + beanFactory.setFeaturesToDisable(new Object[] { SerializationConfig.Feature.AUTO_DETECT_GETTERS, + DeserializationConfig.Feature.AUTO_DETECT_FIELDS, JsonParser.Feature.AUTO_CLOSE_SOURCE, + JsonGenerator.Feature.QUOTE_FIELD_NAMES }); + + beanFactory.afterPropertiesSet(); + + assertTrue(objectMapper == beanFactory.getObject()); + + assertTrue(annotationIntrospector == objectMapper.getSerializationConfig().getAnnotationIntrospector()); + assertTrue(annotationIntrospector == objectMapper.getDeserializationConfig().getAnnotationIntrospector()); + + assertTrue(objectMapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS)); + assertTrue(objectMapper.getDeserializationConfig().isEnabled(DeserializationConfig.Feature.USE_ANNOTATIONS)); + assertTrue(objectMapper.getJsonFactory().isEnabled(JsonParser.Feature.ALLOW_SINGLE_QUOTES)); + assertTrue(objectMapper.getJsonFactory().isEnabled(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS)); + + assertFalse(objectMapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.AUTO_DETECT_GETTERS)); + assertFalse(objectMapper.getDeserializationConfig().isEnabled(DeserializationConfig.Feature.AUTO_DETECT_FIELDS)); + assertFalse(objectMapper.getJsonFactory().isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE)); + assertFalse(objectMapper.getJsonFactory().isEnabled(JsonGenerator.Feature.QUOTE_FIELD_NAMES)); + } +} From 0336c4335b5e0d8c63309608826c57caa03b3269 Mon Sep 17 00:00:00 2001 From: Dmitry Katsubo Date: Fri, 17 Feb 2012 16:38:49 +0100 Subject: [PATCH 4/7] Initial implementation of bean factory to simplify the creation of EasyMock beans (SPR-9130). --- .../context/support/EasyMockBeanFactory.java | 175 ++++++++++++++++++ .../support/EasyMockBeanFactoryTest.java | 117 ++++++++++++ 2 files changed, 292 insertions(+) create mode 100644 spring-test/src/main/java/org/springframework/test/context/support/EasyMockBeanFactory.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/support/EasyMockBeanFactoryTest.java diff --git a/spring-test/src/main/java/org/springframework/test/context/support/EasyMockBeanFactory.java b/spring-test/src/main/java/org/springframework/test/context/support/EasyMockBeanFactory.java new file mode 100644 index 000000000000..e005f8a4d4b5 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/context/support/EasyMockBeanFactory.java @@ -0,0 +1,175 @@ +/* + * Copyright 2002-2009 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.test.context.support; + +import org.easymock.EasyMock; +import org.easymock.IMocksControl; +import org.springframework.beans.BeansException; +import org.springframework.beans.FatalBeanException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.util.Assert; + +/** + * The bean factory for EasyMock mocks. Assumes that spring context has a bean with name "mocksControl" which will be + * used to create mocks; otherwise is customizable via property or factory will fallback to creating a separate control + * per mock. Example of usage: + * + *
+ * <bean name="myService" class="org.springframework.test.context.support.EasyMockBeanFactory"
+ * 	p:mockInterface="org.company.api.MyService"
+ * />
+ * 
+ * <bean name="myDao" class="org.springframework.test.context.support.EasyMockBeanFactory"
+ * 	p:mockInterface="org.company.api.Dao"
+ * 	p:mocksControl-ref="mocksCtrl"
+ * />
+ * 
+ * + * Then test class may look like: + * + *
+ * import static org.junit.Assert.assertEquals;
+ * import static org.junit.Assert.assertNull;
+ * 
+ * import javax.annotation.Resource;
+ * 
+ * import org.easymock.EasyMock;
+ * import org.easymock.IMocksControl;
+ * import org.junit.Before;
+ * import org.junit.Test;
+ * import org.junit.runner.RunWith;
+ * import org.springframework.beans.factory.annotation.Autowired;
+ * import org.springframework.test.context.ContextConfiguration;
+ * import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+ * 
+ * @RunWith(SpringJUnit4ClassRunner.class)
+ * @ContextConfiguration("classpath:/org/company/test-context.xml")
+ * public class MyIntegrationTest {
+ * 	@Autowired
+ * 	private MyService myService;
+ * 
+ * 	// This is a mock:
+ * 	@Autowired
+ * 	private Dao dao;
+ * 
+ * 	@Resource
+ * 	protected IMocksControl mocksControl;
+ * 
+ * 	@Before
+ * 	public void reset() {
+ * 		mocksControl.reset();
+ * 	}
+ * 
+ * 	@Test
+ * 	public void testGetPublicationNoSearchHit() {
+ * 		EasyMock.expect(dao.getItemByKey(999)).andReturn(null);
+ * 
+ * 		mocksControl.replay();
+ * 
+ * 		assertNull(myService.getOrCreateItem());
+ * 
+ * 		mocksControl.verify();
+ * 	}
+ * }
+ * 
+ * + * @author Dmitry Katsubo + */ +public class EasyMockBeanFactory implements FactoryBean, InitializingBean, BeanFactoryAware { + + private IMocksControl mocksControl; + + private Class mockClass; + + private T mock; + + private BeanFactory beanFactory; + + static final String DEFAULT_MOCKS_CONTROL_BEAN_NAME = "mocksControl"; + + /** + * @see org.springframework.beans.factory.FactoryBean#getObject() + */ + public T getObject() { + return mock; + } + + /** + * @see org.springframework.beans.factory.FactoryBean#getObjectType() + */ + public Class getObjectType() { + return mockClass; + } + + /** + * @see org.springframework.beans.factory.FactoryBean#isSingleton() + */ + public boolean isSingleton() { + return true; + } + + /** + * Sets {@link IMocksControl} on this factory. This is necessary when you want to share the same + * {@link IMocksControl} among all mocks. + */ + public void setMocksControl(IMocksControl mocksControl) { + this.mocksControl = mocksControl; + } + + /** + * Set the class to be mocked. + */ + public void setMockClass(Class mockClass) { + this.mockClass = mockClass; + } + + /** + * The same as {@link #setMockClass(Class)} but provided for better expressibility. + */ + public void setMockInterface(Class mockClass) { + this.mockClass = mockClass; + } + + /** + * @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory) + */ + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } + + /** + * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() + */ + public void afterPropertiesSet() throws FatalBeanException { + Assert.notNull(mockClass, "The class/interface to mock must be defined"); + + if (mocksControl == null) { + // Check the presence of a bean with default name: + mocksControl = beanFactory.getBean(DEFAULT_MOCKS_CONTROL_BEAN_NAME, IMocksControl.class); + + if (mocksControl == null) { + // Fallback to separate control per mock: + mocksControl = EasyMock.createControl(); + } + } + + mock = mocksControl.createMock(mockClass); + } +} diff --git a/spring-test/src/test/java/org/springframework/test/context/support/EasyMockBeanFactoryTest.java b/spring-test/src/test/java/org/springframework/test/context/support/EasyMockBeanFactoryTest.java new file mode 100644 index 000000000000..5f8dac0eea8d --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/support/EasyMockBeanFactoryTest.java @@ -0,0 +1,117 @@ +/* + * Copyright 2002-2009 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.test.context.support; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.Closeable; +import java.lang.reflect.Proxy; + +import org.easymock.EasyMock; +import org.easymock.IMocksControl; +import org.easymock.internal.ObjectMethodsFilter; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.BeanFactory; + +/** + * Test cases for {@link EasyMockBeanFactory} class. + * + * @author Dmitry Katsubo + */ +public class EasyMockBeanFactoryTest { + + private EasyMockBeanFactory easyMockBeanFactory; + + private BeanFactory beanFactory; + + @Before + public void setUp() { + easyMockBeanFactory = new EasyMockBeanFactory(); + + beanFactory = EasyMock.createStrictMock(BeanFactory.class); + } + + @Test(expected = IllegalArgumentException.class) + public void testAssertions() { + easyMockBeanFactory.afterPropertiesSet(); + } + + @Test + public void testFlowWithCustomMocksControl() { + IMocksControl control = EasyMock.createControl(); + + easyMockBeanFactory.setMockInterface(Closeable.class); + easyMockBeanFactory.setMocksControl(control); + easyMockBeanFactory.afterPropertiesSet(); + + assertTrue(easyMockBeanFactory.isSingleton()); + assertEquals(Closeable.class, easyMockBeanFactory.getObjectType()); + + // Ignore compiler warning, as after type erasure the return type of getObject() is java.lang.Object. + assertTrue(easyMockBeanFactory.getObject() instanceof Closeable); + + assertTrue(control == ((ObjectMethodsFilter) Proxy.getInvocationHandler(easyMockBeanFactory.getObject())) + .getDelegate().getControl()); + } + + @Test + public void testFlowWithMocksControlInContext() { + IMocksControl control = EasyMock.createControl(); + + EasyMock.expect(beanFactory.getBean(EasyMockBeanFactory.DEFAULT_MOCKS_CONTROL_BEAN_NAME, IMocksControl.class)) + .andReturn(control); + + EasyMock.replay(beanFactory); + + easyMockBeanFactory.setMockInterface(Closeable.class); + easyMockBeanFactory.setBeanFactory(beanFactory); + easyMockBeanFactory.afterPropertiesSet(); + + assertTrue(easyMockBeanFactory.isSingleton()); + assertEquals(Closeable.class, easyMockBeanFactory.getObjectType()); + + // Ignore compiler warning, as after type erasure the return type of getObject() is java.lang.Object. + assertTrue(easyMockBeanFactory.getObject() instanceof Closeable); + + assertTrue(control == ((ObjectMethodsFilter) Proxy.getInvocationHandler(easyMockBeanFactory.getObject())) + .getDelegate().getControl()); + + EasyMock.verify(beanFactory); + } + + @Test + public void testFlowWithNoMocksControlInContext() { + EasyMock.expect(beanFactory.getBean(EasyMockBeanFactory.DEFAULT_MOCKS_CONTROL_BEAN_NAME, IMocksControl.class)) + .andReturn(null); + + EasyMock.replay(beanFactory); + + easyMockBeanFactory.setMockClass(Closeable.class); + easyMockBeanFactory.setBeanFactory(beanFactory); + easyMockBeanFactory.afterPropertiesSet(); + + assertTrue(easyMockBeanFactory.isSingleton()); + assertEquals(Closeable.class, easyMockBeanFactory.getObjectType()); + + // Ignore compiler warning, as after type erasure the return type of getObject() is java.lang.Object. + assertTrue(easyMockBeanFactory.getObject() instanceof Closeable); + + EasyMock.verify(beanFactory); + } +} From 30a74fc90b276089f6c9093146c9d0bab2702cb7 Mon Sep 17 00:00:00 2001 From: Dmitry Katsubo Date: Tue, 5 Jun 2012 11:54:05 +0200 Subject: [PATCH 5/7] Removed classes to be re-added into separate branches (SPR-9125, SPR-9130) --- .../context/support/EasyMockBeanFactory.java | 175 --------------- .../support/EasyMockBeanFactoryTest.java | 117 ---------- .../JacksonObjectMapperBeanFactory.java | 201 ------------------ .../JacksonObjectMapperBeanFactoryTest.java | 144 ------------- 4 files changed, 637 deletions(-) delete mode 100644 spring-test/src/main/java/org/springframework/test/context/support/EasyMockBeanFactory.java delete mode 100644 spring-test/src/test/java/org/springframework/test/context/support/EasyMockBeanFactoryTest.java delete mode 100644 spring-web/src/main/java/org/springframework/web/context/support/JacksonObjectMapperBeanFactory.java delete mode 100644 spring-web/src/test/java/org/springframework/web/context/support/JacksonObjectMapperBeanFactoryTest.java diff --git a/spring-test/src/main/java/org/springframework/test/context/support/EasyMockBeanFactory.java b/spring-test/src/main/java/org/springframework/test/context/support/EasyMockBeanFactory.java deleted file mode 100644 index e005f8a4d4b5..000000000000 --- a/spring-test/src/main/java/org/springframework/test/context/support/EasyMockBeanFactory.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2002-2009 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.test.context.support; - -import org.easymock.EasyMock; -import org.easymock.IMocksControl; -import org.springframework.beans.BeansException; -import org.springframework.beans.FatalBeanException; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.util.Assert; - -/** - * The bean factory for EasyMock mocks. Assumes that spring context has a bean with name "mocksControl" which will be - * used to create mocks; otherwise is customizable via property or factory will fallback to creating a separate control - * per mock. Example of usage: - * - *
- * <bean name="myService" class="org.springframework.test.context.support.EasyMockBeanFactory"
- * 	p:mockInterface="org.company.api.MyService"
- * />
- * 
- * <bean name="myDao" class="org.springframework.test.context.support.EasyMockBeanFactory"
- * 	p:mockInterface="org.company.api.Dao"
- * 	p:mocksControl-ref="mocksCtrl"
- * />
- * 
- * - * Then test class may look like: - * - *
- * import static org.junit.Assert.assertEquals;
- * import static org.junit.Assert.assertNull;
- * 
- * import javax.annotation.Resource;
- * 
- * import org.easymock.EasyMock;
- * import org.easymock.IMocksControl;
- * import org.junit.Before;
- * import org.junit.Test;
- * import org.junit.runner.RunWith;
- * import org.springframework.beans.factory.annotation.Autowired;
- * import org.springframework.test.context.ContextConfiguration;
- * import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
- * 
- * @RunWith(SpringJUnit4ClassRunner.class)
- * @ContextConfiguration("classpath:/org/company/test-context.xml")
- * public class MyIntegrationTest {
- * 	@Autowired
- * 	private MyService myService;
- * 
- * 	// This is a mock:
- * 	@Autowired
- * 	private Dao dao;
- * 
- * 	@Resource
- * 	protected IMocksControl mocksControl;
- * 
- * 	@Before
- * 	public void reset() {
- * 		mocksControl.reset();
- * 	}
- * 
- * 	@Test
- * 	public void testGetPublicationNoSearchHit() {
- * 		EasyMock.expect(dao.getItemByKey(999)).andReturn(null);
- * 
- * 		mocksControl.replay();
- * 
- * 		assertNull(myService.getOrCreateItem());
- * 
- * 		mocksControl.verify();
- * 	}
- * }
- * 
- * - * @author Dmitry Katsubo - */ -public class EasyMockBeanFactory implements FactoryBean, InitializingBean, BeanFactoryAware { - - private IMocksControl mocksControl; - - private Class mockClass; - - private T mock; - - private BeanFactory beanFactory; - - static final String DEFAULT_MOCKS_CONTROL_BEAN_NAME = "mocksControl"; - - /** - * @see org.springframework.beans.factory.FactoryBean#getObject() - */ - public T getObject() { - return mock; - } - - /** - * @see org.springframework.beans.factory.FactoryBean#getObjectType() - */ - public Class getObjectType() { - return mockClass; - } - - /** - * @see org.springframework.beans.factory.FactoryBean#isSingleton() - */ - public boolean isSingleton() { - return true; - } - - /** - * Sets {@link IMocksControl} on this factory. This is necessary when you want to share the same - * {@link IMocksControl} among all mocks. - */ - public void setMocksControl(IMocksControl mocksControl) { - this.mocksControl = mocksControl; - } - - /** - * Set the class to be mocked. - */ - public void setMockClass(Class mockClass) { - this.mockClass = mockClass; - } - - /** - * The same as {@link #setMockClass(Class)} but provided for better expressibility. - */ - public void setMockInterface(Class mockClass) { - this.mockClass = mockClass; - } - - /** - * @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory) - */ - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.beanFactory = beanFactory; - } - - /** - * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() - */ - public void afterPropertiesSet() throws FatalBeanException { - Assert.notNull(mockClass, "The class/interface to mock must be defined"); - - if (mocksControl == null) { - // Check the presence of a bean with default name: - mocksControl = beanFactory.getBean(DEFAULT_MOCKS_CONTROL_BEAN_NAME, IMocksControl.class); - - if (mocksControl == null) { - // Fallback to separate control per mock: - mocksControl = EasyMock.createControl(); - } - } - - mock = mocksControl.createMock(mockClass); - } -} diff --git a/spring-test/src/test/java/org/springframework/test/context/support/EasyMockBeanFactoryTest.java b/spring-test/src/test/java/org/springframework/test/context/support/EasyMockBeanFactoryTest.java deleted file mode 100644 index 5f8dac0eea8d..000000000000 --- a/spring-test/src/test/java/org/springframework/test/context/support/EasyMockBeanFactoryTest.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2002-2009 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.test.context.support; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.io.Closeable; -import java.lang.reflect.Proxy; - -import org.easymock.EasyMock; -import org.easymock.IMocksControl; -import org.easymock.internal.ObjectMethodsFilter; -import org.junit.Before; -import org.junit.Test; -import org.springframework.beans.factory.BeanFactory; - -/** - * Test cases for {@link EasyMockBeanFactory} class. - * - * @author Dmitry Katsubo - */ -public class EasyMockBeanFactoryTest { - - private EasyMockBeanFactory easyMockBeanFactory; - - private BeanFactory beanFactory; - - @Before - public void setUp() { - easyMockBeanFactory = new EasyMockBeanFactory(); - - beanFactory = EasyMock.createStrictMock(BeanFactory.class); - } - - @Test(expected = IllegalArgumentException.class) - public void testAssertions() { - easyMockBeanFactory.afterPropertiesSet(); - } - - @Test - public void testFlowWithCustomMocksControl() { - IMocksControl control = EasyMock.createControl(); - - easyMockBeanFactory.setMockInterface(Closeable.class); - easyMockBeanFactory.setMocksControl(control); - easyMockBeanFactory.afterPropertiesSet(); - - assertTrue(easyMockBeanFactory.isSingleton()); - assertEquals(Closeable.class, easyMockBeanFactory.getObjectType()); - - // Ignore compiler warning, as after type erasure the return type of getObject() is java.lang.Object. - assertTrue(easyMockBeanFactory.getObject() instanceof Closeable); - - assertTrue(control == ((ObjectMethodsFilter) Proxy.getInvocationHandler(easyMockBeanFactory.getObject())) - .getDelegate().getControl()); - } - - @Test - public void testFlowWithMocksControlInContext() { - IMocksControl control = EasyMock.createControl(); - - EasyMock.expect(beanFactory.getBean(EasyMockBeanFactory.DEFAULT_MOCKS_CONTROL_BEAN_NAME, IMocksControl.class)) - .andReturn(control); - - EasyMock.replay(beanFactory); - - easyMockBeanFactory.setMockInterface(Closeable.class); - easyMockBeanFactory.setBeanFactory(beanFactory); - easyMockBeanFactory.afterPropertiesSet(); - - assertTrue(easyMockBeanFactory.isSingleton()); - assertEquals(Closeable.class, easyMockBeanFactory.getObjectType()); - - // Ignore compiler warning, as after type erasure the return type of getObject() is java.lang.Object. - assertTrue(easyMockBeanFactory.getObject() instanceof Closeable); - - assertTrue(control == ((ObjectMethodsFilter) Proxy.getInvocationHandler(easyMockBeanFactory.getObject())) - .getDelegate().getControl()); - - EasyMock.verify(beanFactory); - } - - @Test - public void testFlowWithNoMocksControlInContext() { - EasyMock.expect(beanFactory.getBean(EasyMockBeanFactory.DEFAULT_MOCKS_CONTROL_BEAN_NAME, IMocksControl.class)) - .andReturn(null); - - EasyMock.replay(beanFactory); - - easyMockBeanFactory.setMockClass(Closeable.class); - easyMockBeanFactory.setBeanFactory(beanFactory); - easyMockBeanFactory.afterPropertiesSet(); - - assertTrue(easyMockBeanFactory.isSingleton()); - assertEquals(Closeable.class, easyMockBeanFactory.getObjectType()); - - // Ignore compiler warning, as after type erasure the return type of getObject() is java.lang.Object. - assertTrue(easyMockBeanFactory.getObject() instanceof Closeable); - - EasyMock.verify(beanFactory); - } -} diff --git a/spring-web/src/main/java/org/springframework/web/context/support/JacksonObjectMapperBeanFactory.java b/spring-web/src/main/java/org/springframework/web/context/support/JacksonObjectMapperBeanFactory.java deleted file mode 100644 index d4d9cf09d86d..000000000000 --- a/spring-web/src/main/java/org/springframework/web/context/support/JacksonObjectMapperBeanFactory.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright 2002-2007 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.web.context.support; - -import java.util.HashMap; -import java.util.Map; - -import org.codehaus.jackson.JsonGenerator; -import org.codehaus.jackson.JsonParser; -import org.codehaus.jackson.map.AnnotationIntrospector; -import org.codehaus.jackson.map.DeserializationConfig; -import org.codehaus.jackson.map.ObjectMapper; -import org.codehaus.jackson.map.SerializationConfig; -import org.springframework.beans.FatalBeanException; -import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.InitializingBean; - -/** - * The bean configurator for Jackson {@link ObjectMapper}. Allows to enable/disable certain features for Jackson mapper. - * Examples of usage: - * - *
- * <mvc:annotation-driven>
- * 	<mvc:message-converters>
- * 		<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
- * 			<property name="objectMapper">
- * 				<bean class="org.springframework.web.context.support.JacksonObjectMapperBeanFactory"
- * 					p:autoDetectFields="false"
- * 					p:autoDetectGettersSetters="false"
- * 					p:annotationIntrospector-ref="jaxbAnnotationIntrospector" />
- * 			</property>
- * 		</bean>
- * 	</mvc:message-converters>
- * </mvc:annotation-driven>
- * 
- * - *
- * <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
- * 	<property name="objectMapper">
- * 		<bean class="org.springframework.web.context.support.JacksonObjectMapperBeanFactory">
- * 			<property name="featuresToEnable">
- * 				<array>
- * 					<util:constant static-field="org.codehaus.jackson.map.SerializationConfig$Feature.AUTO_DETECT_FIELDS"/>
- * 					<util:constant static-field="org.codehaus.jackson.map.SerializationConfig$Feature.AUTO_DETECT_GETTERS"/>
- * 				</array>
- * 			</property>
- * 			<property name="featuresToDisable">
- * 				<array>
- * 					<util:constant static-field="org.codehaus.jackson.map.SerializationConfig$Feature.AUTO_DETECT_FIELDS"/>
- * 					<util:constant static-field="org.codehaus.jackson.map.SerializationConfig$Feature.AUTO_DETECT_GETTERS"/>
- * 				</array>
- * 			</property>
- * 		</bean>
- * 	</property>
- * </bean>
- * 
- * - * @author Dmitry Katsubo - */ -public class JacksonObjectMapperBeanFactory implements FactoryBean, InitializingBean { - - private ObjectMapper objectMapper; - - private Map features = new HashMap(); - - private AnnotationIntrospector annotationIntrospector; - - /** - * @see org.springframework.beans.factory.FactoryBean#getObject() - */ - public ObjectMapper getObject() { - return objectMapper; - } - - /** - * @see org.springframework.beans.factory.FactoryBean#getObjectType() - */ - public Class getObjectType() { - return ObjectMapper.class; - } - - /** - * @see org.springframework.beans.factory.FactoryBean#isSingleton() - */ - public boolean isSingleton() { - return true; - } - - public void setObjectMapper(ObjectMapper objectMapper) { - this.objectMapper = objectMapper; - } - - /** - * Define annotationIntrospector for both serializer and deserializer. - */ - public void setAnnotationIntrospector(AnnotationIntrospector annotationIntrospector) { - this.annotationIntrospector = annotationIntrospector; - } - - /** - * Generic setter for features to be enabled. - */ - public void setFeaturesToEnable(Object[] featuresToEnable) { - if (featuresToEnable == null) { - throw new FatalBeanException("featuresToEnable property should not be null"); - } - - for (Object feature : featuresToEnable) { - features.put(feature, Boolean.TRUE); - } - } - - /** - * Generic setter for features to be disabled. - */ - public void setFeaturesToDisable(Object[] featuresToDisable) { - if (featuresToDisable == null) { - throw new FatalBeanException("featuresToDisable property should not be null"); - } - - for (Object feature : featuresToDisable) { - features.put(feature, Boolean.FALSE); - } - } - - /** - * Shortcut for AUTO_DETECT_FIELDS option. - */ - public void setAutoDetectFields(boolean autoDetectFields) { - features.put(DeserializationConfig.Feature.AUTO_DETECT_FIELDS, Boolean.valueOf(autoDetectFields)); - features.put(SerializationConfig.Feature.AUTO_DETECT_FIELDS, Boolean.valueOf(autoDetectFields)); - } - - /** - * Shortcut for AUTO_DETECT_GETTERS / AUTO_DETECT_SETTERS option. - */ - public void setAutoDetectGettersSetters(boolean autoDetectGettersSetters) { - features.put(DeserializationConfig.Feature.AUTO_DETECT_SETTERS, Boolean.valueOf(autoDetectGettersSetters)); - features.put(SerializationConfig.Feature.AUTO_DETECT_GETTERS, Boolean.valueOf(autoDetectGettersSetters)); - } - - /** - * Shortcut for FAIL_ON_EMPTY_BEANS option. - */ - public void setFailOnEmptyBeans(boolean failOnEmptyBeans) { - features.put(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, Boolean.valueOf(failOnEmptyBeans)); - } - - /** - * Shortcut for INDENT_OUTPUT option. - */ - public void setIndentOutput(boolean indentOutput) { - features.put(SerializationConfig.Feature.INDENT_OUTPUT, Boolean.valueOf(indentOutput)); - } - - /** - * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() - */ - public void afterPropertiesSet() throws FatalBeanException { - if (objectMapper == null) { - objectMapper = new ObjectMapper(); - } - - if (annotationIntrospector != null) { - objectMapper.getSerializationConfig().setAnnotationIntrospector(annotationIntrospector); - objectMapper.getDeserializationConfig().setAnnotationIntrospector(annotationIntrospector); - } - - for (Map.Entry entry : features.entrySet()) { - setFeatureEnabled(entry.getKey(), entry.getValue().booleanValue()); - } - } - - private void setFeatureEnabled(Object feature, boolean enabled) { - if (feature instanceof DeserializationConfig.Feature) { - objectMapper.configure((DeserializationConfig.Feature) feature, enabled); - } else if (feature instanceof SerializationConfig.Feature) { - objectMapper.configure((SerializationConfig.Feature) feature, enabled); - } else if (feature instanceof JsonParser.Feature) { - objectMapper.configure((JsonParser.Feature) feature, enabled); - } else if (feature instanceof JsonGenerator.Feature) { - objectMapper.configure((JsonGenerator.Feature) feature, enabled); - } else { - throw new FatalBeanException("Unknown feature class " + feature.getClass().getName()); - } - } -} diff --git a/spring-web/src/test/java/org/springframework/web/context/support/JacksonObjectMapperBeanFactoryTest.java b/spring-web/src/test/java/org/springframework/web/context/support/JacksonObjectMapperBeanFactoryTest.java deleted file mode 100644 index 1a93c636134e..000000000000 --- a/spring-web/src/test/java/org/springframework/web/context/support/JacksonObjectMapperBeanFactoryTest.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2002-2009 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.web.context.support; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import org.codehaus.jackson.JsonGenerator; -import org.codehaus.jackson.JsonParser; -import org.codehaus.jackson.map.DeserializationConfig; -import org.codehaus.jackson.map.ObjectMapper; -import org.codehaus.jackson.map.SerializationConfig; -import org.codehaus.jackson.map.introspect.NopAnnotationIntrospector; -import org.junit.Before; -import org.junit.Test; -import org.springframework.beans.FatalBeanException; - -/** - * Test cases for {@link JacksonObjectMapperBeanFactory} class. - * - * @author Dmitry Katsubo - */ -public class JacksonObjectMapperBeanFactoryTest { - - private JacksonObjectMapperBeanFactory beanFactory; - - @Before - public void setUp() { - beanFactory = new JacksonObjectMapperBeanFactory(); - } - - @Test - public void testChecks() { - try { - beanFactory.setFeaturesToEnable(null); - fail("FatalBeanException should be thrown"); - } catch (FatalBeanException e) { - assertNotNull(e); - // should be thrown - } - - beanFactory.setFeaturesToEnable(new Object[0]); - - try { - beanFactory.setFeaturesToDisable(null); - fail("FatalBeanException should be thrown"); - } catch (FatalBeanException e) { - assertNotNull(e); - // should be thrown - } - - beanFactory.setFeaturesToDisable(new Object[0]); - } - - @Test(expected = FatalBeanException.class) - public void testUnknownFeature() { - beanFactory.setFeaturesToEnable(new Object[] { Boolean.TRUE }); - beanFactory.afterPropertiesSet(); - fail("FatalBeanException should be thrown"); - } - - @Test - public void testCommonSetters() { - beanFactory.setAutoDetectFields(false); - beanFactory.setAutoDetectGettersSetters(false); - beanFactory.setFailOnEmptyBeans(false); - beanFactory.setIndentOutput(true); - - beanFactory.afterPropertiesSet(); - - ObjectMapper objectMapper = beanFactory.getObject(); - - assertFalse(objectMapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.AUTO_DETECT_FIELDS)); - assertFalse(objectMapper.getDeserializationConfig().isEnabled(DeserializationConfig.Feature.AUTO_DETECT_FIELDS)); - - assertFalse(objectMapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.AUTO_DETECT_GETTERS)); - assertFalse(objectMapper.getDeserializationConfig() - .isEnabled(DeserializationConfig.Feature.AUTO_DETECT_SETTERS)); - - assertFalse(objectMapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS)); - - assertTrue(objectMapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.INDENT_OUTPUT)); - } - - @Test - public void testSimpleFlow() { - beanFactory.afterPropertiesSet(); - assertNotNull(beanFactory.getObject()); - assertTrue(beanFactory.isSingleton()); - assertEquals(ObjectMapper.class, beanFactory.getObjectType()); - } - - @Test - public void testCompleteFlow() { - NopAnnotationIntrospector annotationIntrospector = new NopAnnotationIntrospector(); - ObjectMapper objectMapper = new ObjectMapper(); - - assertTrue(beanFactory.isSingleton()); - assertEquals(ObjectMapper.class, beanFactory.getObjectType()); - - beanFactory.setObjectMapper(objectMapper); - beanFactory.setAnnotationIntrospector(annotationIntrospector); - beanFactory.setFeaturesToEnable(new Object[] { SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, - DeserializationConfig.Feature.USE_ANNOTATIONS, JsonParser.Feature.ALLOW_SINGLE_QUOTES, - JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS }); - beanFactory.setFeaturesToDisable(new Object[] { SerializationConfig.Feature.AUTO_DETECT_GETTERS, - DeserializationConfig.Feature.AUTO_DETECT_FIELDS, JsonParser.Feature.AUTO_CLOSE_SOURCE, - JsonGenerator.Feature.QUOTE_FIELD_NAMES }); - - beanFactory.afterPropertiesSet(); - - assertTrue(objectMapper == beanFactory.getObject()); - - assertTrue(annotationIntrospector == objectMapper.getSerializationConfig().getAnnotationIntrospector()); - assertTrue(annotationIntrospector == objectMapper.getDeserializationConfig().getAnnotationIntrospector()); - - assertTrue(objectMapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS)); - assertTrue(objectMapper.getDeserializationConfig().isEnabled(DeserializationConfig.Feature.USE_ANNOTATIONS)); - assertTrue(objectMapper.getJsonFactory().isEnabled(JsonParser.Feature.ALLOW_SINGLE_QUOTES)); - assertTrue(objectMapper.getJsonFactory().isEnabled(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS)); - - assertFalse(objectMapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.AUTO_DETECT_GETTERS)); - assertFalse(objectMapper.getDeserializationConfig().isEnabled(DeserializationConfig.Feature.AUTO_DETECT_FIELDS)); - assertFalse(objectMapper.getJsonFactory().isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE)); - assertFalse(objectMapper.getJsonFactory().isEnabled(JsonGenerator.Feature.QUOTE_FIELD_NAMES)); - } -} From e750099ea4e9ecee06c61b578fc1cdc22771b94e Mon Sep 17 00:00:00 2001 From: Dmitry Katsubo Date: Wed, 29 Aug 2012 21:42:00 +0200 Subject: [PATCH 6/7] Reduced the required memory for Gradle. --- gradlew.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradlew.bat b/gradlew.bat index e1b2af0e7229..bc289266ed33 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -12,7 +12,7 @@ if "%OS%"=="Windows_NT" setlocal set DEFAULT_JVM_OPTS= @rem ADDED BY HAND -- DO NOT ACCIDENTALLY DELETE WHEN UPGRADING GRADLE WRAPPER! -set GRADLE_OPTS=-XX:MaxPermSize=1024m -Xmx1024m %GRADLE_OPTS% +set GRADLE_OPTS=-XX:MaxPermSize=512m -Xmx768m %GRADLE_OPTS% @rem END ADDED BY HAND set DIRNAME=%~dp0 From 6233a944fa4fe1af89b49e404a4fc0c706944b4f Mon Sep 17 00:00:00 2001 From: Dmitry Katsubo Date: Tue, 13 Aug 2013 18:08:12 +0200 Subject: [PATCH 7/7] Fixed converter to delegate the call to marshaller/unmarshaller to check that given class is supported. Issue: SPR-10463 --- .../xml/MarshallingHttpMessageConverter.java | 25 +++- .../MarshallingHttpMessageConverterTests.java | 139 +++++++++++++++--- 2 files changed, 141 insertions(+), 23 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/converter/xml/MarshallingHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/xml/MarshallingHttpMessageConverter.java index 189bf9b21884..6615dd8cfdf8 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/xml/MarshallingHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/xml/MarshallingHttpMessageConverter.java @@ -17,11 +17,13 @@ package org.springframework.http.converter.xml; import java.io.IOException; + import javax.xml.transform.Result; import javax.xml.transform.Source; import org.springframework.beans.TypeMismatchException; import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.oxm.Marshaller; @@ -50,7 +52,6 @@ public class MarshallingHttpMessageConverter extends AbstractXmlHttpMessageConve private Unmarshaller unmarshaller; - /** * Construct a new {@code MarshallingHttpMessageConverter} with no {@link Marshaller} or * {@link Unmarshaller} set. The Marshaller and Unmarshaller must be set after construction @@ -88,7 +89,6 @@ public MarshallingHttpMessageConverter(Marshaller marshaller, Unmarshaller unmar this.unmarshaller = unmarshaller; } - /** * Set the {@link Marshaller} to be used by this message converter. */ @@ -103,10 +103,24 @@ public void setUnmarshaller(Unmarshaller unmarshaller) { this.unmarshaller = unmarshaller; } + @Override + public boolean canRead(Class clazz, MediaType mediaType) { + Assert.notNull(this.unmarshaller, "Property 'unmarshaller' is required"); + + return canRead(mediaType) && unmarshaller.supports(clazz); + } @Override - public boolean supports(Class clazz) { - return this.unmarshaller.supports(clazz); + public boolean canWrite(Class clazz, MediaType mediaType) { + Assert.notNull(this.marshaller, "Property 'marshaller' is required"); + + return canWrite(mediaType) && marshaller.supports(clazz); + } + + @Override + protected boolean supports(Class clazz) { + // should not be called, since we override canRead()/canWrite() + throw new UnsupportedOperationException(); } @Override @@ -131,8 +145,7 @@ protected void writeToResult(Object o, HttpHeaders headers, Result result) throw this.marshaller.marshal(o, result); } catch (MarshallingFailureException ex) { - throw new HttpMessageNotWritableException("Could not write [" + o + "]", ex); + throw new HttpMessageNotWritableException("Could not write [" + o.getClass() + "]", ex); } } - } diff --git a/spring-web/src/test/java/org/springframework/http/converter/xml/MarshallingHttpMessageConverterTests.java b/spring-web/src/test/java/org/springframework/http/converter/xml/MarshallingHttpMessageConverterTests.java index 75de8e40199c..93d143488e8d 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/xml/MarshallingHttpMessageConverterTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/xml/MarshallingHttpMessageConverterTests.java @@ -16,36 +16,69 @@ package org.springframework.http.converter.xml; -import javax.xml.transform.stream.StreamResult; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import javax.xml.transform.Result; import javax.xml.transform.stream.StreamSource; -import org.junit.Before; import org.junit.Test; +import org.springframework.beans.TypeMismatchException; import org.springframework.http.MediaType; import org.springframework.http.MockHttpInputMessage; import org.springframework.http.MockHttpOutputMessage; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.oxm.Marshaller; +import org.springframework.oxm.MarshallingFailureException; import org.springframework.oxm.Unmarshaller; - -import static org.junit.Assert.*; -import static org.mockito.BDDMockito.*; +import org.springframework.oxm.UnmarshallingFailureException; /** + * Tests for {@link MarshallingHttpMessageConverter}. + * * @author Arjen Poutsma */ public class MarshallingHttpMessageConverterTests { - private MarshallingHttpMessageConverter converter; + @Test + public void canRead() throws Exception { + Unmarshaller unmarshaller = mock(Unmarshaller.class); + + MarshallingHttpMessageConverter converter = new MarshallingHttpMessageConverter(); + + converter.setUnmarshaller(unmarshaller); + + when(unmarshaller.supports(Integer.class)).thenReturn(false); + when(unmarshaller.supports(String.class)).thenReturn(true); + + assertFalse(converter.canRead(Boolean.class, MediaType.TEXT_PLAIN)); + assertFalse(converter.canRead(Integer.class, MediaType.TEXT_XML)); + assertTrue(converter.canRead(String.class, MediaType.TEXT_XML)); + } + + @Test + public void canWrite() throws Exception { + Marshaller marshaller = mock(Marshaller.class); + + MarshallingHttpMessageConverter converter = new MarshallingHttpMessageConverter(); - private Marshaller marshaller; + converter.setMarshaller(marshaller); - private Unmarshaller unmarshaller; + when(marshaller.supports(Integer.class)).thenReturn(false); + when(marshaller.supports(String.class)).thenReturn(true); - @Before - public void setUp() { - marshaller = mock(Marshaller.class); - unmarshaller = mock(Unmarshaller.class); - converter = new MarshallingHttpMessageConverter(marshaller, unmarshaller); + assertFalse(converter.canWrite(Boolean.class, MediaType.TEXT_PLAIN)); + assertFalse(converter.canWrite(Integer.class, MediaType.TEXT_XML)); + assertTrue(converter.canWrite(String.class, MediaType.TEXT_XML)); } @Test @@ -53,20 +86,92 @@ public void read() throws Exception { String body = "Hello World"; MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8")); - given(unmarshaller.unmarshal(isA(StreamSource.class))).willReturn(body); + Unmarshaller unmarshaller = mock(Unmarshaller.class); + + MarshallingHttpMessageConverter converter = new MarshallingHttpMessageConverter(); + + converter.setUnmarshaller(unmarshaller); + + when(unmarshaller.unmarshal(isA(StreamSource.class))).thenReturn(body); String result = (String) converter.read(Object.class, inputMessage); assertEquals("Invalid result", body, result); } + @Test(expected = TypeMismatchException.class) + public void readWithTypeMismatchException() throws Exception { + MockHttpInputMessage inputMessage = new MockHttpInputMessage(new byte[0]); + + Marshaller marshaller = mock(Marshaller.class); + Unmarshaller unmarshaller = mock(Unmarshaller.class); + + MarshallingHttpMessageConverter converter = new MarshallingHttpMessageConverter(marshaller, unmarshaller); + + when(unmarshaller.unmarshal(isA(StreamSource.class))).thenReturn(Integer.valueOf(3)); + + converter.read(String.class, inputMessage); + } + + @Test + public void readWithMarshallingFailureException() throws Exception { + MockHttpInputMessage inputMessage = new MockHttpInputMessage(new byte[0]); + UnmarshallingFailureException ex = new UnmarshallingFailureException("forced"); + + Unmarshaller unmarshaller = mock(Unmarshaller.class); + MarshallingHttpMessageConverter converter = new MarshallingHttpMessageConverter(); + + converter.setUnmarshaller(unmarshaller); + + when(unmarshaller.unmarshal(isA(StreamSource.class))).thenThrow(ex); + + try { + converter.read(Object.class, inputMessage); + fail("HttpMessageNotReadableException should be thrown"); + } + catch (HttpMessageNotReadableException e) { + assertTrue("Invalid exception hierarchy", e.getCause() == ex); + } + } + @Test public void write() throws Exception { String body = "Hello World"; MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); + Marshaller marshaller = mock(Marshaller.class); + MarshallingHttpMessageConverter converter = new MarshallingHttpMessageConverter(marshaller); + + doNothing().when(marshaller).marshal(eq(body), isA(Result.class)); + converter.write(body, null, outputMessage); - assertEquals("Invalid content-type", new MediaType("application", "xml"), - outputMessage.getHeaders().getContentType()); - verify(marshaller).marshal(eq(body), isA(StreamResult.class)); + + assertEquals("Invalid content-type", new MediaType("application", "xml"), outputMessage.getHeaders() + .getContentType()); + } + + @Test + public void writeWithMarshallingFailureException() throws Exception { + String body = "Hello World"; + MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); + MarshallingFailureException ex = new MarshallingFailureException("forced"); + + Marshaller marshaller = mock(Marshaller.class); + + MarshallingHttpMessageConverter converter = new MarshallingHttpMessageConverter(marshaller); + + doThrow(ex).when(marshaller).marshal(eq(body), isA(Result.class)); + + try { + converter.write(body, null, outputMessage); + fail("HttpMessageNotWritableException should be thrown"); + } + catch (HttpMessageNotWritableException e) { + assertTrue("Invalid exception hierarchy", e.getCause() == ex); + } + } + + @Test(expected = UnsupportedOperationException.class) + public void supports() throws Exception { + new MarshallingHttpMessageConverter().supports(Object.class); } }