diff --git a/build-spring-framework/resources/changelog.txt b/build-spring-framework/resources/changelog.txt
index f1c47dc6c0..a747bea726 100644
--- a/build-spring-framework/resources/changelog.txt
+++ b/build-spring-framework/resources/changelog.txt
@@ -3,6 +3,16 @@ SPRING FRAMEWORK CHANGELOG
http://www.springsource.org
+Changes in version 3.0.0.RC1 (2009-06-10)
+-----------------------------------------
+
+* Servlet/Portlet ApplicationContexts use a specific id based on servlet/portlet name
+* DefaultListableBeanFactory references are serializable now when initialized with an id
+* scoped proxies are serializable now, for web scopes as well as for singleton beans
+* injected request/session references are serializable proxies for the current request now
+* ReloadableResourceBundleMessageSource correctly calculates filenames for all locales now
+
+
Changes in version 3.0.0.M3 (2009-05-06)
----------------------------------------
diff --git a/org.springframework.aop/src/main/java/org/springframework/aop/scope/DefaultScopedObject.java b/org.springframework.aop/src/main/java/org/springframework/aop/scope/DefaultScopedObject.java
index ea541fb0a7..77c3a107ab 100644
--- a/org.springframework.aop/src/main/java/org/springframework/aop/scope/DefaultScopedObject.java
+++ b/org.springframework.aop/src/main/java/org/springframework/aop/scope/DefaultScopedObject.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2006 the original author or authors.
+ * 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.
@@ -16,6 +16,8 @@
package org.springframework.aop.scope;
+import java.io.Serializable;
+
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.util.Assert;
@@ -32,7 +34,7 @@ import org.springframework.util.Assert;
* @see org.springframework.beans.factory.BeanFactory#getBean
* @see org.springframework.beans.factory.config.ConfigurableBeanFactory#destroyScopedBean
*/
-public class DefaultScopedObject implements ScopedObject {
+public class DefaultScopedObject implements ScopedObject, Serializable {
private final ConfigurableBeanFactory beanFactory;
@@ -43,8 +45,6 @@ public class DefaultScopedObject implements ScopedObject {
* Creates a new instance of the {@link DefaultScopedObject} class.
* @param beanFactory the {@link ConfigurableBeanFactory} that holds the scoped target object
* @param targetBeanName the name of the target bean
- * @throws IllegalArgumentException if either of the parameters is null
; or
- * if the targetBeanName
consists wholly of whitespace
*/
public DefaultScopedObject(ConfigurableBeanFactory beanFactory, String targetBeanName) {
Assert.notNull(beanFactory, "BeanFactory must not be null");
diff --git a/org.springframework.aop/src/main/java/org/springframework/aop/target/AbstractBeanFactoryBasedTargetSource.java b/org.springframework.aop/src/main/java/org/springframework/aop/target/AbstractBeanFactoryBasedTargetSource.java
index f8a0e3a50d..2bd3e3b44c 100644
--- a/org.springframework.aop/src/main/java/org/springframework/aop/target/AbstractBeanFactoryBasedTargetSource.java
+++ b/org.springframework.aop/src/main/java/org/springframework/aop/target/AbstractBeanFactoryBasedTargetSource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2007 the original author or authors.
+ * 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.
@@ -157,29 +157,6 @@ public abstract class AbstractBeanFactoryBasedTargetSource
this.beanFactory = other.beanFactory;
}
- /**
- * Replaces this object with a SingletonTargetSource on serialization.
- * Protected as otherwise it won't be invoked for subclasses.
- * (The writeReplace()
method must be visible to the class
- * being serialized.)
- *
With this implementation of this method, there is no need to mark
- * non-serializable fields in this class or subclasses as transient.
- */
- protected Object writeReplace() throws ObjectStreamException {
- if (logger.isDebugEnabled()) {
- logger.debug("Disconnecting TargetSource [" + this + "]");
- }
- try {
- // Create disconnected SingletonTargetSource.
- return new SingletonTargetSource(getTarget());
- }
- catch (Exception ex) {
- logger.error("Cannot get target for disconnecting TargetSource [" + this + "]", ex);
- throw new NotSerializableException(
- "Cannot get target for disconnecting TargetSource [" + this + "]: " + ex);
- }
- }
-
@Override
public boolean equals(Object other) {
diff --git a/org.springframework.aop/src/main/java/org/springframework/aop/target/AbstractPrototypeBasedTargetSource.java b/org.springframework.aop/src/main/java/org/springframework/aop/target/AbstractPrototypeBasedTargetSource.java
index 605b29d3a0..6ce2bca6ec 100644
--- a/org.springframework.aop/src/main/java/org/springframework/aop/target/AbstractPrototypeBasedTargetSource.java
+++ b/org.springframework.aop/src/main/java/org/springframework/aop/target/AbstractPrototypeBasedTargetSource.java
@@ -16,6 +16,9 @@
package org.springframework.aop.target;
+import java.io.NotSerializableException;
+import java.io.ObjectStreamException;
+
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
@@ -23,8 +26,8 @@ import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
/**
- * Base class for dynamic TargetSources that can create new prototype bean
- * instances to support a pooling or new-instance-per-invocation strategy.
+ * Base class for dynamic {@link TargetSource} implementations that create new prototype
+ * bean instances to support a pooling or new-instance-per-invocation strategy.
*
*
Such TargetSources must run in a {@link BeanFactory}, as it needs to
* call the getBean
method to create a new prototype instance.
@@ -83,4 +86,32 @@ public abstract class AbstractPrototypeBasedTargetSource extends AbstractBeanFac
}
}
+
+ //---------------------------------------------------------------------
+ // Serialization support
+ //---------------------------------------------------------------------
+
+ /**
+ * Replaces this object with a SingletonTargetSource on serialization.
+ * Protected as otherwise it won't be invoked for subclasses.
+ * (The writeReplace()
method must be visible to the class
+ * being serialized.)
+ *
With this implementation of this method, there is no need to mark
+ * non-serializable fields in this class or subclasses as transient.
+ */
+ protected Object writeReplace() throws ObjectStreamException {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Disconnecting TargetSource [" + this + "]");
+ }
+ try {
+ // Create disconnected SingletonTargetSource.
+ return new SingletonTargetSource(getTarget());
+ }
+ catch (Exception ex) {
+ logger.error("Cannot get target for disconnecting TargetSource [" + this + "]", ex);
+ throw new NotSerializableException(
+ "Cannot get target for disconnecting TargetSource [" + this + "]: " + ex);
+ }
+ }
+
}
diff --git a/org.springframework.aop/src/test/java/test/util/SerializationTestUtils.java b/org.springframework.aop/src/test/java/test/util/SerializationTestUtils.java
index cd7df05058..74130ff343 100644
--- a/org.springframework.aop/src/test/java/test/util/SerializationTestUtils.java
+++ b/org.springframework.aop/src/test/java/test/util/SerializationTestUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2008 the original author or authors.
+ * 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.
@@ -16,9 +16,7 @@
package test.util;
-import static org.junit.Assert.*;
-
-import java.awt.Point;
+import java.awt.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -28,8 +26,8 @@ import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
+import static org.junit.Assert.*;
import org.junit.Test;
-
import test.beans.TestBean;
/**
@@ -41,13 +39,13 @@ import test.beans.TestBean;
* @author Chris Beams
*/
public final class SerializationTestUtils {
-
+
public static void testSerialization(Object o) throws IOException {
OutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
}
-
+
public static boolean isSerializable(Object o) throws IOException {
try {
testSerialization(o);
@@ -57,7 +55,7 @@ public final class SerializationTestUtils {
return false;
}
}
-
+
public static Object serializeAndDeserialize(Object o) throws IOException, ClassNotFoundException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
@@ -69,30 +67,30 @@ public final class SerializationTestUtils {
ByteArrayInputStream is = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(is);
Object o2 = ois.readObject();
-
return o2;
}
-
+
+
@Test(expected=NotSerializableException.class)
public void testWithNonSerializableObject() throws IOException {
TestBean o = new TestBean();
assertFalse(o instanceof Serializable);
assertFalse(isSerializable(o));
-
+
testSerialization(o);
}
-
+
@Test
public void testWithSerializableObject() throws Exception {
int x = 5;
int y = 10;
Point p = new Point(x, y);
assertTrue(p instanceof Serializable);
-
+
testSerialization(p);
-
+
assertTrue(isSerializable(p));
-
+
Point p2 = (Point) serializeAndDeserialize(p);
assertNotSame(p, p2);
assertEquals(x, (int) p2.getX());
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java
index 6bb7a2aaa2..2b0cfa9b48 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2008 the original author or authors.
+ * 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.
@@ -236,6 +236,9 @@ public abstract class AbstractFactoryBean
}
+ /**
+ * Reflective InvocationHandler for lazy access to the actual singleton object.
+ */
private class EarlySingletonInvocationHandler implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
index e1c5db7069..b14c87110c 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
@@ -868,12 +868,12 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
if (mbd.isSingleton() || mbd.isPrototype()) {
throw new IllegalArgumentException(
- "Bean name '" + beanName + "' does not correspond to an object in a Scope");
+ "Bean name '" + beanName + "' does not correspond to an object in a mutable scope");
}
String scopeName = mbd.getScope();
Scope scope = this.scopes.get(scopeName);
if (scope == null) {
- throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
+ throw new IllegalStateException("No Scope SPI registered for scope '" + scopeName + "'");
}
Object bean = scope.remove(beanName);
if (bean != null) {
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java
index ac19224baf..4d83d9c8e2 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2008 the original author or authors.
+ * 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.
@@ -17,13 +17,18 @@
package org.springframework.beans.factory.support;
import java.beans.PropertyDescriptor;
+import java.io.Serializable;
import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
+import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Set;
+import org.springframework.beans.factory.ObjectFactory;
import org.springframework.util.ClassUtils;
/**
@@ -123,4 +128,55 @@ abstract class AutowireUtils {
return false;
}
+ /**
+ * Resolve the given autowiring value against the given required type,
+ * e.g. an {@link ObjectFactory} value to its actual object result.
+ * @param autowiringValue the value to resolve
+ * @param requiredType the type to assign the result to
+ * @return the resolved value
+ */
+ public static Object resolveAutowiringValue(Object autowiringValue, Class requiredType) {
+ if (autowiringValue instanceof ObjectFactory && !requiredType.isInstance(autowiringValue) &&
+ autowiringValue instanceof Serializable && requiredType.isInterface()) {
+ autowiringValue = Proxy.newProxyInstance(
+ requiredType.getClassLoader(), new Class[] {requiredType},
+ new ObjectFactoryDelegatingInvocationHandler((ObjectFactory) autowiringValue));
+ }
+ return autowiringValue;
+ }
+
+
+ /**
+ * Reflective InvocationHandler for lazy access to the current target object.
+ */
+ private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable {
+
+ private final ObjectFactory objectFactory;
+
+ public ObjectFactoryDelegatingInvocationHandler(ObjectFactory objectFactory) {
+ this.objectFactory = objectFactory;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ String methodName = method.getName();
+ if (methodName.equals("equals")) {
+ // Only consider equal when proxies are identical.
+ return (proxy == args[0]);
+ }
+ else if (methodName.equals("hashCode")) {
+ // Use hashCode of proxy.
+ return System.identityHashCode(proxy);
+ }
+ else if (methodName.equals("toString")) {
+ return this.objectFactory.toString();
+ }
+ try {
+ return method.invoke(this.objectFactory.getObject(), args);
+ }
+ catch (InvocationTargetException ex) {
+ throw ex.getTargetException();
+ }
+ }
+ }
+
}
diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
index cb8dbb70f5..e50cb973f9 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
@@ -16,7 +16,11 @@
package org.springframework.beans.factory.support;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
import java.lang.annotation.Annotation;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -81,7 +85,14 @@ import org.springframework.util.StringUtils;
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
*/
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
- implements ConfigurableListableBeanFactory, BeanDefinitionRegistry {
+ implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
+
+ /** Map from serialized id to factory instance */
+ private static final Map> serializableFactories =
+ new ConcurrentHashMap>();
+
+ /** Optional id for this factory, for serialization purposes */
+ private String serializationId;
/** Whether to allow re-registration of a different definition with the same name */
private boolean allowBeanDefinitionOverriding = true;
@@ -127,6 +138,20 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
}
+ /**
+ * Specify an id for serialization purposes, allowing this BeanFactory to be
+ * deserialized from this id back into the BeanFactory object, if needed.
+ */
+ public void setSerializationId(String serializationId) {
+ if (serializationId != null) {
+ serializableFactories.put(serializationId, new WeakReference(this));
+ }
+ else if (this.serializationId != null) {
+ serializableFactories.remove(this.serializationId);
+ }
+ this.serializationId = serializationId;
+ }
+
/**
* Set whether it should be allowed to override bean definitions by registering
* a different definition with the same name, automatically replacing the former.
@@ -724,9 +749,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
for (Class autowiringType : this.resolvableDependencies.keySet()) {
if (autowiringType.isAssignableFrom(requiredType)) {
Object autowiringValue = this.resolvableDependencies.get(autowiringType);
- if (autowiringValue instanceof ObjectFactory && !requiredType.isInstance(autowiringValue)) {
- autowiringValue = ((ObjectFactory) autowiringValue).getObject();
- }
+ autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
if (requiredType.isInstance(autowiringValue)) {
result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
break;
@@ -824,4 +847,46 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
return sb.toString();
}
+
+ //---------------------------------------------------------------------
+ // Serialization support
+ //---------------------------------------------------------------------
+
+ protected Object writeReplace() throws ObjectStreamException {
+ if (this.serializationId != null) {
+ return new SerializedBeanFactoryReference(this.serializationId);
+ }
+ else {
+ return this;
+ }
+ }
+
+
+ /**
+ * Minimal id reference to the factory.
+ * Resolved to the actual factory instance on deserialization.
+ */
+ private static class SerializedBeanFactoryReference implements Serializable {
+
+ private final String id;
+
+ public SerializedBeanFactoryReference(String id) {
+ this.id = id;
+ }
+
+ private Object readResolve() {
+ Reference ref = serializableFactories.get(this.id);
+ if (ref == null) {
+ throw new IllegalStateException(
+ "Cannot deserialize BeanFactory with id " + this.id + ": no factory registered for this id");
+ }
+ Object result = ref.get();
+ if (result == null) {
+ throw new IllegalStateException(
+ "Cannot deserialize BeanFactory with id " + this.id + ": factory has been garbage-collected");
+ }
+ return result;
+ }
+ }
+
}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java
index 6854cd1aa8..e00ff721d2 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2008 the original author or authors.
+ * 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.
@@ -65,6 +65,11 @@ public interface ConfigurableApplicationContext extends ApplicationContext, Life
String SYSTEM_ENVIRONMENT_BEAN_NAME = "systemEnvironment";
+ /**
+ * Set the unique id of this application context.
+ */
+ void setId(String id);
+
/**
* Set the parent of this application context.
* Note that the parent shouldn't be changed: It should only be set outside
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
index 212f7ec31f..0b417ac5ac 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
@@ -382,7 +382,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
catch (BeansException ex) {
// Destroy already created singletons to avoid dangling resources.
- beanFactory.destroySingletons();
+ destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
@@ -794,7 +794,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
* destroys the singletons in the bean factory of this application context.
*
Called by both close()
and a JVM shutdown hook, if any.
* @see org.springframework.context.event.ContextClosedEvent
- * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#destroySingletons()
+ * @see #destroyBeans()
* @see #close()
* @see #registerShutdownHook()
*/
@@ -803,6 +803,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
if (logger.isInfoEnabled()) {
logger.info("Closing " + this);
}
+
try {
// Publish shutdown event.
publishEvent(new ContextClosedEvent(this));
@@ -810,15 +811,19 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
catch (Throwable ex) {
logger.error("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
}
+
// Stop all Lifecycle beans, to avoid delays during individual destruction.
Map lifecycleBeans = getLifecycleBeans();
for (String beanName : new LinkedHashSet(lifecycleBeans.keySet())) {
doStop(lifecycleBeans, beanName);
}
+
// Destroy all cached singletons in the context's BeanFactory.
destroyBeans();
+
// Close the state of this context itself.
closeBeanFactory();
+
onClose();
synchronized (this.activeMonitor) {
this.active = false;
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java
index 2eee9bf0d2..161ecdce94 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java
@@ -122,6 +122,7 @@ public abstract class AbstractRefreshableApplicationContext extends AbstractAppl
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
+ beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
@@ -134,9 +135,18 @@ public abstract class AbstractRefreshableApplicationContext extends AbstractAppl
}
}
+ @Override
+ protected void cancelRefresh(BeansException ex) {
+ synchronized (this.beanFactoryMonitor) {
+ this.beanFactory.setSerializationId(null);
+ }
+ super.cancelRefresh(ex);
+ }
+
@Override
protected final void closeBeanFactory() {
synchronized (this.beanFactoryMonitor) {
+ this.beanFactory.setSerializationId(null);
this.beanFactory = null;
}
}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/GenericApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/support/GenericApplicationContext.java
index 11b5370f37..cc0218172c 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/support/GenericApplicationContext.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/GenericApplicationContext.java
@@ -99,6 +99,7 @@ public class GenericApplicationContext extends AbstractApplicationContext implem
*/
public GenericApplicationContext() {
this.beanFactory = new DefaultListableBeanFactory();
+ this.beanFactory.setSerializationId(getId());
this.beanFactory.setParameterNameDiscoverer(new LocalVariableTableParameterNameDiscoverer());
this.beanFactory.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
}
@@ -149,6 +150,12 @@ public class GenericApplicationContext extends AbstractApplicationContext implem
this.beanFactory.setParentBeanFactory(getInternalParentBeanFactory());
}
+ @Override
+ public void setId(String id) {
+ super.setId(id);
+ this.beanFactory.setSerializationId(id);
+ }
+
/**
* Set a ResourceLoader to use for this context. If set, the context will
* delegate all getResource
calls to the given ResourceLoader.
@@ -219,11 +226,12 @@ public class GenericApplicationContext extends AbstractApplicationContext implem
}
/**
- * Do nothing: We hold a single internal BeanFactory that will never
+ * Not much to do: We hold a single internal BeanFactory that will never
* get released.
*/
@Override
protected final void closeBeanFactory() {
+ this.beanFactory.setSerializationId(null);
}
/**
diff --git a/org.springframework.context/src/test/java/org/springframework/aop/scope/ScopedProxyTests.java b/org.springframework.context/src/test/java/org/springframework/aop/scope/ScopedProxyTests.java
index 291dc7f81a..8c3cfa094b 100644
--- a/org.springframework.context/src/test/java/org/springframework/aop/scope/ScopedProxyTests.java
+++ b/org.springframework.context/src/test/java/org/springframework/aop/scope/ScopedProxyTests.java
@@ -16,13 +16,13 @@
package org.springframework.aop.scope;
-import static org.junit.Assert.*;
-
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
+import static org.junit.Assert.*;
import org.junit.Test;
+
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.ITestBean;
import org.springframework.beans.TestBean;
@@ -31,6 +31,7 @@ import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.ClassPathResource;
+import org.springframework.util.SerializationTestUtils;
/**
* @author Rob Harrop
@@ -82,6 +83,7 @@ public class ScopedProxyTests {
@Test
public void testJdkScopedProxy() throws Exception {
XmlBeanFactory bf = new XmlBeanFactory(TESTBEAN_CONTEXT);
+ bf.setSerializationId("X");
SimpleMapScope scope = new SimpleMapScope();
bf.registerScope("request", scope);
@@ -91,14 +93,26 @@ public class ScopedProxyTests {
assertTrue(bean instanceof ScopedObject);
ScopedObject scoped = (ScopedObject) bean;
assertEquals(TestBean.class, scoped.getTargetObject().getClass());
+ bean.setAge(101);
assertTrue(scope.getMap().containsKey("testBeanTarget"));
assertEquals(TestBean.class, scope.getMap().get("testBeanTarget").getClass());
+
+ ITestBean deserialized = (ITestBean) SerializationTestUtils.serializeAndDeserialize(bean);
+ assertNotNull(deserialized);
+ assertTrue(AopUtils.isJdkDynamicProxy(deserialized));
+ assertEquals(101, bean.getAge());
+ assertTrue(deserialized instanceof ScopedObject);
+ ScopedObject scopedDeserialized = (ScopedObject) deserialized;
+ assertEquals(TestBean.class, scopedDeserialized.getTargetObject().getClass());
+
+ bf.setSerializationId(null);
}
@Test
- public void testCglibScopedProxy() {
+ public void testCglibScopedProxy() throws Exception {
XmlBeanFactory bf = new XmlBeanFactory(LIST_CONTEXT);
+ bf.setSerializationId("Y");
SimpleMapScope scope = new SimpleMapScope();
bf.registerScope("request", scope);
@@ -107,9 +121,20 @@ public class ScopedProxyTests {
assertTrue(tb.getFriends() instanceof ScopedObject);
ScopedObject scoped = (ScopedObject) tb.getFriends();
assertEquals(ArrayList.class, scoped.getTargetObject().getClass());
+ tb.getFriends().add("myFriend");
assertTrue(scope.getMap().containsKey("scopedTarget.scopedList"));
assertEquals(ArrayList.class, scope.getMap().get("scopedTarget.scopedList").getClass());
+
+ ArrayList deserialized = (ArrayList) SerializationTestUtils.serializeAndDeserialize(tb.getFriends());
+ assertNotNull(deserialized);
+ assertTrue(AopUtils.isCglibProxy(deserialized));
+ assertTrue(deserialized.contains("myFriend"));
+ assertTrue(deserialized instanceof ScopedObject);
+ ScopedObject scopedDeserialized = (ScopedObject) deserialized;
+ assertEquals(ArrayList.class, scopedDeserialized.getTargetObject().getClass());
+
+ bf.setSerializationId(null);
}
}
diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/ComponentScanParserScopedProxyTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/ComponentScanParserScopedProxyTests.java
index d806807dd3..4cbfd9b34e 100644
--- a/org.springframework.context/src/test/java/org/springframework/context/annotation/ComponentScanParserScopedProxyTests.java
+++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/ComponentScanParserScopedProxyTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2007 the original author or authors.
+ * 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.
@@ -16,16 +16,16 @@
package org.springframework.context.annotation;
+import example.scannable.FooService;
+import example.scannable.ScopedProxyTestBean;
import static org.junit.Assert.*;
-
import org.junit.Test;
+
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.config.SimpleMapScope;
import org.springframework.context.support.ClassPathXmlApplicationContext;
-
-import example.scannable.FooService;
-import example.scannable.ScopedProxyTestBean;
+import org.springframework.util.SerializationTestUtils;
/**
* @author Mark Fisher
@@ -54,7 +54,7 @@ public class ComponentScanParserScopedProxyTests {
}
@Test
- public void testInterfacesScopedProxy() {
+ public void testInterfacesScopedProxy() throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"org/springframework/context/annotation/scopedProxyInterfacesTests.xml");
context.getBeanFactory().registerScope("myScope", new SimpleMapScope());
@@ -62,16 +62,26 @@ public class ComponentScanParserScopedProxyTests {
FooService bean = (FooService) context.getBean("scopedProxyTestBean");
// should be dynamic proxy
assertTrue(AopUtils.isJdkDynamicProxy(bean));
+ // test serializability
+ assertEquals("bar", bean.foo(1));
+ FooService deserialized = (FooService) SerializationTestUtils.serializeAndDeserialize(bean);
+ assertNotNull(deserialized);
+ assertEquals("bar", deserialized.foo(1));
}
@Test
- public void testTargetClassScopedProxy() {
+ public void testTargetClassScopedProxy() throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"org/springframework/context/annotation/scopedProxyTargetClassTests.xml");
context.getBeanFactory().registerScope("myScope", new SimpleMapScope());
ScopedProxyTestBean bean = (ScopedProxyTestBean) context.getBean("scopedProxyTestBean");
// should be a class-based proxy
assertTrue(AopUtils.isCglibProxy(bean));
+ // test serializability
+ assertEquals("bar", bean.foo(1));
+ ScopedProxyTestBean deserialized = (ScopedProxyTestBean) SerializationTestUtils.serializeAndDeserialize(bean);
+ assertNotNull(deserialized);
+ assertEquals("bar", deserialized.foo(1));
}
@Test
diff --git a/org.springframework.context/src/test/java/org/springframework/util/SerializationTestUtils.java b/org.springframework.context/src/test/java/org/springframework/util/SerializationTestUtils.java
index bdffe7578d..f42512ccb0 100644
--- a/org.springframework.context/src/test/java/org/springframework/util/SerializationTestUtils.java
+++ b/org.springframework.context/src/test/java/org/springframework/util/SerializationTestUtils.java
@@ -1,11 +1,21 @@
/*
- * The Spring Framework is published under the terms
- * of the Apache Software License.
+ * 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.util;
-import java.awt.Point;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -13,20 +23,14 @@ import java.io.NotSerializableException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
-import java.io.Serializable;
-
-import junit.framework.TestCase;
-
-import org.springframework.beans.TestBean;
/**
* Utilities for testing serializability of objects.
* Exposes static methods for use in other test cases.
- * Extends TestCase only to test itself.
*
* @author Rod Johnson
*/
-public class SerializationTestUtils extends TestCase {
+public class SerializationTestUtils {
public static void testSerialization(Object o) throws IOException {
OutputStream baos = new ByteArrayOutputStream();
@@ -55,43 +59,7 @@ public class SerializationTestUtils extends TestCase {
ByteArrayInputStream is = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(is);
Object o2 = ois.readObject();
-
return o2;
}
-
- public SerializationTestUtils(String s) {
- super(s);
- }
-
- public void testWithNonSerializableObject() throws IOException {
- TestBean o = new TestBean();
- assertFalse(o instanceof Serializable);
-
- assertFalse(isSerializable(o));
-
- try {
- testSerialization(o);
- fail();
- }
- catch (NotSerializableException ex) {
- // Ok
- }
- }
-
- public void testWithSerializableObject() throws Exception {
- int x = 5;
- int y = 10;
- Point p = new Point(x, y);
- assertTrue(p instanceof Serializable);
-
- testSerialization(p);
-
- assertTrue(isSerializable(p));
-
- Point p2 = (Point) serializeAndDeserialize(p);
- assertNotSame(p, p2);
- assertEquals(x, (int) p2.getX());
- assertEquals(y, (int) p2.getY());
- }
}
diff --git a/org.springframework.integration-tests/src/test/java/org/springframework/aop/config/AopNamespaceHandlerScopeIntegrationTests-context.xml b/org.springframework.integration-tests/src/test/java/org/springframework/aop/config/AopNamespaceHandlerScopeIntegrationTests-context.xml
index add6b7e481..4be55f0ea7 100644
--- a/org.springframework.integration-tests/src/test/java/org/springframework/aop/config/AopNamespaceHandlerScopeIntegrationTests-context.xml
+++ b/org.springframework.integration-tests/src/test/java/org/springframework/aop/config/AopNamespaceHandlerScopeIntegrationTests-context.xml
@@ -13,6 +13,11 @@
+
+
+
+
+
diff --git a/org.springframework.integration-tests/src/test/java/org/springframework/aop/config/AopNamespaceHandlerScopeIntegrationTests.java b/org.springframework.integration-tests/src/test/java/org/springframework/aop/config/AopNamespaceHandlerScopeIntegrationTests.java
index 78b24f5aab..fa5feaeed6 100644
--- a/org.springframework.integration-tests/src/test/java/org/springframework/aop/config/AopNamespaceHandlerScopeIntegrationTests.java
+++ b/org.springframework.integration-tests/src/test/java/org/springframework/aop/config/AopNamespaceHandlerScopeIntegrationTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2007 the original author or authors.
+ * 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.
@@ -17,23 +17,24 @@
package org.springframework.aop.config;
import static java.lang.String.format;
-import static org.junit.Assert.*;
-import static org.springframework.util.ClassUtils.convertClassNameToResourcePath;
+import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
+import test.beans.ITestBean;
+import test.beans.TestBean;
+import test.util.SerializationTestUtils;
+
import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpSession;
+import org.springframework.util.ClassUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.support.XmlWebApplicationContext;
-import test.beans.ITestBean;
-import test.beans.TestBean;
-
/**
* Integration tests for scoped proxy use in conjunction with aop: namespace.
* Deemed an integration test because .web mocks and application contexts are required.
@@ -47,7 +48,7 @@ import test.beans.TestBean;
public final class AopNamespaceHandlerScopeIntegrationTests {
private static final String CLASSNAME = AopNamespaceHandlerScopeIntegrationTests.class.getName();
- private static final String CONTEXT = format("classpath:%s-context.xml", convertClassNameToResourcePath(CLASSNAME));
+ private static final String CONTEXT = format("classpath:%s-context.xml", ClassUtils.convertClassNameToResourcePath(CLASSNAME));
private ApplicationContext context;
@@ -59,6 +60,20 @@ public final class AopNamespaceHandlerScopeIntegrationTests {
this.context = wac;
}
+ @Test
+ public void testSingletonScoping() throws Exception {
+ ITestBean scoped = (ITestBean) this.context.getBean("singletonScoped");
+ assertTrue("Should be AOP proxy", AopUtils.isAopProxy(scoped));
+ assertTrue("Should be target class proxy", scoped instanceof TestBean);
+ String rob = "Rob Harrop";
+ String bram = "Bram Smeets";
+ assertEquals(rob, scoped.getName());
+ scoped.setName(bram);
+ assertEquals(bram, scoped.getName());
+ ITestBean deserialized = (ITestBean) SerializationTestUtils.serializeAndDeserialize(scoped);
+ assertEquals(bram, deserialized.getName());
+ }
+
@Test
public void testRequestScoping() throws Exception {
MockHttpServletRequest oldRequest = new MockHttpServletRequest();
diff --git a/org.springframework.integration-tests/src/test/java/test/util/SerializationTestUtils.java b/org.springframework.integration-tests/src/test/java/test/util/SerializationTestUtils.java
new file mode 100644
index 0000000000..609ae77e00
--- /dev/null
+++ b/org.springframework.integration-tests/src/test/java/test/util/SerializationTestUtils.java
@@ -0,0 +1,100 @@
+/*
+ * 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 test.util;
+
+import java.awt.*;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+
+import static org.junit.Assert.*;
+import org.junit.Test;
+import test.beans.TestBean;
+
+/**
+ * Utilities for testing serializability of objects.
+ * Exposes static methods for use in other test cases.
+ * Contains {@link org.junit.Test} methods to test itself.
+ *
+ * @author Rod Johnson
+ * @author Chris Beams
+ */
+public final class SerializationTestUtils {
+
+ public static void testSerialization(Object o) throws IOException {
+ OutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ oos.writeObject(o);
+ }
+
+ public static boolean isSerializable(Object o) throws IOException {
+ try {
+ testSerialization(o);
+ return true;
+ }
+ catch (NotSerializableException ex) {
+ return false;
+ }
+ }
+
+ public static Object serializeAndDeserialize(Object o) throws IOException, ClassNotFoundException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ oos.writeObject(o);
+ oos.flush();
+ baos.flush();
+ byte[] bytes = baos.toByteArray();
+
+ ByteArrayInputStream is = new ByteArrayInputStream(bytes);
+ ObjectInputStream ois = new ObjectInputStream(is);
+ Object o2 = ois.readObject();
+ return o2;
+ }
+
+
+ @Test(expected=NotSerializableException.class)
+ public void testWithNonSerializableObject() throws IOException {
+ TestBean o = new TestBean();
+ assertFalse(o instanceof Serializable);
+ assertFalse(isSerializable(o));
+
+ testSerialization(o);
+ }
+
+ @Test
+ public void testWithSerializableObject() throws Exception {
+ int x = 5;
+ int y = 10;
+ Point p = new Point(x, y);
+ assertTrue(p instanceof Serializable);
+
+ testSerialization(p);
+
+ assertTrue(isSerializable(p));
+
+ Point p2 = (Point) serializeAndDeserialize(p);
+ assertNotSame(p, p2);
+ assertEquals(x, (int) p2.getX());
+ assertEquals(y, (int) p2.getY());
+ }
+
+}
diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/FrameworkPortlet.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/FrameworkPortlet.java
index 3797f58621..a59f053ae0 100644
--- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/FrameworkPortlet.java
+++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/FrameworkPortlet.java
@@ -340,6 +340,7 @@ public abstract class FrameworkPortlet extends GenericPortletBean
ConfigurablePortletApplicationContext pac =
(ConfigurablePortletApplicationContext) BeanUtils.instantiateClass(getContextClass());
+ pac.setId(getPortletContext().getPortletContextName() + "." + getPortletName());
pac.setParent(parent);
pac.setPortletContext(getPortletContext());
pac.setPortletConfig(getPortletConfig());
diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/PortletApplicationContextUtils.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/PortletApplicationContextUtils.java
index 71b65c5186..9346983916 100644
--- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/PortletApplicationContextUtils.java
+++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/context/PortletApplicationContextUtils.java
@@ -16,6 +16,7 @@
package org.springframework.web.portlet.context;
+import java.io.Serializable;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
@@ -120,24 +121,8 @@ public abstract class PortletApplicationContextUtils {
pc.setAttribute(PortletContextScope.class.getName(), appScope);
}
- beanFactory.registerResolvableDependency(PortletRequest.class, new ObjectFactory() {
- public PortletRequest getObject() {
- RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
- if (!(requestAttr instanceof PortletRequestAttributes)) {
- throw new IllegalStateException("Current request is not a portlet request");
- }
- return ((PortletRequestAttributes) requestAttr).getRequest();
- }
- });
- beanFactory.registerResolvableDependency(PortletSession.class, new ObjectFactory() {
- public PortletSession getObject() {
- RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
- if (!(requestAttr instanceof PortletRequestAttributes)) {
- throw new IllegalStateException("Current request is not a portlet request");
- }
- return ((PortletRequestAttributes) requestAttr).getRequest().getPortletSession();
- }
- });
+ beanFactory.registerResolvableDependency(PortletRequest.class, new RequestObjectFactory());
+ beanFactory.registerResolvableDependency(PortletSession.class, new SessionObjectFactory());
}
/**
@@ -197,4 +182,44 @@ public abstract class PortletApplicationContextUtils {
}
}
+
+ /**
+ * Factory that exposes the current request object on demand.
+ */
+ private static class RequestObjectFactory implements ObjectFactory, Serializable {
+
+ public PortletRequest getObject() {
+ RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
+ if (!(requestAttr instanceof PortletRequestAttributes)) {
+ throw new IllegalStateException("Current request is not a portlet request");
+ }
+ return ((PortletRequestAttributes) requestAttr).getRequest();
+ }
+
+ @Override
+ public String toString() {
+ return "Current PortletRequest";
+ }
+ }
+
+
+ /**
+ * Factory that exposes the current session object on demand.
+ */
+ private static class SessionObjectFactory implements ObjectFactory, Serializable {
+
+ public PortletSession getObject() {
+ RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
+ if (!(requestAttr instanceof PortletRequestAttributes)) {
+ throw new IllegalStateException("Current request is not a portlet request");
+ }
+ return ((PortletRequestAttributes) requestAttr).getRequest().getPortletSession();
+ }
+
+ @Override
+ public String toString() {
+ return "Current PortletSession";
+ }
+ }
+
}
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FrameworkServlet.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FrameworkServlet.java
index a2a4d95db6..6a9b68aedf 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FrameworkServlet.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FrameworkServlet.java
@@ -416,6 +416,7 @@ public abstract class FrameworkServlet extends HttpServletBean
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(getContextClass());
+ wac.setId(getServletContext().getServletContextName() + "." + getServletName());
wac.setParent(parent);
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/util/SerializationTestUtils.java b/org.springframework.web.servlet/src/test/java/org/springframework/util/SerializationTestUtils.java
new file mode 100644
index 0000000000..d920a3b4ff
--- /dev/null
+++ b/org.springframework.web.servlet/src/test/java/org/springframework/util/SerializationTestUtils.java
@@ -0,0 +1,65 @@
+/*
+ * 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.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+
+/**
+ * Utilities for testing serializability of objects.
+ * Exposes static methods for use in other test cases.
+ *
+ * @author Rod Johnson
+ */
+public class SerializationTestUtils {
+
+ public static void testSerialization(Object o) throws IOException {
+ OutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ oos.writeObject(o);
+ }
+
+ public static boolean isSerializable(Object o) throws IOException {
+ try {
+ testSerialization(o);
+ return true;
+ }
+ catch (NotSerializableException ex) {
+ return false;
+ }
+ }
+
+ public static Object serializeAndDeserialize(Object o) throws IOException, ClassNotFoundException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ oos.writeObject(o);
+ oos.flush();
+ baos.flush();
+ byte[] bytes = baos.toByteArray();
+
+ ByteArrayInputStream is = new ByteArrayInputStream(bytes);
+ ObjectInputStream ois = new ObjectInputStream(is);
+ Object o2 = ois.readObject();
+ return o2;
+ }
+
+}
diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java
index 5b0cd45dd2..ee297ee1eb 100644
--- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java
+++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java
@@ -18,6 +18,7 @@ package org.springframework.web.servlet.mvc.annotation;
import java.io.IOException;
import java.io.Writer;
+import java.io.Serializable;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -72,6 +73,7 @@ import org.springframework.stereotype.Controller;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
+import org.springframework.util.SerializationTestUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors;
@@ -524,7 +526,7 @@ public class ServletAnnotationControllerTests {
GenericWebApplicationContext wac = new GenericWebApplicationContext();
wac.setServletContext(servletContext);
RootBeanDefinition bd = new RootBeanDefinition(MyParameterDispatchingController.class);
- bd.setScope(WebApplicationContext.SCOPE_REQUEST);
+ //bd.setScope(WebApplicationContext.SCOPE_REQUEST);
wac.registerBeanDefinition("controller", bd);
AnnotationConfigUtils.registerAnnotationConfigProcessors(wac);
wac.getBeanFactory().registerResolvableDependency(ServletConfig.class, servletConfig);
@@ -541,8 +543,8 @@ public class ServletAnnotationControllerTests {
assertEquals("myView", response.getContentAsString());
assertSame(servletContext, request.getAttribute("servletContext"));
assertSame(servletConfig, request.getAttribute("servletConfig"));
- assertSame(session, request.getAttribute("session"));
- assertSame(request, request.getAttribute("request"));
+ assertSame(session.getId(), request.getAttribute("sessionId"));
+ assertSame(request.getRequestURI(), request.getAttribute("requestUri"));
request = new MockHttpServletRequest(servletContext, "GET", "/myPath.do");
response = new MockHttpServletResponse();
@@ -551,8 +553,8 @@ public class ServletAnnotationControllerTests {
assertEquals("myView", response.getContentAsString());
assertSame(servletContext, request.getAttribute("servletContext"));
assertSame(servletConfig, request.getAttribute("servletConfig"));
- assertSame(session, request.getAttribute("session"));
- assertSame(request, request.getAttribute("request"));
+ assertSame(session.getId(), request.getAttribute("sessionId"));
+ assertSame(request.getRequestURI(), request.getAttribute("requestUri"));
request = new MockHttpServletRequest(servletContext, "GET", "/myPath.do");
request.addParameter("view", "other");
@@ -572,6 +574,11 @@ public class ServletAnnotationControllerTests {
response = new MockHttpServletResponse();
servlet.service(request, response);
assertEquals("mySurpriseView", response.getContentAsString());
+
+ MyParameterDispatchingController deserialized = (MyParameterDispatchingController)
+ SerializationTestUtils.serializeAndDeserialize(servlet.getWebApplicationContext().getBean("controller"));
+ assertNotNull(deserialized.request);
+ assertNotNull(deserialized.session);
}
@Test
@@ -1265,13 +1272,13 @@ public class ServletAnnotationControllerTests {
@Controller
@RequestMapping("/myPath.do")
- private static class MyParameterDispatchingController {
+ private static class MyParameterDispatchingController implements Serializable {
@Autowired
- private ServletContext servletContext;
+ private transient ServletContext servletContext;
@Autowired
- private ServletConfig servletConfig;
+ private transient ServletConfig servletConfig;
@Autowired
private HttpSession session;
@@ -1288,8 +1295,8 @@ public class ServletAnnotationControllerTests {
response.getWriter().write("myView");
request.setAttribute("servletContext", this.servletContext);
request.setAttribute("servletConfig", this.servletConfig);
- request.setAttribute("session", this.session);
- request.setAttribute("request", this.request);
+ request.setAttribute("sessionId", this.session.getId());
+ request.setAttribute("requestUri", this.request.getRequestURI());
}
@RequestMapping(params = {"view", "!lang"})
diff --git a/org.springframework.web/src/main/java/org/springframework/web/context/ContextLoader.java b/org.springframework.web/src/main/java/org/springframework/web/context/ContextLoader.java
index d926fdcef0..83e9845b83 100644
--- a/org.springframework.web/src/main/java/org/springframework/web/context/ContextLoader.java
+++ b/org.springframework.web/src/main/java/org/springframework/web/context/ContextLoader.java
@@ -247,6 +247,7 @@ public class ContextLoader {
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
+ wac.setId(servletContext.getServletContextName());
wac.setParent(parent);
wac.setServletContext(servletContext);
wac.setConfigLocation(servletContext.getInitParameter(CONFIG_LOCATION_PARAM));
diff --git a/org.springframework.web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java b/org.springframework.web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java
index 771d50766f..483bbd9fc0 100644
--- a/org.springframework.web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java
+++ b/org.springframework.web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java
@@ -16,6 +16,7 @@
package org.springframework.web.context.support;
+import java.io.Serializable;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
@@ -150,25 +151,8 @@ public abstract class WebApplicationContextUtils {
sc.setAttribute(ServletContextScope.class.getName(), appScope);
}
- beanFactory.registerResolvableDependency(ServletRequest.class, new ObjectFactory() {
- public ServletRequest getObject() {
- RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
- if (!(requestAttr instanceof ServletRequestAttributes)) {
- throw new IllegalStateException("Current request is not a servlet request");
- }
- return ((ServletRequestAttributes) requestAttr).getRequest();
- }
- });
- beanFactory.registerResolvableDependency(HttpSession.class, new ObjectFactory() {
- public HttpSession getObject() {
- RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
- if (!(requestAttr instanceof ServletRequestAttributes)) {
- throw new IllegalStateException("Current request is not a servlet request");
- }
- return ((ServletRequestAttributes) requestAttr).getRequest().getSession();
- }
- });
-
+ beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory());
+ beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory());
if (jsfPresent) {
FacesDependencyRegistrar.registerFacesDependencies(beanFactory);
}
@@ -237,6 +221,46 @@ public abstract class WebApplicationContextUtils {
}
+ /**
+ * Factory that exposes the current request object on demand.
+ */
+ private static class RequestObjectFactory implements ObjectFactory, Serializable {
+
+ public ServletRequest getObject() {
+ RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
+ if (!(requestAttr instanceof ServletRequestAttributes)) {
+ throw new IllegalStateException("Current request is not a servlet request");
+ }
+ return ((ServletRequestAttributes) requestAttr).getRequest();
+ }
+
+ @Override
+ public String toString() {
+ return "Current HttpServletRequest";
+ }
+ }
+
+
+ /**
+ * Factory that exposes the current session object on demand.
+ */
+ private static class SessionObjectFactory implements ObjectFactory, Serializable {
+
+ public HttpSession getObject() {
+ RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
+ if (!(requestAttr instanceof ServletRequestAttributes)) {
+ throw new IllegalStateException("Current request is not a servlet request");
+ }
+ return ((ServletRequestAttributes) requestAttr).getRequest().getSession();
+ }
+
+ @Override
+ public String toString() {
+ return "Current HttpSession";
+ }
+ }
+
+
/**
* Inner class to avoid hard-coded JSF dependency.
*/