Skip to content

Commit

Permalink
JndiObjectFactoryBean converts a "defaultObject" value to the expecte…
Browse files Browse the repository at this point in the history
…d type if necessary

Issue: SPR-11039
  • Loading branch information
jhoeller committed Nov 5, 2013
1 parent dcc6ef2 commit 0aedd81
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 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.
Expand All @@ -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;

/**
Expand Down Expand Up @@ -61,9 +67,10 @@
* @see #setCache
* @see JndiObjectTargetSource
*/
public class JndiObjectFactoryBean extends JndiObjectLocator implements FactoryBean<Object>, BeanClassLoaderAware {
public class JndiObjectFactoryBean extends JndiObjectLocator
implements FactoryBean<Object>, BeanFactoryAware, BeanClassLoaderAware {

private Class[] proxyInterfaces;
private Class<?>[] proxyInterfaces;

private boolean lookupOnStartup = true;

Expand All @@ -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;
Expand All @@ -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};
}

/**
Expand All @@ -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;
}

Expand Down Expand Up @@ -149,12 +158,25 @@ 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.
* <p>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;
Expand All @@ -180,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();
Expand Down Expand Up @@ -267,7 +296,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);
}

Expand All @@ -294,13 +323,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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -142,24 +144,22 @@ 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 {
jof.afterPropertiesSet();
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");
Expand All @@ -170,23 +170,44 @@ 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");
jof.afterPropertiesSet();
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");
Expand Down

0 comments on commit 0aedd81

Please sign in to comment.