From fe46a01a5748f586a6ea88bbabc11f290c63bedc Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 6 Nov 2013 00:36:48 +0100 Subject: [PATCH] JndiObjectFactoryBean converts a "defaultObject" value to the expected type if necessary Issue: SPR-11039 (cherry picked from commit 0aedd81) --- .../jndi/JndiObjectFactoryBean.java | 56 ++++++++++++++----- .../jndi/JndiObjectFactoryBeanTests.java | 43 ++++++++++---- 2 files changed, 75 insertions(+), 24 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/jndi/JndiObjectFactoryBean.java b/spring-context/src/main/java/org/springframework/jndi/JndiObjectFactoryBean.java index 504a96ad8c64..a906dab947bb 100644 --- a/spring-context/src/main/java/org/springframework/jndi/JndiObjectFactoryBean.java +++ b/spring-context/src/main/java/org/springframework/jndi/JndiObjectFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2013 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. @@ -25,8 +25,14 @@ import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.framework.ProxyFactory; +import org.springframework.beans.SimpleTypeConverter; +import org.springframework.beans.TypeConverter; +import org.springframework.beans.TypeMismatchException; import org.springframework.beans.factory.BeanClassLoaderAware; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.util.ClassUtils; /** @@ -61,9 +67,10 @@ * @see #setCache * @see JndiObjectTargetSource */ -public class JndiObjectFactoryBean extends JndiObjectLocator implements FactoryBean, BeanClassLoaderAware { +public class JndiObjectFactoryBean extends JndiObjectLocator + implements FactoryBean, BeanFactoryAware, BeanClassLoaderAware { - private Class[] proxyInterfaces; + private Class[] proxyInterfaces; private boolean lookupOnStartup = true; @@ -73,6 +80,8 @@ public class JndiObjectFactoryBean extends JndiObjectLocator implements FactoryB private Object defaultObject; + private ConfigurableBeanFactory beanFactory; + private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader(); private Object jndiObject; @@ -87,8 +96,8 @@ public class JndiObjectFactoryBean extends JndiObjectLocator implements FactoryB * @see #setLookupOnStartup * @see #setCache */ - public void setProxyInterface(Class proxyInterface) { - this.proxyInterfaces = new Class[] {proxyInterface}; + public void setProxyInterface(Class proxyInterface) { + this.proxyInterfaces = new Class[] {proxyInterface}; } /** @@ -100,7 +109,7 @@ public void setProxyInterface(Class proxyInterface) { * @see #setLookupOnStartup * @see #setCache */ - public void setProxyInterfaces(Class[] proxyInterfaces) { + public void setProxyInterfaces(Class... proxyInterfaces) { this.proxyInterfaces = proxyInterfaces; } @@ -149,12 +158,26 @@ public void setExposeAccessContext(boolean exposeAccessContext) { * It is typically used for literal values in scenarios where the JNDI environment * might define specific config settings but those are not required to be present. *

Note: This is only supported for lookup on startup. + * If specified together with {@link #setExpectedType}, the specified value + * needs to be either of that type or convertible to it. * @see #setLookupOnStartup + * @see ConfigurableBeanFactory#getTypeConverter() + * @see SimpleTypeConverter */ public void setDefaultObject(Object defaultObject) { this.defaultObject = defaultObject; } + @Override + public void setBeanFactory(BeanFactory beanFactory) { + if (beanFactory instanceof ConfigurableBeanFactory) { + // Just optional - for getting a specifically configured TypeConverter if needed. + // We'll simply fall back to a SimpleTypeConverter if no specific one available. + this.beanFactory = (ConfigurableBeanFactory) beanFactory; + } + } + + @Override public void setBeanClassLoader(ClassLoader classLoader) { this.beanClassLoader = classLoader; } @@ -179,9 +202,16 @@ public void afterPropertiesSet() throws IllegalArgumentException, NamingExceptio else { if (this.defaultObject != null && getExpectedType() != null && !getExpectedType().isInstance(this.defaultObject)) { - throw new IllegalArgumentException("Default object [" + this.defaultObject + - "] of type [" + this.defaultObject.getClass().getName() + - "] is not of expected type [" + getExpectedType().getName() + "]"); + TypeConverter converter = (this.beanFactory != null ? + this.beanFactory.getTypeConverter() : new SimpleTypeConverter()); + try { + this.defaultObject = converter.convertIfNecessary(this.defaultObject, getExpectedType()); + } + catch (TypeMismatchException ex) { + throw new IllegalArgumentException("Default object [" + this.defaultObject + "] of type [" + + this.defaultObject.getClass().getName() + "] is not of expected type [" + + getExpectedType().getName() + "] and cannot be converted either", ex); + } } // Locate specified JNDI object. this.jndiObject = lookupWithFallback(); @@ -263,7 +293,7 @@ public boolean isSingleton() { * @return the merged interface as Class * @see java.lang.reflect.Proxy#getProxyClass */ - protected Class createCompositeInterface(Class[] interfaces) { + protected Class createCompositeInterface(Class[] interfaces) { return ClassUtils.createCompositeInterface(interfaces, this.beanClassLoader); } @@ -290,13 +320,13 @@ private static Object createJndiObjectProxy(JndiObjectFactoryBean jof) throws Na proxyFactory.setInterfaces(jof.proxyInterfaces); } else { - Class targetClass = targetSource.getTargetClass(); + Class targetClass = targetSource.getTargetClass(); if (targetClass == null) { throw new IllegalStateException( "Cannot deactivate 'lookupOnStartup' without specifying a 'proxyInterface' or 'expectedType'"); } - Class[] ifcs = ClassUtils.getAllInterfacesForClass(targetClass, jof.beanClassLoader); - for (Class ifc : ifcs) { + Class[] ifcs = ClassUtils.getAllInterfacesForClass(targetClass, jof.beanClassLoader); + for (Class ifc : ifcs) { if (Modifier.isPublic(ifc.getModifiers())) { proxyFactory.addInterface(ifc); } diff --git a/spring-context/src/test/java/org/springframework/jndi/JndiObjectFactoryBeanTests.java b/spring-context/src/test/java/org/springframework/jndi/JndiObjectFactoryBeanTests.java index d9d7e60ac6b5..5d14e715a376 100644 --- a/spring-context/src/test/java/org/springframework/jndi/JndiObjectFactoryBeanTests.java +++ b/spring-context/src/test/java/org/springframework/jndi/JndiObjectFactoryBeanTests.java @@ -20,6 +20,8 @@ import javax.naming.NamingException; import org.junit.Test; + +import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.tests.mock.jndi.ExpectedLookupTemplate; import org.springframework.tests.sample.beans.DerivedTestBean; import org.springframework.tests.sample.beans.ITestBean; @@ -142,8 +144,7 @@ public void testLookupWithExpectedTypeAndMatch() throws Exception { @Test public void testLookupWithExpectedTypeAndNoMatch() throws Exception { JndiObjectFactoryBean jof = new JndiObjectFactoryBean(); - Object o = new Object(); - jof.setJndiTemplate(new ExpectedLookupTemplate("foo", o)); + jof.setJndiTemplate(new ExpectedLookupTemplate("foo", new Object())); jof.setJndiName("foo"); jof.setExpectedType(String.class); try { @@ -151,15 +152,14 @@ public void testLookupWithExpectedTypeAndNoMatch() throws Exception { fail("Should have thrown NamingException"); } catch (NamingException ex) { - assertTrue(ex.getMessage().indexOf("java.lang.String") != -1); + assertTrue(ex.getMessage().contains("java.lang.String")); } } @Test public void testLookupWithDefaultObject() throws Exception { JndiObjectFactoryBean jof = new JndiObjectFactoryBean(); - String s = ""; - jof.setJndiTemplate(new ExpectedLookupTemplate("foo", s)); + jof.setJndiTemplate(new ExpectedLookupTemplate("foo", "")); jof.setJndiName("myFoo"); jof.setExpectedType(String.class); jof.setDefaultObject("myString"); @@ -170,8 +170,7 @@ public void testLookupWithDefaultObject() throws Exception { @Test public void testLookupWithDefaultObjectAndExpectedType() throws Exception { JndiObjectFactoryBean jof = new JndiObjectFactoryBean(); - String s = ""; - jof.setJndiTemplate(new ExpectedLookupTemplate("foo", s)); + jof.setJndiTemplate(new ExpectedLookupTemplate("foo", "")); jof.setJndiName("myFoo"); jof.setExpectedType(String.class); jof.setDefaultObject("myString"); @@ -179,14 +178,36 @@ public void testLookupWithDefaultObjectAndExpectedType() throws Exception { assertEquals("myString", jof.getObject()); } + @Test + public void testLookupWithDefaultObjectAndExpectedTypeConversion() throws Exception { + JndiObjectFactoryBean jof = new JndiObjectFactoryBean(); + jof.setJndiTemplate(new ExpectedLookupTemplate("foo", "")); + jof.setJndiName("myFoo"); + jof.setExpectedType(Integer.class); + jof.setDefaultObject("5"); + jof.afterPropertiesSet(); + assertEquals(new Integer(5), jof.getObject()); + } + + @Test + public void testLookupWithDefaultObjectAndExpectedTypeConversionViaBeanFactory() throws Exception { + JndiObjectFactoryBean jof = new JndiObjectFactoryBean(); + jof.setJndiTemplate(new ExpectedLookupTemplate("foo", "")); + jof.setJndiName("myFoo"); + jof.setExpectedType(Integer.class); + jof.setDefaultObject("5"); + jof.setBeanFactory(new DefaultListableBeanFactory()); + jof.afterPropertiesSet(); + assertEquals(new Integer(5), jof.getObject()); + } + @Test public void testLookupWithDefaultObjectAndExpectedTypeNoMatch() throws Exception { JndiObjectFactoryBean jof = new JndiObjectFactoryBean(); - String s = ""; - jof.setJndiTemplate(new ExpectedLookupTemplate("foo", s)); + jof.setJndiTemplate(new ExpectedLookupTemplate("foo", "")); jof.setJndiName("myFoo"); - jof.setExpectedType(String.class); - jof.setDefaultObject(Boolean.TRUE); + jof.setExpectedType(Boolean.class); + jof.setDefaultObject("5"); try { jof.afterPropertiesSet(); fail("Should have thrown IllegalArgumentException");