Rename modules {org.springframework.*=>spring-*}
This renaming more intuitively expresses the relationship between
subprojects and the JAR artifacts they produce.
Tracking history across these renames is possible, but it requires
use of the --follow flag to `git log`, for example
$ git log spring-aop/src/main/java/org/springframework/aop/Advisor.java
will show history up until the renaming event, where
$ git log --follow spring-aop/src/main/java/org/springframework/aop/Advisor.java
will show history for all changes to the file, before and after the
renaming.
See http://chrisbeams.com/git-diff-across-renamed-directories
This commit is contained in:
103
spring-test/src/main/java/org/springframework/mock/env/MockPropertySource.java
vendored
Normal file
103
spring-test/src/main/java/org/springframework/mock/env/MockPropertySource.java
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.mock.env;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.core.env.PropertiesPropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
|
||||
/**
|
||||
* Simple {@link PropertySource} implementation for use in testing. Accepts
|
||||
* a user-provided {@link Properties} object, or if omitted during construction,
|
||||
* the implementation will initialize its own.
|
||||
*
|
||||
* The {@link #setProperty} and {@link #withProperty} methods are exposed for
|
||||
* convenience, for example:
|
||||
* <pre class="code">
|
||||
* {@code
|
||||
* PropertySource<?> source = new MockPropertySource().withProperty("foo", "bar");
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see MockEnvironment
|
||||
*/
|
||||
public class MockPropertySource extends PropertiesPropertySource {
|
||||
|
||||
/**
|
||||
* {@value} is the default name for {@link MockPropertySource} instances not
|
||||
* otherwise given an explicit name.
|
||||
* @see #MockPropertySource()
|
||||
* @see #MockPropertySource(String)
|
||||
*/
|
||||
public static final String MOCK_PROPERTIES_PROPERTY_SOURCE_NAME = "mockProperties";
|
||||
|
||||
/**
|
||||
* Create a new {@code MockPropertySource} named {@value #MOCK_PROPERTIES_PROPERTY_SOURCE_NAME}
|
||||
* that will maintain its own internal {@link Properties} instance.
|
||||
*/
|
||||
public MockPropertySource() {
|
||||
this(new Properties());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code MockPropertySource} with the given name that will
|
||||
* maintain its own internal {@link Properties} instance.
|
||||
* @param name the {@linkplain #getName() name} of the property source
|
||||
*/
|
||||
public MockPropertySource(String name) {
|
||||
this(name, new Properties());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code MockPropertySource} named {@value #MOCK_PROPERTIES_PROPERTY_SOURCE_NAME}
|
||||
* and backed by the given {@link Properties} object.
|
||||
* @param properties the properties to use
|
||||
*/
|
||||
public MockPropertySource(Properties properties) {
|
||||
this(MOCK_PROPERTIES_PROPERTY_SOURCE_NAME, properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code MockPropertySource} with with the given name and backed by the given
|
||||
* {@link Properties} object
|
||||
* @param name the {@linkplain #getName() name} of the property source
|
||||
* @param properties the properties to use
|
||||
*/
|
||||
public MockPropertySource(String name, Properties properties) {
|
||||
super(name, properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the given property on the underlying {@link Properties} object.
|
||||
*/
|
||||
public void setProperty(String name, Object value) {
|
||||
this.source.put(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient synonym for {@link #setProperty} that returns the current instance.
|
||||
* Useful for method chaining and fluent-style use.
|
||||
* @return this {@link MockPropertySource} instance
|
||||
*/
|
||||
public MockPropertySource withProperty(String name, Object value) {
|
||||
this.setProperty(name, value);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.mock.jndi;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
|
||||
import org.springframework.jndi.JndiTemplate;
|
||||
|
||||
/**
|
||||
* Simple extension of the JndiTemplate class that always returns a given
|
||||
* object. Very useful for testing. Effectively a mock object.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class ExpectedLookupTemplate extends JndiTemplate {
|
||||
|
||||
private final Map<String, Object> jndiObjects = new ConcurrentHashMap<String, Object>();
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new JndiTemplate that will always return given objects for
|
||||
* given names. To be populated through <code>addObject</code> calls.
|
||||
*
|
||||
* @see #addObject(String, Object)
|
||||
*/
|
||||
public ExpectedLookupTemplate() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new JndiTemplate that will always return the given object,
|
||||
* but honour only requests for the given name.
|
||||
*
|
||||
* @param name the name the client is expected to look up
|
||||
* @param object the object that will be returned
|
||||
*/
|
||||
public ExpectedLookupTemplate(String name, Object object) {
|
||||
addObject(name, object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given object to the list of JNDI objects that this template will
|
||||
* expose.
|
||||
*
|
||||
* @param name the name the client is expected to look up
|
||||
* @param object the object that will be returned
|
||||
*/
|
||||
public void addObject(String name, Object object) {
|
||||
this.jndiObjects.put(name, object);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the name is the expected name specified in the constructor, return the
|
||||
* object provided in the constructor. If the name is unexpected, a
|
||||
* respective NamingException gets thrown.
|
||||
*/
|
||||
public Object lookup(String name) throws NamingException {
|
||||
Object object = this.jndiObjects.get(name);
|
||||
if (object == null) {
|
||||
throw new NamingException("Unexpected JNDI name '" + name + "': expecting " + this.jndiObjects.keySet());
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.mock.jndi;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import javax.naming.Binding;
|
||||
import javax.naming.Context;
|
||||
import javax.naming.Name;
|
||||
import javax.naming.NameClassPair;
|
||||
import javax.naming.NameNotFoundException;
|
||||
import javax.naming.NameParser;
|
||||
import javax.naming.NamingEnumeration;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.OperationNotSupportedException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Simple implementation of a JNDI naming context.
|
||||
* Only supports binding plain Objects to String names.
|
||||
* Mainly for test environments, but also usable for standalone applications.
|
||||
*
|
||||
* <p>This class is not intended for direct usage by applications, although it
|
||||
* can be used for example to override JndiTemplate's <code>createInitialContext</code>
|
||||
* method in unit tests. Typically, SimpleNamingContextBuilder will be used to
|
||||
* set up a JVM-level JNDI environment.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see org.springframework.mock.jndi.SimpleNamingContextBuilder
|
||||
* @see org.springframework.jndi.JndiTemplate#createInitialContext
|
||||
*/
|
||||
public class SimpleNamingContext implements Context {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final String root;
|
||||
|
||||
private final Hashtable<String, Object> boundObjects;
|
||||
|
||||
private final Hashtable<String, Object> environment = new Hashtable<String, Object>();
|
||||
|
||||
|
||||
/**
|
||||
* Create a new naming context.
|
||||
*/
|
||||
public SimpleNamingContext() {
|
||||
this("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new naming context with the given naming root.
|
||||
*/
|
||||
public SimpleNamingContext(String root) {
|
||||
this.root = root;
|
||||
this.boundObjects = new Hashtable<String, Object>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new naming context with the given naming root,
|
||||
* the given name/object map, and the JNDI environment entries.
|
||||
*/
|
||||
public SimpleNamingContext(String root, Hashtable<String, Object> boundObjects, Hashtable<String, Object> env) {
|
||||
this.root = root;
|
||||
this.boundObjects = boundObjects;
|
||||
if (env != null) {
|
||||
this.environment.putAll(env);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Actual implementations of Context methods follow
|
||||
|
||||
public NamingEnumeration<NameClassPair> list(String root) throws NamingException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Listing name/class pairs under [" + root + "]");
|
||||
}
|
||||
return new NameClassPairEnumeration(this, root);
|
||||
}
|
||||
|
||||
public NamingEnumeration<Binding> listBindings(String root) throws NamingException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Listing bindings under [" + root + "]");
|
||||
}
|
||||
return new BindingEnumeration(this, root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up the object with the given name.
|
||||
* <p>Note: Not intended for direct use by applications.
|
||||
* Will be used by any standard InitialContext JNDI lookups.
|
||||
* @throws javax.naming.NameNotFoundException if the object could not be found
|
||||
*/
|
||||
public Object lookup(String lookupName) throws NameNotFoundException {
|
||||
String name = this.root + lookupName;
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Static JNDI lookup: [" + name + "]");
|
||||
}
|
||||
if ("".equals(name)) {
|
||||
return new SimpleNamingContext(this.root, this.boundObjects, this.environment);
|
||||
}
|
||||
Object found = this.boundObjects.get(name);
|
||||
if (found == null) {
|
||||
if (!name.endsWith("/")) {
|
||||
name = name + "/";
|
||||
}
|
||||
for (String boundName : this.boundObjects.keySet()) {
|
||||
if (boundName.startsWith(name)) {
|
||||
return new SimpleNamingContext(name, this.boundObjects, this.environment);
|
||||
}
|
||||
}
|
||||
throw new NameNotFoundException(
|
||||
"Name [" + this.root + lookupName + "] not bound; " + this.boundObjects.size() + " bindings: [" +
|
||||
StringUtils.collectionToDelimitedString(this.boundObjects.keySet(), ",") + "]");
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
public Object lookupLink(String name) throws NameNotFoundException {
|
||||
return lookup(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind the given object to the given name.
|
||||
* Note: Not intended for direct use by applications
|
||||
* if setting up a JVM-level JNDI environment.
|
||||
* Use SimpleNamingContextBuilder to set up JNDI bindings then.
|
||||
* @see org.springframework.mock.jndi.SimpleNamingContextBuilder#bind
|
||||
*/
|
||||
public void bind(String name, Object obj) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Static JNDI binding: [" + this.root + name + "] = [" + obj + "]");
|
||||
}
|
||||
this.boundObjects.put(this.root + name, obj);
|
||||
}
|
||||
|
||||
public void unbind(String name) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Static JNDI remove: [" + this.root + name + "]");
|
||||
}
|
||||
this.boundObjects.remove(this.root + name);
|
||||
}
|
||||
|
||||
public void rebind(String name, Object obj) {
|
||||
bind(name, obj);
|
||||
}
|
||||
|
||||
public void rename(String oldName, String newName) throws NameNotFoundException {
|
||||
Object obj = lookup(oldName);
|
||||
unbind(oldName);
|
||||
bind(newName, obj);
|
||||
}
|
||||
|
||||
public Context createSubcontext(String name) {
|
||||
String subcontextName = this.root + name;
|
||||
if (!subcontextName.endsWith("/")) {
|
||||
subcontextName += "/";
|
||||
}
|
||||
Context subcontext = new SimpleNamingContext(subcontextName, this.boundObjects, this.environment);
|
||||
bind(name, subcontext);
|
||||
return subcontext;
|
||||
}
|
||||
|
||||
public void destroySubcontext(String name) {
|
||||
unbind(name);
|
||||
}
|
||||
|
||||
public String composeName(String name, String prefix) {
|
||||
return prefix + name;
|
||||
}
|
||||
|
||||
public Hashtable<String, Object> getEnvironment() {
|
||||
return this.environment;
|
||||
}
|
||||
|
||||
public Object addToEnvironment(String propName, Object propVal) {
|
||||
return this.environment.put(propName, propVal);
|
||||
}
|
||||
|
||||
public Object removeFromEnvironment(String propName) {
|
||||
return this.environment.remove(propName);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
}
|
||||
|
||||
|
||||
// Unsupported methods follow: no support for javax.naming.Name
|
||||
|
||||
public NamingEnumeration<NameClassPair> list(Name name) throws NamingException {
|
||||
throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]");
|
||||
}
|
||||
|
||||
public NamingEnumeration<Binding> listBindings(Name name) throws NamingException {
|
||||
throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]");
|
||||
}
|
||||
|
||||
public Object lookup(Name name) throws NamingException {
|
||||
throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]");
|
||||
}
|
||||
|
||||
public Object lookupLink(Name name) throws NamingException {
|
||||
throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]");
|
||||
}
|
||||
|
||||
public void bind(Name name, Object obj) throws NamingException {
|
||||
throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]");
|
||||
}
|
||||
|
||||
public void unbind(Name name) throws NamingException {
|
||||
throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]");
|
||||
}
|
||||
|
||||
public void rebind(Name name, Object obj) throws NamingException {
|
||||
throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]");
|
||||
}
|
||||
|
||||
public void rename(Name oldName, Name newName) throws NamingException {
|
||||
throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]");
|
||||
}
|
||||
|
||||
public Context createSubcontext(Name name) throws NamingException {
|
||||
throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]");
|
||||
}
|
||||
|
||||
public void destroySubcontext(Name name) throws NamingException {
|
||||
throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]");
|
||||
}
|
||||
|
||||
public String getNameInNamespace() throws NamingException {
|
||||
throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]");
|
||||
}
|
||||
|
||||
public NameParser getNameParser(Name name) throws NamingException {
|
||||
throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]");
|
||||
}
|
||||
|
||||
public NameParser getNameParser(String name) throws NamingException {
|
||||
throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]");
|
||||
}
|
||||
|
||||
public Name composeName(Name name, Name prefix) throws NamingException {
|
||||
throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]");
|
||||
}
|
||||
|
||||
|
||||
private static abstract class AbstractNamingEnumeration<T> implements NamingEnumeration<T> {
|
||||
|
||||
private Iterator<T> iterator;
|
||||
|
||||
private AbstractNamingEnumeration(SimpleNamingContext context, String proot) throws NamingException {
|
||||
if (!"".equals(proot) && !proot.endsWith("/")) {
|
||||
proot = proot + "/";
|
||||
}
|
||||
String root = context.root + proot;
|
||||
Map<String, T> contents = new HashMap<String, T>();
|
||||
for (String boundName : context.boundObjects.keySet()) {
|
||||
if (boundName.startsWith(root)) {
|
||||
int startIndex = root.length();
|
||||
int endIndex = boundName.indexOf('/', startIndex);
|
||||
String strippedName =
|
||||
(endIndex != -1 ? boundName.substring(startIndex, endIndex) : boundName.substring(startIndex));
|
||||
if (!contents.containsKey(strippedName)) {
|
||||
try {
|
||||
contents.put(strippedName, createObject(strippedName, context.lookup(proot + strippedName)));
|
||||
}
|
||||
catch (NameNotFoundException ex) {
|
||||
// cannot happen
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (contents.size() == 0) {
|
||||
throw new NamingException("Invalid root: [" + context.root + proot + "]");
|
||||
}
|
||||
this.iterator = contents.values().iterator();
|
||||
}
|
||||
|
||||
protected abstract T createObject(String strippedName, Object obj);
|
||||
|
||||
public boolean hasMore() {
|
||||
return this.iterator.hasNext();
|
||||
}
|
||||
|
||||
public T next() {
|
||||
return this.iterator.next();
|
||||
}
|
||||
|
||||
public boolean hasMoreElements() {
|
||||
return this.iterator.hasNext();
|
||||
}
|
||||
|
||||
public T nextElement() {
|
||||
return this.iterator.next();
|
||||
}
|
||||
|
||||
public void close() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class NameClassPairEnumeration extends AbstractNamingEnumeration<NameClassPair> {
|
||||
|
||||
private NameClassPairEnumeration(SimpleNamingContext context, String root) throws NamingException {
|
||||
super(context, root);
|
||||
}
|
||||
|
||||
protected NameClassPair createObject(String strippedName, Object obj) {
|
||||
return new NameClassPair(strippedName, obj.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class BindingEnumeration extends AbstractNamingEnumeration<Binding> {
|
||||
|
||||
private BindingEnumeration(SimpleNamingContext context, String root) throws NamingException {
|
||||
super(context, root);
|
||||
}
|
||||
|
||||
protected Binding createObject(String strippedName, Object obj) {
|
||||
return new Binding(strippedName, obj);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* 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.mock.jndi;
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
import javax.naming.Context;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.spi.InitialContextFactory;
|
||||
import javax.naming.spi.InitialContextFactoryBuilder;
|
||||
import javax.naming.spi.NamingManager;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Simple implementation of a JNDI naming context builder.
|
||||
*
|
||||
* <p>Mainly targeted at test environments, where each test case can
|
||||
* configure JNDI appropriately, so that <code>new InitialContext()</code>
|
||||
* will expose the required objects. Also usable for standalone applications,
|
||||
* e.g. for binding a JDBC DataSource to a well-known JNDI location, to be
|
||||
* able to use traditional J2EE data access code outside of a J2EE container.
|
||||
*
|
||||
* <p>There are various choices for DataSource implementations:
|
||||
* <ul>
|
||||
* <li>SingleConnectionDataSource (using the same Connection for all getConnection calls);
|
||||
* <li>DriverManagerDataSource (creating a new Connection on each getConnection call);
|
||||
* <li>Apache's Jakarta Commons DBCP offers BasicDataSource (a real pool).
|
||||
* </ul>
|
||||
*
|
||||
* <p>Typical usage in bootstrap code:
|
||||
*
|
||||
* <pre class="code">
|
||||
* SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
|
||||
* DataSource ds = new DriverManagerDataSource(...);
|
||||
* builder.bind("java:comp/env/jdbc/myds", ds);
|
||||
* builder.activate();</pre>
|
||||
*
|
||||
* Note that it's impossible to activate multiple builders within the same JVM,
|
||||
* due to JNDI restrictions. Thus to configure a fresh builder repeatedly, use
|
||||
* the following code to get a reference to either an already activated builder
|
||||
* or a newly activated one:
|
||||
*
|
||||
* <pre class="code">
|
||||
* SimpleNamingContextBuilder builder = SimpleNamingContextBuilder.emptyActivatedContextBuilder();
|
||||
* DataSource ds = new DriverManagerDataSource(...);
|
||||
* builder.bind("java:comp/env/jdbc/myds", ds);</pre>
|
||||
*
|
||||
* Note that you <i>should not</i> call <code>activate()</code> on a builder from
|
||||
* this factory method, as there will already be an activated one in any case.
|
||||
*
|
||||
* <p>An instance of this class is only necessary at setup time.
|
||||
* An application does not need to keep a reference to it after activation.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rod Johnson
|
||||
* @see #emptyActivatedContextBuilder()
|
||||
* @see #bind(String, Object)
|
||||
* @see #activate()
|
||||
* @see org.springframework.mock.jndi.SimpleNamingContext
|
||||
* @see org.springframework.jdbc.datasource.SingleConnectionDataSource
|
||||
* @see org.springframework.jdbc.datasource.DriverManagerDataSource
|
||||
* @see org.apache.commons.dbcp.BasicDataSource
|
||||
*/
|
||||
public class SimpleNamingContextBuilder implements InitialContextFactoryBuilder {
|
||||
|
||||
/** An instance of this class bound to JNDI */
|
||||
private static volatile SimpleNamingContextBuilder activated;
|
||||
|
||||
private static boolean initialized = false;
|
||||
|
||||
private static final Object initializationLock = new Object();
|
||||
|
||||
|
||||
/**
|
||||
* Checks if a SimpleNamingContextBuilder is active.
|
||||
* @return the current SimpleNamingContextBuilder instance,
|
||||
* or <code>null</code> if none
|
||||
*/
|
||||
public static SimpleNamingContextBuilder getCurrentContextBuilder() {
|
||||
return activated;
|
||||
}
|
||||
|
||||
/**
|
||||
* If no SimpleNamingContextBuilder is already configuring JNDI,
|
||||
* create and activate one. Otherwise take the existing activate
|
||||
* SimpleNamingContextBuilder, clear it and return it.
|
||||
* <p>This is mainly intended for test suites that want to
|
||||
* reinitialize JNDI bindings from scratch repeatedly.
|
||||
* @return an empty SimpleNamingContextBuilder that can be used
|
||||
* to control JNDI bindings
|
||||
*/
|
||||
public static SimpleNamingContextBuilder emptyActivatedContextBuilder() throws NamingException {
|
||||
if (activated != null) {
|
||||
// Clear already activated context builder.
|
||||
activated.clear();
|
||||
}
|
||||
else {
|
||||
// Create and activate new context builder.
|
||||
SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
|
||||
// The activate() call will cause an assignment to the activated variable.
|
||||
builder.activate();
|
||||
}
|
||||
return activated;
|
||||
}
|
||||
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final Hashtable<String,Object> boundObjects = new Hashtable<String,Object>();
|
||||
|
||||
|
||||
/**
|
||||
* Register the context builder by registering it with the JNDI NamingManager.
|
||||
* Note that once this has been done, <code>new InitialContext()</code> will always
|
||||
* return a context from this factory. Use the <code>emptyActivatedContextBuilder()</code>
|
||||
* static method to get an empty context (for example, in test methods).
|
||||
* @throws IllegalStateException if there's already a naming context builder
|
||||
* registered with the JNDI NamingManager
|
||||
*/
|
||||
public void activate() throws IllegalStateException, NamingException {
|
||||
logger.info("Activating simple JNDI environment");
|
||||
synchronized (initializationLock) {
|
||||
if (!initialized) {
|
||||
if (NamingManager.hasInitialContextFactoryBuilder()) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot activate SimpleNamingContextBuilder: there is already a JNDI provider registered. " +
|
||||
"Note that JNDI is a JVM-wide service, shared at the JVM system class loader level, " +
|
||||
"with no reset option. As a consequence, a JNDI provider must only be registered once per JVM.");
|
||||
}
|
||||
NamingManager.setInitialContextFactoryBuilder(this);
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
activated = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporarily deactivate this context builder. It will remain registered with
|
||||
* the JNDI NamingManager but will delegate to the standard JNDI InitialContextFactory
|
||||
* (if configured) instead of exposing its own bound objects.
|
||||
* <p>Call <code>activate()</code> again in order to expose this context builder's own
|
||||
* bound objects again. Such activate/deactivate sequences can be applied any number
|
||||
* of times (e.g. within a larger integration test suite running in the same VM).
|
||||
* @see #activate()
|
||||
*/
|
||||
public void deactivate() {
|
||||
logger.info("Deactivating simple JNDI environment");
|
||||
activated = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all bindings in this context builder, while keeping it active.
|
||||
*/
|
||||
public void clear() {
|
||||
this.boundObjects.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind the given object under the given name, for all naming contexts
|
||||
* that this context builder will generate.
|
||||
* @param name the JNDI name of the object (e.g. "java:comp/env/jdbc/myds")
|
||||
* @param obj the object to bind (e.g. a DataSource implementation)
|
||||
*/
|
||||
public void bind(String name, Object obj) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Static JNDI binding: [" + name + "] = [" + obj + "]");
|
||||
}
|
||||
this.boundObjects.put(name, obj);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simple InitialContextFactoryBuilder implementation,
|
||||
* creating a new SimpleNamingContext instance.
|
||||
* @see SimpleNamingContext
|
||||
*/
|
||||
public InitialContextFactory createInitialContextFactory(Hashtable<?,?> environment) {
|
||||
if (activated == null && environment != null) {
|
||||
Object icf = environment.get(Context.INITIAL_CONTEXT_FACTORY);
|
||||
if (icf != null) {
|
||||
Class<?> icfClass = null;
|
||||
if (icf instanceof Class) {
|
||||
icfClass = (Class<?>) icf;
|
||||
}
|
||||
else if (icf instanceof String) {
|
||||
icfClass = ClassUtils.resolveClassName((String) icf, getClass().getClassLoader());
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Invalid value type for environment key [" +
|
||||
Context.INITIAL_CONTEXT_FACTORY + "]: " + icf.getClass().getName());
|
||||
}
|
||||
if (!InitialContextFactory.class.isAssignableFrom(icfClass)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Specified class does not implement [" + InitialContextFactory.class.getName() + "]: " + icf);
|
||||
}
|
||||
try {
|
||||
return (InitialContextFactory) icfClass.newInstance();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
IllegalStateException ise =
|
||||
new IllegalStateException("Cannot instantiate specified InitialContextFactory: " + icf);
|
||||
ise.initCause(ex);
|
||||
throw ise;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Default case...
|
||||
return new InitialContextFactory() {
|
||||
@SuppressWarnings("unchecked")
|
||||
public Context getInitialContext(Hashtable<?,?> environment) {
|
||||
return new SimpleNamingContext("", boundObjects, (Hashtable<String, Object>) environment);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* The simplest implementation of the JNDI SPI that could possibly work.
|
||||
*
|
||||
* <p>Useful for setting up a simple JNDI environment for test suites
|
||||
* or stand-alone applications. If, for example, JDBC DataSources get bound to the
|
||||
* same JNDI names as within a Java EE container, both application code and
|
||||
* configuration can be reused without changes.
|
||||
*
|
||||
*/
|
||||
package org.springframework.mock.jndi;
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.mock.web;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import javax.servlet.ServletInputStream;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Delegating implementation of {@link javax.servlet.ServletInputStream}.
|
||||
*
|
||||
* <p>Used by {@link MockHttpServletRequest}; typically not directly
|
||||
* used for testing application controllers.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.0.2
|
||||
* @see MockHttpServletRequest
|
||||
*/
|
||||
public class DelegatingServletInputStream extends ServletInputStream {
|
||||
|
||||
private final InputStream sourceStream;
|
||||
|
||||
|
||||
/**
|
||||
* Create a DelegatingServletInputStream for the given source stream.
|
||||
* @param sourceStream the source stream (never <code>null</code>)
|
||||
*/
|
||||
public DelegatingServletInputStream(InputStream sourceStream) {
|
||||
Assert.notNull(sourceStream, "Source InputStream must not be null");
|
||||
this.sourceStream = sourceStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying source stream (never <code>null</code>).
|
||||
*/
|
||||
public final InputStream getSourceStream() {
|
||||
return this.sourceStream;
|
||||
}
|
||||
|
||||
|
||||
public int read() throws IOException {
|
||||
return this.sourceStream.read();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
this.sourceStream.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.mock.web;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Delegating implementation of {@link javax.servlet.ServletOutputStream}.
|
||||
*
|
||||
* <p>Used by {@link MockHttpServletResponse}; typically not directly
|
||||
* used for testing application controllers.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.0.2
|
||||
* @see MockHttpServletResponse
|
||||
*/
|
||||
public class DelegatingServletOutputStream extends ServletOutputStream {
|
||||
|
||||
private final OutputStream targetStream;
|
||||
|
||||
|
||||
/**
|
||||
* Create a DelegatingServletOutputStream for the given target stream.
|
||||
* @param targetStream the target stream (never <code>null</code>)
|
||||
*/
|
||||
public DelegatingServletOutputStream(OutputStream targetStream) {
|
||||
Assert.notNull(targetStream, "Target OutputStream must not be null");
|
||||
this.targetStream = targetStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying target stream (never <code>null</code>).
|
||||
*/
|
||||
public final OutputStream getTargetStream() {
|
||||
return this.targetStream;
|
||||
}
|
||||
|
||||
|
||||
public void write(int b) throws IOException {
|
||||
this.targetStream.write(b);
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
super.flush();
|
||||
this.targetStream.flush();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
this.targetStream.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.mock.web;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Internal helper class that serves as value holder for request headers.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rick Evans
|
||||
* @since 2.0.1
|
||||
*/
|
||||
class HeaderValueHolder {
|
||||
|
||||
private final List<Object> values = new LinkedList<Object>();
|
||||
|
||||
|
||||
public void setValue(Object value) {
|
||||
this.values.clear();
|
||||
this.values.add(value);
|
||||
}
|
||||
|
||||
public void addValue(Object value) {
|
||||
this.values.add(value);
|
||||
}
|
||||
|
||||
public void addValues(Collection<?> values) {
|
||||
this.values.addAll(values);
|
||||
}
|
||||
|
||||
public void addValueArray(Object values) {
|
||||
CollectionUtils.mergeArrayIntoCollection(values, this.values);
|
||||
}
|
||||
|
||||
public List<Object> getValues() {
|
||||
return Collections.unmodifiableList(this.values);
|
||||
}
|
||||
|
||||
public List<String> getStringValues() {
|
||||
List<String> stringList = new ArrayList<String>(this.values.size());
|
||||
for (Object value : this.values) {
|
||||
stringList.add(value.toString());
|
||||
}
|
||||
return Collections.unmodifiableList(stringList);
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return (!this.values.isEmpty() ? this.values.get(0) : null);
|
||||
}
|
||||
|
||||
public String getStringValue() {
|
||||
return (!this.values.isEmpty() ? this.values.get(0).toString() : null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find a HeaderValueHolder by name, ignoring casing.
|
||||
* @param headers the Map of header names to HeaderValueHolders
|
||||
* @param name the name of the desired header
|
||||
* @return the corresponding HeaderValueHolder,
|
||||
* or <code>null</code> if none found
|
||||
*/
|
||||
public static HeaderValueHolder getByName(Map<String, HeaderValueHolder> headers, String name) {
|
||||
Assert.notNull(name, "Header name must not be null");
|
||||
for (String headerName : headers.keySet()) {
|
||||
if (headerName.equalsIgnoreCase(name)) {
|
||||
return headers.get(headerName);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* 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.mock.web;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.io.Writer;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.jsp.JspWriter;
|
||||
import javax.servlet.jsp.tagext.BodyContent;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.servlet.jsp.tagext.BodyContent} class.
|
||||
*
|
||||
* <p>Used for testing the web framework; only necessary for testing
|
||||
* applications when testing custom JSP tags.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
*/
|
||||
public class MockBodyContent extends BodyContent {
|
||||
|
||||
private final String content;
|
||||
|
||||
|
||||
/**
|
||||
* Create a MockBodyContent for the given response.
|
||||
* @param content the body content to expose
|
||||
* @param response the servlet response to wrap
|
||||
*/
|
||||
public MockBodyContent(String content, HttpServletResponse response) {
|
||||
this(content, response, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a MockBodyContent for the given response.
|
||||
* @param content the body content to expose
|
||||
* @param targetWriter the target Writer to wrap
|
||||
*/
|
||||
public MockBodyContent(String content, Writer targetWriter) {
|
||||
this(content, null, targetWriter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a MockBodyContent for the given response.
|
||||
* @param content the body content to expose
|
||||
* @param response the servlet response to wrap
|
||||
* @param targetWriter the target Writer to wrap
|
||||
*/
|
||||
public MockBodyContent(String content, HttpServletResponse response, Writer targetWriter) {
|
||||
super(adaptJspWriter(targetWriter, response));
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
private static JspWriter adaptJspWriter(Writer targetWriter, HttpServletResponse response) {
|
||||
if (targetWriter instanceof JspWriter) {
|
||||
return (JspWriter) targetWriter;
|
||||
}
|
||||
else {
|
||||
return new MockJspWriter(response, targetWriter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Reader getReader() {
|
||||
return new StringReader(this.content);
|
||||
}
|
||||
|
||||
public String getString() {
|
||||
return this.content;
|
||||
}
|
||||
|
||||
public void writeOut(Writer writer) throws IOException {
|
||||
writer.write(this.content);
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Delegating implementations of JspWriter's abstract methods
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
public void clear() throws IOException {
|
||||
getEnclosingWriter().clear();
|
||||
}
|
||||
|
||||
public void clearBuffer() throws IOException {
|
||||
getEnclosingWriter().clearBuffer();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
getEnclosingWriter().close();
|
||||
}
|
||||
|
||||
public int getRemaining() {
|
||||
return getEnclosingWriter().getRemaining();
|
||||
}
|
||||
|
||||
public void newLine() throws IOException {
|
||||
getEnclosingWriter().println();
|
||||
}
|
||||
|
||||
public void write(char value[], int offset, int length) throws IOException {
|
||||
getEnclosingWriter().write(value, offset, length);
|
||||
}
|
||||
|
||||
public void print(boolean value) throws IOException {
|
||||
getEnclosingWriter().print(value);
|
||||
}
|
||||
|
||||
public void print(char value) throws IOException {
|
||||
getEnclosingWriter().print(value);
|
||||
}
|
||||
|
||||
public void print(char[] value) throws IOException {
|
||||
getEnclosingWriter().print(value);
|
||||
}
|
||||
|
||||
public void print(double value) throws IOException {
|
||||
getEnclosingWriter().print(value);
|
||||
}
|
||||
|
||||
public void print(float value) throws IOException {
|
||||
getEnclosingWriter().print(value);
|
||||
}
|
||||
|
||||
public void print(int value) throws IOException {
|
||||
getEnclosingWriter().print(value);
|
||||
}
|
||||
|
||||
public void print(long value) throws IOException {
|
||||
getEnclosingWriter().print(value);
|
||||
}
|
||||
|
||||
public void print(Object value) throws IOException {
|
||||
getEnclosingWriter().print(value);
|
||||
}
|
||||
|
||||
public void print(String value) throws IOException {
|
||||
getEnclosingWriter().print(value);
|
||||
}
|
||||
|
||||
public void println() throws IOException {
|
||||
getEnclosingWriter().println();
|
||||
}
|
||||
|
||||
public void println(boolean value) throws IOException {
|
||||
getEnclosingWriter().println(value);
|
||||
}
|
||||
|
||||
public void println(char value) throws IOException {
|
||||
getEnclosingWriter().println(value);
|
||||
}
|
||||
|
||||
public void println(char[] value) throws IOException {
|
||||
getEnclosingWriter().println(value);
|
||||
}
|
||||
|
||||
public void println(double value) throws IOException {
|
||||
getEnclosingWriter().println(value);
|
||||
}
|
||||
|
||||
public void println(float value) throws IOException {
|
||||
getEnclosingWriter().println(value);
|
||||
}
|
||||
|
||||
public void println(int value) throws IOException {
|
||||
getEnclosingWriter().println(value);
|
||||
}
|
||||
|
||||
public void println(long value) throws IOException {
|
||||
getEnclosingWriter().println(value);
|
||||
}
|
||||
|
||||
public void println(Object value) throws IOException {
|
||||
getEnclosingWriter().println(value);
|
||||
}
|
||||
|
||||
public void println(String value) throws IOException {
|
||||
getEnclosingWriter().println(value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.mock.web;
|
||||
|
||||
import javax.servlet.jsp.JspException;
|
||||
import javax.servlet.jsp.PageContext;
|
||||
import javax.servlet.jsp.el.ELException;
|
||||
import javax.servlet.jsp.el.Expression;
|
||||
import javax.servlet.jsp.el.ExpressionEvaluator;
|
||||
import javax.servlet.jsp.el.FunctionMapper;
|
||||
import javax.servlet.jsp.el.VariableResolver;
|
||||
|
||||
import org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager;
|
||||
|
||||
/**
|
||||
* Mock implementation of the JSP 2.0
|
||||
* {@link javax.servlet.jsp.el.ExpressionEvaluator} interface, delegating to the
|
||||
* Jakarta JSTL ExpressionEvaluatorManager.
|
||||
* <p>
|
||||
* Used for testing the web framework; only necessary for testing applications
|
||||
* when testing custom JSP tags.
|
||||
* <p>
|
||||
* Note that the Jakarta JSTL implementation (jstl.jar, standard.jar) has to be
|
||||
* available on the class path to use this expression evaluator.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1.5
|
||||
* @see org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class MockExpressionEvaluator extends ExpressionEvaluator {
|
||||
|
||||
private final PageContext pageContext;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MockExpressionEvaluator for the given PageContext.
|
||||
*
|
||||
* @param pageContext the JSP PageContext to run in
|
||||
*/
|
||||
public MockExpressionEvaluator(PageContext pageContext) {
|
||||
this.pageContext = pageContext;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Expression parseExpression(final String expression, final Class expectedType,
|
||||
final FunctionMapper functionMapper) throws ELException {
|
||||
|
||||
return new Expression() {
|
||||
|
||||
public Object evaluate(VariableResolver variableResolver) throws ELException {
|
||||
return doEvaluate(expression, expectedType, functionMapper);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Object evaluate(String expression, Class expectedType, VariableResolver variableResolver,
|
||||
FunctionMapper functionMapper) throws ELException {
|
||||
|
||||
if (variableResolver != null) {
|
||||
throw new IllegalArgumentException("Custom VariableResolver not supported");
|
||||
}
|
||||
return doEvaluate(expression, expectedType, functionMapper);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
protected Object doEvaluate(String expression, Class expectedType, FunctionMapper functionMapper)
|
||||
throws ELException {
|
||||
|
||||
if (functionMapper != null) {
|
||||
throw new IllegalArgumentException("Custom FunctionMapper not supported");
|
||||
}
|
||||
try {
|
||||
return ExpressionEvaluatorManager.evaluate("JSP EL expression", expression, expectedType, this.pageContext);
|
||||
}
|
||||
catch (JspException ex) {
|
||||
throw new ELException("Parsing of JSP EL expression \"" + expression + "\" failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.mock.web;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.servlet.FilterConfig} interface.
|
||||
*
|
||||
* <p>Used for testing the web framework; also useful for testing
|
||||
* custom {@link javax.servlet.Filter} implementations.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0.3
|
||||
* @see MockFilterConfig
|
||||
* @see PassThroughFilterChain
|
||||
*/
|
||||
public class MockFilterChain implements FilterChain {
|
||||
|
||||
private ServletRequest request;
|
||||
|
||||
private ServletResponse response;
|
||||
|
||||
|
||||
/**
|
||||
* Records the request and response.
|
||||
*/
|
||||
public void doFilter(ServletRequest request, ServletResponse response) {
|
||||
Assert.notNull(request, "Request must not be null");
|
||||
Assert.notNull(response, "Response must not be null");
|
||||
if (this.request != null) {
|
||||
throw new IllegalStateException("This FilterChain has already been called!");
|
||||
}
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the request that {@link #doFilter} has been called with.
|
||||
*/
|
||||
public ServletRequest getRequest() {
|
||||
return this.request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the response that {@link #doFilter} has been called with.
|
||||
*/
|
||||
public ServletResponse getResponse() {
|
||||
return this.response;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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.mock.web;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.servlet.FilterConfig} interface.
|
||||
*
|
||||
* <p>Used for testing the web framework; also useful for testing
|
||||
* custom {@link javax.servlet.Filter} implementations.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.0.2
|
||||
* @see MockFilterChain
|
||||
* @see PassThroughFilterChain
|
||||
*/
|
||||
public class MockFilterConfig implements FilterConfig {
|
||||
|
||||
private final ServletContext servletContext;
|
||||
|
||||
private final String filterName;
|
||||
|
||||
private final Map<String, String> initParameters = new LinkedHashMap<String, String>();
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MockFilterConfig with a default {@link MockServletContext}.
|
||||
*/
|
||||
public MockFilterConfig() {
|
||||
this(null, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockFilterConfig with a default {@link MockServletContext}.
|
||||
* @param filterName the name of the filter
|
||||
*/
|
||||
public MockFilterConfig(String filterName) {
|
||||
this(null, filterName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockFilterConfig.
|
||||
* @param servletContext the ServletContext that the servlet runs in
|
||||
*/
|
||||
public MockFilterConfig(ServletContext servletContext) {
|
||||
this(servletContext, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockFilterConfig.
|
||||
* @param servletContext the ServletContext that the servlet runs in
|
||||
* @param filterName the name of the filter
|
||||
*/
|
||||
public MockFilterConfig(ServletContext servletContext, String filterName) {
|
||||
this.servletContext = (servletContext != null ? servletContext : new MockServletContext());
|
||||
this.filterName = filterName;
|
||||
}
|
||||
|
||||
|
||||
public String getFilterName() {
|
||||
return filterName;
|
||||
}
|
||||
|
||||
public ServletContext getServletContext() {
|
||||
return servletContext;
|
||||
}
|
||||
|
||||
public void addInitParameter(String name, String value) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
this.initParameters.put(name, value);
|
||||
}
|
||||
|
||||
public String getInitParameter(String name) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
return this.initParameters.get(name);
|
||||
}
|
||||
|
||||
public Enumeration<String> getInitParameterNames() {
|
||||
return Collections.enumeration(this.initParameters.keySet());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,904 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.mock.web;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.Principal;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Vector;
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletInputStream;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.LinkedCaseInsensitiveMap;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.servlet.http.HttpServletRequest} interface.
|
||||
*
|
||||
* <p>Compatible with Servlet 2.5 and partially with Servlet 3.0 (notable exceptions:
|
||||
* the <code>getPart(s)</code> and <code>startAsync</code> families of methods).
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rod Johnson
|
||||
* @author Rick Evans
|
||||
* @author Mark Fisher
|
||||
* @since 1.0.2
|
||||
*/
|
||||
public class MockHttpServletRequest implements HttpServletRequest {
|
||||
|
||||
/**
|
||||
* The default protocol: 'http'.
|
||||
*/
|
||||
public static final String DEFAULT_PROTOCOL = "http";
|
||||
|
||||
/**
|
||||
* The default server address: '127.0.0.1'.
|
||||
*/
|
||||
public static final String DEFAULT_SERVER_ADDR = "127.0.0.1";
|
||||
|
||||
/**
|
||||
* The default server name: 'localhost'.
|
||||
*/
|
||||
public static final String DEFAULT_SERVER_NAME = "localhost";
|
||||
|
||||
/**
|
||||
* The default server port: '80'.
|
||||
*/
|
||||
public static final int DEFAULT_SERVER_PORT = 80;
|
||||
|
||||
/**
|
||||
* The default remote address: '127.0.0.1'.
|
||||
*/
|
||||
public static final String DEFAULT_REMOTE_ADDR = "127.0.0.1";
|
||||
|
||||
/**
|
||||
* The default remote host: 'localhost'.
|
||||
*/
|
||||
public static final String DEFAULT_REMOTE_HOST = "localhost";
|
||||
|
||||
private static final String CONTENT_TYPE_HEADER = "Content-Type";
|
||||
|
||||
private static final String CHARSET_PREFIX = "charset=";
|
||||
|
||||
|
||||
private boolean active = true;
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// ServletRequest properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
private final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
|
||||
|
||||
private String characterEncoding;
|
||||
|
||||
private byte[] content;
|
||||
|
||||
private String contentType;
|
||||
|
||||
private final Map<String, String[]> parameters = new LinkedHashMap<String, String[]>(16);
|
||||
|
||||
private String protocol = DEFAULT_PROTOCOL;
|
||||
|
||||
private String scheme = DEFAULT_PROTOCOL;
|
||||
|
||||
private String serverName = DEFAULT_SERVER_NAME;
|
||||
|
||||
private int serverPort = DEFAULT_SERVER_PORT;
|
||||
|
||||
private String remoteAddr = DEFAULT_REMOTE_ADDR;
|
||||
|
||||
private String remoteHost = DEFAULT_REMOTE_HOST;
|
||||
|
||||
/** List of locales in descending order */
|
||||
private final List<Locale> locales = new LinkedList<Locale>();
|
||||
|
||||
private boolean secure = false;
|
||||
|
||||
private final ServletContext servletContext;
|
||||
|
||||
private int remotePort = DEFAULT_SERVER_PORT;
|
||||
|
||||
private String localName = DEFAULT_SERVER_NAME;
|
||||
|
||||
private String localAddr = DEFAULT_SERVER_ADDR;
|
||||
|
||||
private int localPort = DEFAULT_SERVER_PORT;
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// HttpServletRequest properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
private String authType;
|
||||
|
||||
private Cookie[] cookies;
|
||||
|
||||
private final Map<String, HeaderValueHolder> headers = new LinkedCaseInsensitiveMap<HeaderValueHolder>();
|
||||
|
||||
private String method;
|
||||
|
||||
private String pathInfo;
|
||||
|
||||
private String contextPath = "";
|
||||
|
||||
private String queryString;
|
||||
|
||||
private String remoteUser;
|
||||
|
||||
private final Set<String> userRoles = new HashSet<String>();
|
||||
|
||||
private Principal userPrincipal;
|
||||
|
||||
private String requestedSessionId;
|
||||
|
||||
private String requestURI;
|
||||
|
||||
private String servletPath = "";
|
||||
|
||||
private HttpSession session;
|
||||
|
||||
private boolean requestedSessionIdValid = true;
|
||||
|
||||
private boolean requestedSessionIdFromCookie = true;
|
||||
|
||||
private boolean requestedSessionIdFromURL = false;
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Constructors
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Create a new MockHttpServletRequest with a default
|
||||
* {@link MockServletContext}.
|
||||
* @see MockServletContext
|
||||
*/
|
||||
public MockHttpServletRequest() {
|
||||
this(null, "", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockHttpServletRequest with a default
|
||||
* {@link MockServletContext}.
|
||||
* @param method the request method (may be <code>null</code>)
|
||||
* @param requestURI the request URI (may be <code>null</code>)
|
||||
* @see #setMethod
|
||||
* @see #setRequestURI
|
||||
* @see MockServletContext
|
||||
*/
|
||||
public MockHttpServletRequest(String method, String requestURI) {
|
||||
this(null, method, requestURI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockHttpServletRequest.
|
||||
* @param servletContext the ServletContext that the request runs in (may be
|
||||
* <code>null</code> to use a default MockServletContext)
|
||||
* @see MockServletContext
|
||||
*/
|
||||
public MockHttpServletRequest(ServletContext servletContext) {
|
||||
this(servletContext, "", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockHttpServletRequest.
|
||||
* @param servletContext the ServletContext that the request runs in (may be
|
||||
* <code>null</code> to use a default MockServletContext)
|
||||
* @param method the request method (may be <code>null</code>)
|
||||
* @param requestURI the request URI (may be <code>null</code>)
|
||||
* @see #setMethod
|
||||
* @see #setRequestURI
|
||||
* @see MockServletContext
|
||||
*/
|
||||
public MockHttpServletRequest(ServletContext servletContext, String method, String requestURI) {
|
||||
this.servletContext = (servletContext != null ? servletContext : new MockServletContext());
|
||||
this.method = method;
|
||||
this.requestURI = requestURI;
|
||||
this.locales.add(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Lifecycle methods
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Return the ServletContext that this request is associated with. (Not
|
||||
* available in the standard HttpServletRequest interface for some reason.)
|
||||
*/
|
||||
public ServletContext getServletContext() {
|
||||
return this.servletContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this request is still active (that is, not completed yet).
|
||||
*/
|
||||
public boolean isActive() {
|
||||
return this.active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this request as completed, keeping its state.
|
||||
*/
|
||||
public void close() {
|
||||
this.active = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate this request, clearing its state.
|
||||
*/
|
||||
public void invalidate() {
|
||||
close();
|
||||
clearAttributes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this request is still active (that is, not completed yet),
|
||||
* throwing an IllegalStateException if not active anymore.
|
||||
*/
|
||||
protected void checkActive() throws IllegalStateException {
|
||||
if (!this.active) {
|
||||
throw new IllegalStateException("Request is not active anymore");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// ServletRequest interface
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
public Object getAttribute(String name) {
|
||||
checkActive();
|
||||
return this.attributes.get(name);
|
||||
}
|
||||
|
||||
public Enumeration<String> getAttributeNames() {
|
||||
checkActive();
|
||||
return new Vector<String>(this.attributes.keySet()).elements();
|
||||
}
|
||||
|
||||
public String getCharacterEncoding() {
|
||||
return this.characterEncoding;
|
||||
}
|
||||
|
||||
public void setCharacterEncoding(String characterEncoding) {
|
||||
this.characterEncoding = characterEncoding;
|
||||
updateContentTypeHeader();
|
||||
}
|
||||
|
||||
private void updateContentTypeHeader() {
|
||||
if (this.contentType != null) {
|
||||
StringBuilder sb = new StringBuilder(this.contentType);
|
||||
if (!this.contentType.toLowerCase().contains(CHARSET_PREFIX) && this.characterEncoding != null) {
|
||||
sb.append(";").append(CHARSET_PREFIX).append(this.characterEncoding);
|
||||
}
|
||||
doAddHeaderValue(CONTENT_TYPE_HEADER, sb.toString(), true);
|
||||
}
|
||||
}
|
||||
|
||||
public void setContent(byte[] content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public int getContentLength() {
|
||||
return (this.content != null ? this.content.length : -1);
|
||||
}
|
||||
|
||||
public void setContentType(String contentType) {
|
||||
this.contentType = contentType;
|
||||
if (contentType != null) {
|
||||
int charsetIndex = contentType.toLowerCase().indexOf(CHARSET_PREFIX);
|
||||
if (charsetIndex != -1) {
|
||||
String encoding = contentType.substring(charsetIndex + CHARSET_PREFIX.length());
|
||||
this.characterEncoding = encoding;
|
||||
}
|
||||
updateContentTypeHeader();
|
||||
}
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return this.contentType;
|
||||
}
|
||||
|
||||
public ServletInputStream getInputStream() {
|
||||
if (this.content != null) {
|
||||
return new DelegatingServletInputStream(new ByteArrayInputStream(this.content));
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a single value for the specified HTTP parameter.
|
||||
* <p>
|
||||
* If there are already one or more values registered for the given
|
||||
* parameter name, they will be replaced.
|
||||
*/
|
||||
public void setParameter(String name, String value) {
|
||||
setParameter(name, new String[] { value });
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an array of values for the specified HTTP parameter.
|
||||
* <p>
|
||||
* If there are already one or more values registered for the given
|
||||
* parameter name, they will be replaced.
|
||||
*/
|
||||
public void setParameter(String name, String[] values) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
this.parameters.put(name, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all provided parameters <emphasis>replacing</emphasis> any existing
|
||||
* values for the provided parameter names. To add without replacing
|
||||
* existing values, use {@link #addParameters(java.util.Map)}.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void setParameters(Map params) {
|
||||
Assert.notNull(params, "Parameter map must not be null");
|
||||
for (Object key : params.keySet()) {
|
||||
Assert.isInstanceOf(String.class, key, "Parameter map key must be of type [" + String.class.getName() + "]");
|
||||
Object value = params.get(key);
|
||||
if (value instanceof String) {
|
||||
this.setParameter((String) key, (String) value);
|
||||
}
|
||||
else if (value instanceof String[]) {
|
||||
this.setParameter((String) key, (String[]) value);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Parameter map value must be single value " + " or array of type ["
|
||||
+ String.class.getName() + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single value for the specified HTTP parameter.
|
||||
* <p>
|
||||
* If there are already one or more values registered for the given
|
||||
* parameter name, the given value will be added to the end of the list.
|
||||
*/
|
||||
public void addParameter(String name, String value) {
|
||||
addParameter(name, new String[] { value });
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an array of values for the specified HTTP parameter.
|
||||
* <p>
|
||||
* If there are already one or more values registered for the given
|
||||
* parameter name, the given values will be added to the end of the list.
|
||||
*/
|
||||
public void addParameter(String name, String[] values) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
String[] oldArr = this.parameters.get(name);
|
||||
if (oldArr != null) {
|
||||
String[] newArr = new String[oldArr.length + values.length];
|
||||
System.arraycopy(oldArr, 0, newArr, 0, oldArr.length);
|
||||
System.arraycopy(values, 0, newArr, oldArr.length, values.length);
|
||||
this.parameters.put(name, newArr);
|
||||
}
|
||||
else {
|
||||
this.parameters.put(name, values);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds all provided parameters <emphasis>without</emphasis> replacing any
|
||||
* existing values. To replace existing values, use
|
||||
* {@link #setParameters(java.util.Map)}.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void addParameters(Map params) {
|
||||
Assert.notNull(params, "Parameter map must not be null");
|
||||
for (Object key : params.keySet()) {
|
||||
Assert.isInstanceOf(String.class, key, "Parameter map key must be of type [" + String.class.getName() + "]");
|
||||
Object value = params.get(key);
|
||||
if (value instanceof String) {
|
||||
this.addParameter((String) key, (String) value);
|
||||
}
|
||||
else if (value instanceof String[]) {
|
||||
this.addParameter((String) key, (String[]) value);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Parameter map value must be single value " + " or array of type ["
|
||||
+ String.class.getName() + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove already registered values for the specified HTTP parameter, if
|
||||
* any.
|
||||
*/
|
||||
public void removeParameter(String name) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
this.parameters.remove(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all existing parameters.
|
||||
*/
|
||||
public void removeAllParameters() {
|
||||
this.parameters.clear();
|
||||
}
|
||||
|
||||
public String getParameter(String name) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
String[] arr = this.parameters.get(name);
|
||||
return (arr != null && arr.length > 0 ? arr[0] : null);
|
||||
}
|
||||
|
||||
public Enumeration<String> getParameterNames() {
|
||||
return Collections.enumeration(this.parameters.keySet());
|
||||
}
|
||||
|
||||
public String[] getParameterValues(String name) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
return this.parameters.get(name);
|
||||
}
|
||||
|
||||
public Map<String, String[]> getParameterMap() {
|
||||
return Collections.unmodifiableMap(this.parameters);
|
||||
}
|
||||
|
||||
public void setProtocol(String protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public String getProtocol() {
|
||||
return this.protocol;
|
||||
}
|
||||
|
||||
public void setScheme(String scheme) {
|
||||
this.scheme = scheme;
|
||||
}
|
||||
|
||||
public String getScheme() {
|
||||
return this.scheme;
|
||||
}
|
||||
|
||||
public void setServerName(String serverName) {
|
||||
this.serverName = serverName;
|
||||
}
|
||||
|
||||
public String getServerName() {
|
||||
return this.serverName;
|
||||
}
|
||||
|
||||
public void setServerPort(int serverPort) {
|
||||
this.serverPort = serverPort;
|
||||
}
|
||||
|
||||
public int getServerPort() {
|
||||
return this.serverPort;
|
||||
}
|
||||
|
||||
public BufferedReader getReader() throws UnsupportedEncodingException {
|
||||
if (this.content != null) {
|
||||
InputStream sourceStream = new ByteArrayInputStream(this.content);
|
||||
Reader sourceReader = (this.characterEncoding != null) ? new InputStreamReader(sourceStream,
|
||||
this.characterEncoding) : new InputStreamReader(sourceStream);
|
||||
return new BufferedReader(sourceReader);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setRemoteAddr(String remoteAddr) {
|
||||
this.remoteAddr = remoteAddr;
|
||||
}
|
||||
|
||||
public String getRemoteAddr() {
|
||||
return this.remoteAddr;
|
||||
}
|
||||
|
||||
public void setRemoteHost(String remoteHost) {
|
||||
this.remoteHost = remoteHost;
|
||||
}
|
||||
|
||||
public String getRemoteHost() {
|
||||
return this.remoteHost;
|
||||
}
|
||||
|
||||
public void setAttribute(String name, Object value) {
|
||||
checkActive();
|
||||
Assert.notNull(name, "Attribute name must not be null");
|
||||
if (value != null) {
|
||||
this.attributes.put(name, value);
|
||||
}
|
||||
else {
|
||||
this.attributes.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAttribute(String name) {
|
||||
checkActive();
|
||||
Assert.notNull(name, "Attribute name must not be null");
|
||||
this.attributes.remove(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all of this request's attributes.
|
||||
*/
|
||||
public void clearAttributes() {
|
||||
this.attributes.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new preferred locale, before any existing locales.
|
||||
*/
|
||||
public void addPreferredLocale(Locale locale) {
|
||||
Assert.notNull(locale, "Locale must not be null");
|
||||
this.locales.add(0, locale);
|
||||
}
|
||||
|
||||
public Locale getLocale() {
|
||||
return this.locales.get(0);
|
||||
}
|
||||
|
||||
public Enumeration<Locale> getLocales() {
|
||||
return Collections.enumeration(this.locales);
|
||||
}
|
||||
|
||||
public void setSecure(boolean secure) {
|
||||
this.secure = secure;
|
||||
}
|
||||
|
||||
public boolean isSecure() {
|
||||
return this.secure;
|
||||
}
|
||||
|
||||
public RequestDispatcher getRequestDispatcher(String path) {
|
||||
return new MockRequestDispatcher(path);
|
||||
}
|
||||
|
||||
public String getRealPath(String path) {
|
||||
return this.servletContext.getRealPath(path);
|
||||
}
|
||||
|
||||
public void setRemotePort(int remotePort) {
|
||||
this.remotePort = remotePort;
|
||||
}
|
||||
|
||||
public int getRemotePort() {
|
||||
return this.remotePort;
|
||||
}
|
||||
|
||||
public void setLocalName(String localName) {
|
||||
this.localName = localName;
|
||||
}
|
||||
|
||||
public String getLocalName() {
|
||||
return this.localName;
|
||||
}
|
||||
|
||||
public void setLocalAddr(String localAddr) {
|
||||
this.localAddr = localAddr;
|
||||
}
|
||||
|
||||
public String getLocalAddr() {
|
||||
return this.localAddr;
|
||||
}
|
||||
|
||||
public void setLocalPort(int localPort) {
|
||||
this.localPort = localPort;
|
||||
}
|
||||
|
||||
public int getLocalPort() {
|
||||
return this.localPort;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// HttpServletRequest interface
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
public void setAuthType(String authType) {
|
||||
this.authType = authType;
|
||||
}
|
||||
|
||||
public String getAuthType() {
|
||||
return this.authType;
|
||||
}
|
||||
|
||||
public void setCookies(Cookie... cookies) {
|
||||
this.cookies = cookies;
|
||||
}
|
||||
|
||||
public Cookie[] getCookies() {
|
||||
return this.cookies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a header entry for the given name.
|
||||
* <p>If there was no entry for that header name before, the value will be used
|
||||
* as-is. In case of an existing entry, a String array will be created,
|
||||
* adding the given value (more specifically, its toString representation)
|
||||
* as further element.
|
||||
* <p>Multiple values can only be stored as list of Strings, following the
|
||||
* Servlet spec (see <code>getHeaders</code> accessor). As alternative to
|
||||
* repeated <code>addHeader</code> calls for individual elements, you can
|
||||
* use a single call with an entire array or Collection of values as
|
||||
* parameter.
|
||||
* @see #getHeaderNames
|
||||
* @see #getHeader
|
||||
* @see #getHeaders
|
||||
* @see #getDateHeader
|
||||
* @see #getIntHeader
|
||||
*/
|
||||
public void addHeader(String name, Object value) {
|
||||
if (CONTENT_TYPE_HEADER.equalsIgnoreCase(name)) {
|
||||
setContentType((String) value);
|
||||
return;
|
||||
}
|
||||
doAddHeaderValue(name, value, false);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private void doAddHeaderValue(String name, Object value, boolean replace) {
|
||||
HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
|
||||
Assert.notNull(value, "Header value must not be null");
|
||||
if (header == null || replace) {
|
||||
header = new HeaderValueHolder();
|
||||
this.headers.put(name, header);
|
||||
}
|
||||
if (value instanceof Collection) {
|
||||
header.addValues((Collection) value);
|
||||
}
|
||||
else if (value.getClass().isArray()) {
|
||||
header.addValueArray(value);
|
||||
}
|
||||
else {
|
||||
header.addValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
public long getDateHeader(String name) {
|
||||
HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
|
||||
Object value = (header != null ? header.getValue() : null);
|
||||
if (value instanceof Date) {
|
||||
return ((Date) value).getTime();
|
||||
}
|
||||
else if (value instanceof Number) {
|
||||
return ((Number) value).longValue();
|
||||
}
|
||||
else if (value != null) {
|
||||
throw new IllegalArgumentException("Value for header '" + name + "' is neither a Date nor a Number: "
|
||||
+ value);
|
||||
}
|
||||
else {
|
||||
return -1L;
|
||||
}
|
||||
}
|
||||
|
||||
public int getIntHeader(String name) {
|
||||
HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
|
||||
Object value = (header != null ? header.getValue() : null);
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).intValue();
|
||||
}
|
||||
else if (value instanceof String) {
|
||||
return Integer.parseInt((String) value);
|
||||
}
|
||||
else if (value != null) {
|
||||
throw new NumberFormatException("Value for header '" + name + "' is not a Number: " + value);
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public String getHeader(String name) {
|
||||
HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
|
||||
return (header != null ? header.getStringValue() : null);
|
||||
}
|
||||
|
||||
public Enumeration<String> getHeaders(String name) {
|
||||
HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
|
||||
return Collections.enumeration(header != null ? header.getStringValues() : new LinkedList<String>());
|
||||
}
|
||||
|
||||
public Enumeration<String> getHeaderNames() {
|
||||
return Collections.enumeration(this.headers.keySet());
|
||||
}
|
||||
|
||||
public void setMethod(String method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public String getMethod() {
|
||||
return this.method;
|
||||
}
|
||||
|
||||
public void setPathInfo(String pathInfo) {
|
||||
this.pathInfo = pathInfo;
|
||||
}
|
||||
|
||||
public String getPathInfo() {
|
||||
return this.pathInfo;
|
||||
}
|
||||
|
||||
public String getPathTranslated() {
|
||||
return (this.pathInfo != null ? getRealPath(this.pathInfo) : null);
|
||||
}
|
||||
|
||||
public void setContextPath(String contextPath) {
|
||||
this.contextPath = contextPath;
|
||||
}
|
||||
|
||||
public String getContextPath() {
|
||||
return this.contextPath;
|
||||
}
|
||||
|
||||
public void setQueryString(String queryString) {
|
||||
this.queryString = queryString;
|
||||
}
|
||||
|
||||
public String getQueryString() {
|
||||
return this.queryString;
|
||||
}
|
||||
|
||||
public void setRemoteUser(String remoteUser) {
|
||||
this.remoteUser = remoteUser;
|
||||
}
|
||||
|
||||
public String getRemoteUser() {
|
||||
return this.remoteUser;
|
||||
}
|
||||
|
||||
public void addUserRole(String role) {
|
||||
this.userRoles.add(role);
|
||||
}
|
||||
|
||||
public boolean isUserInRole(String role) {
|
||||
return (this.userRoles.contains(role) || (this.servletContext instanceof MockServletContext &&
|
||||
((MockServletContext) this.servletContext).getDeclaredRoles().contains(role)));
|
||||
}
|
||||
|
||||
public void setUserPrincipal(Principal userPrincipal) {
|
||||
this.userPrincipal = userPrincipal;
|
||||
}
|
||||
|
||||
public Principal getUserPrincipal() {
|
||||
return this.userPrincipal;
|
||||
}
|
||||
|
||||
public void setRequestedSessionId(String requestedSessionId) {
|
||||
this.requestedSessionId = requestedSessionId;
|
||||
}
|
||||
|
||||
public String getRequestedSessionId() {
|
||||
return this.requestedSessionId;
|
||||
}
|
||||
|
||||
public void setRequestURI(String requestURI) {
|
||||
this.requestURI = requestURI;
|
||||
}
|
||||
|
||||
public String getRequestURI() {
|
||||
return this.requestURI;
|
||||
}
|
||||
|
||||
public StringBuffer getRequestURL() {
|
||||
StringBuffer url = new StringBuffer(this.scheme);
|
||||
url.append("://").append(this.serverName).append(':').append(this.serverPort);
|
||||
url.append(getRequestURI());
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setServletPath(String servletPath) {
|
||||
this.servletPath = servletPath;
|
||||
}
|
||||
|
||||
public String getServletPath() {
|
||||
return this.servletPath;
|
||||
}
|
||||
|
||||
public void setSession(HttpSession session) {
|
||||
this.session = session;
|
||||
if (session instanceof MockHttpSession) {
|
||||
MockHttpSession mockSession = ((MockHttpSession) session);
|
||||
mockSession.access();
|
||||
}
|
||||
}
|
||||
|
||||
public HttpSession getSession(boolean create) {
|
||||
checkActive();
|
||||
// Reset session if invalidated.
|
||||
if (this.session instanceof MockHttpSession && ((MockHttpSession) this.session).isInvalid()) {
|
||||
this.session = null;
|
||||
}
|
||||
// Create new session if necessary.
|
||||
if (this.session == null && create) {
|
||||
this.session = new MockHttpSession(this.servletContext);
|
||||
}
|
||||
return this.session;
|
||||
}
|
||||
|
||||
public HttpSession getSession() {
|
||||
return getSession(true);
|
||||
}
|
||||
|
||||
public void setRequestedSessionIdValid(boolean requestedSessionIdValid) {
|
||||
this.requestedSessionIdValid = requestedSessionIdValid;
|
||||
}
|
||||
|
||||
public boolean isRequestedSessionIdValid() {
|
||||
return this.requestedSessionIdValid;
|
||||
}
|
||||
|
||||
public void setRequestedSessionIdFromCookie(boolean requestedSessionIdFromCookie) {
|
||||
this.requestedSessionIdFromCookie = requestedSessionIdFromCookie;
|
||||
}
|
||||
|
||||
public boolean isRequestedSessionIdFromCookie() {
|
||||
return this.requestedSessionIdFromCookie;
|
||||
}
|
||||
|
||||
public void setRequestedSessionIdFromURL(boolean requestedSessionIdFromURL) {
|
||||
this.requestedSessionIdFromURL = requestedSessionIdFromURL;
|
||||
}
|
||||
|
||||
public boolean isRequestedSessionIdFromURL() {
|
||||
return this.requestedSessionIdFromURL;
|
||||
}
|
||||
|
||||
public boolean isRequestedSessionIdFromUrl() {
|
||||
return isRequestedSessionIdFromURL();
|
||||
}
|
||||
|
||||
public boolean authenticate(HttpServletResponse response) throws IOException, ServletException {
|
||||
return (this.userPrincipal != null && this.remoteUser != null && this.authType != null);
|
||||
}
|
||||
|
||||
public void login(String username, String password) throws ServletException {
|
||||
throw new ServletException("Username-password authentication not supported - override the login method");
|
||||
}
|
||||
|
||||
public void logout() throws ServletException {
|
||||
this.userPrincipal = null;
|
||||
this.remoteUser = null;
|
||||
this.authType = null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,602 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.mock.web;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.LinkedCaseInsensitiveMap;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.servlet.http.HttpServletResponse} interface.
|
||||
*
|
||||
* <p>Compatible with Servlet 2.5 as well as Servlet 3.0.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rod Johnson
|
||||
* @since 1.0.2
|
||||
*/
|
||||
public class MockHttpServletResponse implements HttpServletResponse {
|
||||
|
||||
private static final String CHARSET_PREFIX = "charset=";
|
||||
|
||||
private static final String CONTENT_TYPE_HEADER = "Content-Type";
|
||||
|
||||
private static final String CONTENT_LENGTH_HEADER = "Content-Length";
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// ServletResponse properties
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
private boolean outputStreamAccessAllowed = true;
|
||||
|
||||
private boolean writerAccessAllowed = true;
|
||||
|
||||
private String characterEncoding = WebUtils.DEFAULT_CHARACTER_ENCODING;
|
||||
|
||||
private boolean charset = false;
|
||||
|
||||
private final ByteArrayOutputStream content = new ByteArrayOutputStream();
|
||||
|
||||
private final ServletOutputStream outputStream = new ResponseServletOutputStream(this.content);
|
||||
|
||||
private PrintWriter writer;
|
||||
|
||||
private int contentLength = 0;
|
||||
|
||||
private String contentType;
|
||||
|
||||
private int bufferSize = 4096;
|
||||
|
||||
private boolean committed;
|
||||
|
||||
private Locale locale = Locale.getDefault();
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// HttpServletResponse properties
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
private final List<Cookie> cookies = new ArrayList<Cookie>();
|
||||
|
||||
private final Map<String, HeaderValueHolder> headers = new LinkedCaseInsensitiveMap<HeaderValueHolder>();
|
||||
|
||||
private int status = HttpServletResponse.SC_OK;
|
||||
|
||||
private String errorMessage;
|
||||
|
||||
private String redirectedUrl;
|
||||
|
||||
private String forwardedUrl;
|
||||
|
||||
private final List<String> includedUrls = new ArrayList<String>();
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// ServletResponse interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Set whether {@link #getOutputStream()} access is allowed.
|
||||
* <p>Default is <code>true</code>.
|
||||
*/
|
||||
public void setOutputStreamAccessAllowed(boolean outputStreamAccessAllowed) {
|
||||
this.outputStreamAccessAllowed = outputStreamAccessAllowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether {@link #getOutputStream()} access is allowed.
|
||||
*/
|
||||
public boolean isOutputStreamAccessAllowed() {
|
||||
return this.outputStreamAccessAllowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether {@link #getWriter()} access is allowed.
|
||||
* <p>Default is <code>true</code>.
|
||||
*/
|
||||
public void setWriterAccessAllowed(boolean writerAccessAllowed) {
|
||||
this.writerAccessAllowed = writerAccessAllowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether {@link #getOutputStream()} access is allowed.
|
||||
*/
|
||||
public boolean isWriterAccessAllowed() {
|
||||
return this.writerAccessAllowed;
|
||||
}
|
||||
|
||||
public void setCharacterEncoding(String characterEncoding) {
|
||||
this.characterEncoding = characterEncoding;
|
||||
this.charset = true;
|
||||
updateContentTypeHeader();
|
||||
}
|
||||
|
||||
private void updateContentTypeHeader() {
|
||||
if (this.contentType != null) {
|
||||
StringBuilder sb = new StringBuilder(this.contentType);
|
||||
if (this.contentType.toLowerCase().indexOf(CHARSET_PREFIX) == -1 && this.charset) {
|
||||
sb.append(";").append(CHARSET_PREFIX).append(this.characterEncoding);
|
||||
}
|
||||
doAddHeaderValue(CONTENT_TYPE_HEADER, sb.toString(), true);
|
||||
}
|
||||
}
|
||||
|
||||
public String getCharacterEncoding() {
|
||||
return this.characterEncoding;
|
||||
}
|
||||
|
||||
public ServletOutputStream getOutputStream() {
|
||||
if (!this.outputStreamAccessAllowed) {
|
||||
throw new IllegalStateException("OutputStream access not allowed");
|
||||
}
|
||||
return this.outputStream;
|
||||
}
|
||||
|
||||
public PrintWriter getWriter() throws UnsupportedEncodingException {
|
||||
if (!this.writerAccessAllowed) {
|
||||
throw new IllegalStateException("Writer access not allowed");
|
||||
}
|
||||
if (this.writer == null) {
|
||||
Writer targetWriter = (this.characterEncoding != null ?
|
||||
new OutputStreamWriter(this.content, this.characterEncoding) : new OutputStreamWriter(this.content));
|
||||
this.writer = new ResponsePrintWriter(targetWriter);
|
||||
}
|
||||
return this.writer;
|
||||
}
|
||||
|
||||
public byte[] getContentAsByteArray() {
|
||||
flushBuffer();
|
||||
return this.content.toByteArray();
|
||||
}
|
||||
|
||||
public String getContentAsString() throws UnsupportedEncodingException {
|
||||
flushBuffer();
|
||||
return (this.characterEncoding != null) ?
|
||||
this.content.toString(this.characterEncoding) : this.content.toString();
|
||||
}
|
||||
|
||||
public void setContentLength(int contentLength) {
|
||||
this.contentLength = contentLength;
|
||||
doAddHeaderValue(CONTENT_LENGTH_HEADER, contentLength, true);
|
||||
}
|
||||
|
||||
public int getContentLength() {
|
||||
return this.contentLength;
|
||||
}
|
||||
|
||||
public void setContentType(String contentType) {
|
||||
this.contentType = contentType;
|
||||
if (contentType != null) {
|
||||
int charsetIndex = contentType.toLowerCase().indexOf(CHARSET_PREFIX);
|
||||
if (charsetIndex != -1) {
|
||||
String encoding = contentType.substring(charsetIndex + CHARSET_PREFIX.length());
|
||||
this.characterEncoding = encoding;
|
||||
this.charset = true;
|
||||
}
|
||||
updateContentTypeHeader();
|
||||
}
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return this.contentType;
|
||||
}
|
||||
|
||||
public void setBufferSize(int bufferSize) {
|
||||
this.bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
public int getBufferSize() {
|
||||
return this.bufferSize;
|
||||
}
|
||||
|
||||
public void flushBuffer() {
|
||||
setCommitted(true);
|
||||
}
|
||||
|
||||
public void resetBuffer() {
|
||||
if (isCommitted()) {
|
||||
throw new IllegalStateException("Cannot reset buffer - response is already committed");
|
||||
}
|
||||
this.content.reset();
|
||||
}
|
||||
|
||||
private void setCommittedIfBufferSizeExceeded() {
|
||||
int bufSize = getBufferSize();
|
||||
if (bufSize > 0 && this.content.size() > bufSize) {
|
||||
setCommitted(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCommitted(boolean committed) {
|
||||
this.committed = committed;
|
||||
}
|
||||
|
||||
public boolean isCommitted() {
|
||||
return this.committed;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
resetBuffer();
|
||||
this.characterEncoding = null;
|
||||
this.contentLength = 0;
|
||||
this.contentType = null;
|
||||
this.locale = null;
|
||||
this.cookies.clear();
|
||||
this.headers.clear();
|
||||
this.status = HttpServletResponse.SC_OK;
|
||||
this.errorMessage = null;
|
||||
}
|
||||
|
||||
public void setLocale(Locale locale) {
|
||||
this.locale = locale;
|
||||
}
|
||||
|
||||
public Locale getLocale() {
|
||||
return this.locale;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// HttpServletResponse interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
public void addCookie(Cookie cookie) {
|
||||
Assert.notNull(cookie, "Cookie must not be null");
|
||||
this.cookies.add(cookie);
|
||||
}
|
||||
|
||||
public Cookie[] getCookies() {
|
||||
return this.cookies.toArray(new Cookie[this.cookies.size()]);
|
||||
}
|
||||
|
||||
public Cookie getCookie(String name) {
|
||||
Assert.notNull(name, "Cookie name must not be null");
|
||||
for (Cookie cookie : this.cookies) {
|
||||
if (name.equals(cookie.getName())) {
|
||||
return cookie;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean containsHeader(String name) {
|
||||
return (HeaderValueHolder.getByName(this.headers, name) != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the names of all specified headers as a Set of Strings.
|
||||
* <p>As of Servlet 3.0, this method is also defined HttpServletResponse.
|
||||
* @return the <code>Set</code> of header name <code>Strings</code>, or an empty <code>Set</code> if none
|
||||
*/
|
||||
public Set<String> getHeaderNames() {
|
||||
return this.headers.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the primary value for the given header as a String, if any.
|
||||
* Will return the first value in case of multiple values.
|
||||
* <p>As of Servlet 3.0, this method is also defined HttpServletResponse.
|
||||
* As of Spring 3.1, it returns a stringified value for Servlet 3.0 compatibility.
|
||||
* Consider using {@link #getHeaderValue(String)} for raw Object access.
|
||||
* @param name the name of the header
|
||||
* @return the associated header value, or <code>null<code> if none
|
||||
*/
|
||||
public String getHeader(String name) {
|
||||
HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
|
||||
return (header != null ? header.getStringValue() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all values for the given header as a List of Strings.
|
||||
* <p>As of Servlet 3.0, this method is also defined HttpServletResponse.
|
||||
* As of Spring 3.1, it returns a List of stringified values for Servlet 3.0 compatibility.
|
||||
* Consider using {@link #getHeaderValues(String)} for raw Object access.
|
||||
* @param name the name of the header
|
||||
* @return the associated header values, or an empty List if none
|
||||
*/
|
||||
public List<String> getHeaders(String name) {
|
||||
HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
|
||||
if (header != null) {
|
||||
return header.getStringValues();
|
||||
}
|
||||
else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the primary value for the given header, if any.
|
||||
* <p>Will return the first value in case of multiple values.
|
||||
* @param name the name of the header
|
||||
* @return the associated header value, or <code>null<code> if none
|
||||
*/
|
||||
public Object getHeaderValue(String name) {
|
||||
HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
|
||||
return (header != null ? header.getValue() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all values for the given header as a List of value objects.
|
||||
* @param name the name of the header
|
||||
* @return the associated header values, or an empty List if none
|
||||
*/
|
||||
public List<Object> getHeaderValues(String name) {
|
||||
HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
|
||||
if (header != null) {
|
||||
return header.getValues();
|
||||
}
|
||||
else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The default implementation returns the given URL String as-is.
|
||||
* <p>Can be overridden in subclasses, appending a session id or the like.
|
||||
*/
|
||||
public String encodeURL(String url) {
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* The default implementation delegates to {@link #encodeURL},
|
||||
* returning the given URL String as-is.
|
||||
* <p>Can be overridden in subclasses, appending a session id or the like
|
||||
* in a redirect-specific fashion. For general URL encoding rules,
|
||||
* override the common {@link #encodeURL} method instead, appyling
|
||||
* to redirect URLs as well as to general URLs.
|
||||
*/
|
||||
public String encodeRedirectURL(String url) {
|
||||
return encodeURL(url);
|
||||
}
|
||||
|
||||
public String encodeUrl(String url) {
|
||||
return encodeURL(url);
|
||||
}
|
||||
|
||||
public String encodeRedirectUrl(String url) {
|
||||
return encodeRedirectURL(url);
|
||||
}
|
||||
|
||||
public void sendError(int status, String errorMessage) throws IOException {
|
||||
if (isCommitted()) {
|
||||
throw new IllegalStateException("Cannot set error status - response is already committed");
|
||||
}
|
||||
this.status = status;
|
||||
this.errorMessage = errorMessage;
|
||||
setCommitted(true);
|
||||
}
|
||||
|
||||
public void sendError(int status) throws IOException {
|
||||
if (isCommitted()) {
|
||||
throw new IllegalStateException("Cannot set error status - response is already committed");
|
||||
}
|
||||
this.status = status;
|
||||
setCommitted(true);
|
||||
}
|
||||
|
||||
public void sendRedirect(String url) throws IOException {
|
||||
if (isCommitted()) {
|
||||
throw new IllegalStateException("Cannot send redirect - response is already committed");
|
||||
}
|
||||
Assert.notNull(url, "Redirect URL must not be null");
|
||||
this.redirectedUrl = url;
|
||||
setCommitted(true);
|
||||
}
|
||||
|
||||
public String getRedirectedUrl() {
|
||||
return this.redirectedUrl;
|
||||
}
|
||||
|
||||
public void setDateHeader(String name, long value) {
|
||||
setHeaderValue(name, value);
|
||||
}
|
||||
|
||||
public void addDateHeader(String name, long value) {
|
||||
addHeaderValue(name, value);
|
||||
}
|
||||
|
||||
public void setHeader(String name, String value) {
|
||||
setHeaderValue(name, value);
|
||||
}
|
||||
|
||||
public void addHeader(String name, String value) {
|
||||
addHeaderValue(name, value);
|
||||
}
|
||||
|
||||
public void setIntHeader(String name, int value) {
|
||||
setHeaderValue(name, value);
|
||||
}
|
||||
|
||||
public void addIntHeader(String name, int value) {
|
||||
addHeaderValue(name, value);
|
||||
}
|
||||
|
||||
private void setHeaderValue(String name, Object value) {
|
||||
if (setSpecialHeader(name, value)) {
|
||||
return;
|
||||
}
|
||||
doAddHeaderValue(name, value, true);
|
||||
}
|
||||
|
||||
private void addHeaderValue(String name, Object value) {
|
||||
if (setSpecialHeader(name, value)) {
|
||||
return;
|
||||
}
|
||||
doAddHeaderValue(name, value, false);
|
||||
}
|
||||
|
||||
private boolean setSpecialHeader(String name, Object value) {
|
||||
if (CONTENT_TYPE_HEADER.equalsIgnoreCase(name)) {
|
||||
setContentType((String) value);
|
||||
return true;
|
||||
}
|
||||
else if (CONTENT_LENGTH_HEADER.equalsIgnoreCase(name)) {
|
||||
setContentLength(Integer.parseInt((String) value));
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void doAddHeaderValue(String name, Object value, boolean replace) {
|
||||
HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
|
||||
Assert.notNull(value, "Header value must not be null");
|
||||
if (header == null) {
|
||||
header = new HeaderValueHolder();
|
||||
this.headers.put(name, header);
|
||||
}
|
||||
if (replace) {
|
||||
header.setValue(value);
|
||||
}
|
||||
else {
|
||||
header.addValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void setStatus(int status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public void setStatus(int status, String errorMessage) {
|
||||
this.status = status;
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
|
||||
public int getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
public String getErrorMessage() {
|
||||
return this.errorMessage;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Methods for MockRequestDispatcher
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
public void setForwardedUrl(String forwardedUrl) {
|
||||
this.forwardedUrl = forwardedUrl;
|
||||
}
|
||||
|
||||
public String getForwardedUrl() {
|
||||
return this.forwardedUrl;
|
||||
}
|
||||
|
||||
public void setIncludedUrl(String includedUrl) {
|
||||
this.includedUrls.clear();
|
||||
if (includedUrl != null) {
|
||||
this.includedUrls.add(includedUrl);
|
||||
}
|
||||
}
|
||||
|
||||
public String getIncludedUrl() {
|
||||
int count = this.includedUrls.size();
|
||||
if (count > 1) {
|
||||
throw new IllegalStateException(
|
||||
"More than 1 URL included - check getIncludedUrls instead: " + this.includedUrls);
|
||||
}
|
||||
return (count == 1 ? this.includedUrls.get(0) : null);
|
||||
}
|
||||
|
||||
public void addIncludedUrl(String includedUrl) {
|
||||
Assert.notNull(includedUrl, "Included URL must not be null");
|
||||
this.includedUrls.add(includedUrl);
|
||||
}
|
||||
|
||||
public List<String> getIncludedUrls() {
|
||||
return this.includedUrls;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inner class that adapts the ServletOutputStream to mark the
|
||||
* response as committed once the buffer size is exceeded.
|
||||
*/
|
||||
private class ResponseServletOutputStream extends DelegatingServletOutputStream {
|
||||
|
||||
public ResponseServletOutputStream(OutputStream out) {
|
||||
super(out);
|
||||
}
|
||||
|
||||
public void write(int b) throws IOException {
|
||||
super.write(b);
|
||||
super.flush();
|
||||
setCommittedIfBufferSizeExceeded();
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
super.flush();
|
||||
setCommitted(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inner class that adapts the PrintWriter to mark the
|
||||
* response as committed once the buffer size is exceeded.
|
||||
*/
|
||||
private class ResponsePrintWriter extends PrintWriter {
|
||||
|
||||
public ResponsePrintWriter(Writer out) {
|
||||
super(out, true);
|
||||
}
|
||||
|
||||
public void write(char buf[], int off, int len) {
|
||||
super.write(buf, off, len);
|
||||
super.flush();
|
||||
setCommittedIfBufferSizeExceeded();
|
||||
}
|
||||
|
||||
public void write(String s, int off, int len) {
|
||||
super.write(s, off, len);
|
||||
super.flush();
|
||||
setCommittedIfBufferSizeExceeded();
|
||||
}
|
||||
|
||||
public void write(int c) {
|
||||
super.write(c);
|
||||
super.flush();
|
||||
setCommittedIfBufferSizeExceeded();
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
super.flush();
|
||||
setCommitted(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.mock.web;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Vector;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import javax.servlet.http.HttpSessionBindingEvent;
|
||||
import javax.servlet.http.HttpSessionBindingListener;
|
||||
import javax.servlet.http.HttpSessionContext;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.servlet.http.HttpSession} interface.
|
||||
*
|
||||
* <p>Compatible with Servlet 2.5 as well as Servlet 3.0.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rod Johnson
|
||||
* @author Mark Fisher
|
||||
* @since 1.0.2
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class MockHttpSession implements HttpSession {
|
||||
|
||||
private static int nextId = 1;
|
||||
|
||||
private final String id;
|
||||
|
||||
private final long creationTime = System.currentTimeMillis();
|
||||
|
||||
private int maxInactiveInterval;
|
||||
|
||||
private long lastAccessedTime = System.currentTimeMillis();
|
||||
|
||||
private final ServletContext servletContext;
|
||||
|
||||
private final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
|
||||
|
||||
private boolean invalid = false;
|
||||
|
||||
private boolean isNew = true;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MockHttpSession with a default {@link MockServletContext}.
|
||||
*
|
||||
* @see MockServletContext
|
||||
*/
|
||||
public MockHttpSession() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockHttpSession.
|
||||
*
|
||||
* @param servletContext the ServletContext that the session runs in
|
||||
*/
|
||||
public MockHttpSession(ServletContext servletContext) {
|
||||
this(servletContext, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockHttpSession.
|
||||
*
|
||||
* @param servletContext the ServletContext that the session runs in
|
||||
* @param id a unique identifier for this session
|
||||
*/
|
||||
public MockHttpSession(ServletContext servletContext, String id) {
|
||||
this.servletContext = (servletContext != null ? servletContext : new MockServletContext());
|
||||
this.id = (id != null ? id : Integer.toString(nextId++));
|
||||
}
|
||||
|
||||
public long getCreationTime() {
|
||||
return this.creationTime;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void access() {
|
||||
this.lastAccessedTime = System.currentTimeMillis();
|
||||
this.isNew = false;
|
||||
}
|
||||
|
||||
public long getLastAccessedTime() {
|
||||
return this.lastAccessedTime;
|
||||
}
|
||||
|
||||
public ServletContext getServletContext() {
|
||||
return this.servletContext;
|
||||
}
|
||||
|
||||
public void setMaxInactiveInterval(int interval) {
|
||||
this.maxInactiveInterval = interval;
|
||||
}
|
||||
|
||||
public int getMaxInactiveInterval() {
|
||||
return this.maxInactiveInterval;
|
||||
}
|
||||
|
||||
public HttpSessionContext getSessionContext() {
|
||||
throw new UnsupportedOperationException("getSessionContext");
|
||||
}
|
||||
|
||||
public Object getAttribute(String name) {
|
||||
Assert.notNull(name, "Attribute name must not be null");
|
||||
return this.attributes.get(name);
|
||||
}
|
||||
|
||||
public Object getValue(String name) {
|
||||
return getAttribute(name);
|
||||
}
|
||||
|
||||
public Enumeration<String> getAttributeNames() {
|
||||
return new Vector<String>(this.attributes.keySet()).elements();
|
||||
}
|
||||
|
||||
public String[] getValueNames() {
|
||||
return this.attributes.keySet().toArray(new String[this.attributes.size()]);
|
||||
}
|
||||
|
||||
public void setAttribute(String name, Object value) {
|
||||
Assert.notNull(name, "Attribute name must not be null");
|
||||
if (value != null) {
|
||||
this.attributes.put(name, value);
|
||||
if (value instanceof HttpSessionBindingListener) {
|
||||
((HttpSessionBindingListener) value).valueBound(new HttpSessionBindingEvent(this, name, value));
|
||||
}
|
||||
}
|
||||
else {
|
||||
removeAttribute(name);
|
||||
}
|
||||
}
|
||||
|
||||
public void putValue(String name, Object value) {
|
||||
setAttribute(name, value);
|
||||
}
|
||||
|
||||
public void removeAttribute(String name) {
|
||||
Assert.notNull(name, "Attribute name must not be null");
|
||||
Object value = this.attributes.remove(name);
|
||||
if (value instanceof HttpSessionBindingListener) {
|
||||
((HttpSessionBindingListener) value).valueUnbound(new HttpSessionBindingEvent(this, name, value));
|
||||
}
|
||||
}
|
||||
|
||||
public void removeValue(String name) {
|
||||
removeAttribute(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all of this session's attributes.
|
||||
*/
|
||||
public void clearAttributes() {
|
||||
for (Iterator<Map.Entry<String, Object>> it = this.attributes.entrySet().iterator(); it.hasNext();) {
|
||||
Map.Entry<String, Object> entry = it.next();
|
||||
String name = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
it.remove();
|
||||
if (value instanceof HttpSessionBindingListener) {
|
||||
((HttpSessionBindingListener) value).valueUnbound(new HttpSessionBindingEvent(this, name, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void invalidate() {
|
||||
this.invalid = true;
|
||||
clearAttributes();
|
||||
}
|
||||
|
||||
public boolean isInvalid() {
|
||||
return this.invalid;
|
||||
}
|
||||
|
||||
public void setNew(boolean value) {
|
||||
this.isNew = value;
|
||||
}
|
||||
|
||||
public boolean isNew() {
|
||||
return this.isNew;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the attributes of this session into an object that can be
|
||||
* turned into a byte array with standard Java serialization.
|
||||
*
|
||||
* @return a representation of this session's serialized state
|
||||
*/
|
||||
public Serializable serializeState() {
|
||||
HashMap<String, Serializable> state = new HashMap<String, Serializable>();
|
||||
for (Iterator<Map.Entry<String, Object>> it = this.attributes.entrySet().iterator(); it.hasNext();) {
|
||||
Map.Entry<String, Object> entry = it.next();
|
||||
String name = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
it.remove();
|
||||
if (value instanceof Serializable) {
|
||||
state.put(name, (Serializable) value);
|
||||
}
|
||||
else {
|
||||
// Not serializable... Servlet containers usually automatically
|
||||
// unbind the attribute in this case.
|
||||
if (value instanceof HttpSessionBindingListener) {
|
||||
((HttpSessionBindingListener) value).valueUnbound(new HttpSessionBindingEvent(this, name, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize the attributes of this session from a state object created by
|
||||
* {@link #serializeState()}.
|
||||
*
|
||||
* @param state a representation of this session's serialized state
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void deserializeState(Serializable state) {
|
||||
Assert.isTrue(state instanceof Map, "Serialized state needs to be of type [java.util.Map]");
|
||||
this.attributes.putAll((Map<String, Object>) state);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* 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.mock.web;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Writer;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.jsp.JspWriter;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.servlet.jsp.JspWriter} class.
|
||||
*
|
||||
* <p>Used for testing the web framework; only necessary for testing
|
||||
* applications when testing custom JSP tags.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
*/
|
||||
public class MockJspWriter extends JspWriter {
|
||||
|
||||
private final HttpServletResponse response;
|
||||
|
||||
private PrintWriter targetWriter;
|
||||
|
||||
|
||||
/**
|
||||
* Create a MockJspWriter for the given response,
|
||||
* using the response's default Writer.
|
||||
* @param response the servlet response to wrap
|
||||
*/
|
||||
public MockJspWriter(HttpServletResponse response) {
|
||||
this(response, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a MockJspWriter for the given plain Writer.
|
||||
* @param targetWriter the target Writer to wrap
|
||||
*/
|
||||
public MockJspWriter(Writer targetWriter) {
|
||||
this(null, targetWriter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a MockJspWriter for the given response.
|
||||
* @param response the servlet response to wrap
|
||||
* @param targetWriter the target Writer to wrap
|
||||
*/
|
||||
public MockJspWriter(HttpServletResponse response, Writer targetWriter) {
|
||||
super(DEFAULT_BUFFER, true);
|
||||
this.response = (response != null ? response : new MockHttpServletResponse());
|
||||
if (targetWriter instanceof PrintWriter) {
|
||||
this.targetWriter = (PrintWriter) targetWriter;
|
||||
}
|
||||
else if (targetWriter != null) {
|
||||
this.targetWriter = new PrintWriter(targetWriter);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily initialize the target Writer.
|
||||
*/
|
||||
protected PrintWriter getTargetWriter() throws IOException {
|
||||
if (this.targetWriter == null) {
|
||||
this.targetWriter = this.response.getWriter();
|
||||
}
|
||||
return this.targetWriter;
|
||||
}
|
||||
|
||||
|
||||
public void clear() throws IOException {
|
||||
if (this.response.isCommitted()) {
|
||||
throw new IOException("Response already committed");
|
||||
}
|
||||
this.response.resetBuffer();
|
||||
}
|
||||
|
||||
public void clearBuffer() throws IOException {
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
this.response.flushBuffer();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
flush();
|
||||
}
|
||||
|
||||
public int getRemaining() {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
public void newLine() throws IOException {
|
||||
getTargetWriter().println();
|
||||
}
|
||||
|
||||
public void write(char value[], int offset, int length) throws IOException {
|
||||
getTargetWriter().write(value, offset, length);
|
||||
}
|
||||
|
||||
public void print(boolean value) throws IOException {
|
||||
getTargetWriter().print(value);
|
||||
}
|
||||
|
||||
public void print(char value) throws IOException {
|
||||
getTargetWriter().print(value);
|
||||
}
|
||||
|
||||
public void print(char[] value) throws IOException {
|
||||
getTargetWriter().print(value);
|
||||
}
|
||||
|
||||
public void print(double value) throws IOException {
|
||||
getTargetWriter().print(value);
|
||||
}
|
||||
|
||||
public void print(float value) throws IOException {
|
||||
getTargetWriter().print(value);
|
||||
}
|
||||
|
||||
public void print(int value) throws IOException {
|
||||
getTargetWriter().print(value);
|
||||
}
|
||||
|
||||
public void print(long value) throws IOException {
|
||||
getTargetWriter().print(value);
|
||||
}
|
||||
|
||||
public void print(Object value) throws IOException {
|
||||
getTargetWriter().print(value);
|
||||
}
|
||||
|
||||
public void print(String value) throws IOException {
|
||||
getTargetWriter().print(value);
|
||||
}
|
||||
|
||||
public void println() throws IOException {
|
||||
getTargetWriter().println();
|
||||
}
|
||||
|
||||
public void println(boolean value) throws IOException {
|
||||
getTargetWriter().println(value);
|
||||
}
|
||||
|
||||
public void println(char value) throws IOException {
|
||||
getTargetWriter().println(value);
|
||||
}
|
||||
|
||||
public void println(char[] value) throws IOException {
|
||||
getTargetWriter().println(value);
|
||||
}
|
||||
|
||||
public void println(double value) throws IOException {
|
||||
getTargetWriter().println(value);
|
||||
}
|
||||
|
||||
public void println(float value) throws IOException {
|
||||
getTargetWriter().println(value);
|
||||
}
|
||||
|
||||
public void println(int value) throws IOException {
|
||||
getTargetWriter().println(value);
|
||||
}
|
||||
|
||||
public void println(long value) throws IOException {
|
||||
getTargetWriter().println(value);
|
||||
}
|
||||
|
||||
public void println(Object value) throws IOException {
|
||||
getTargetWriter().println(value);
|
||||
}
|
||||
|
||||
public void println(String value) throws IOException {
|
||||
getTargetWriter().println(value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.mock.web;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link org.springframework.web.multipart.MultipartFile}
|
||||
* interface.
|
||||
*
|
||||
* <p>Useful in conjunction with a {@link MockMultipartHttpServletRequest}
|
||||
* for testing application controllers that access multipart uploads.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Eric Crampton
|
||||
* @since 2.0
|
||||
* @see MockMultipartHttpServletRequest
|
||||
*/
|
||||
public class MockMultipartFile implements MultipartFile {
|
||||
|
||||
private final String name;
|
||||
|
||||
private String originalFilename;
|
||||
|
||||
private String contentType;
|
||||
|
||||
private final byte[] content;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MockMultipartFile with the given content.
|
||||
* @param name the name of the file
|
||||
* @param content the content of the file
|
||||
*/
|
||||
public MockMultipartFile(String name, byte[] content) {
|
||||
this(name, "", null, content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockMultipartFile with the given content.
|
||||
* @param name the name of the file
|
||||
* @param contentStream the content of the file as stream
|
||||
* @throws IOException if reading from the stream failed
|
||||
*/
|
||||
public MockMultipartFile(String name, InputStream contentStream) throws IOException {
|
||||
this(name, "", null, FileCopyUtils.copyToByteArray(contentStream));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockMultipartFile with the given content.
|
||||
* @param name the name of the file
|
||||
* @param originalFilename the original filename (as on the client's machine)
|
||||
* @param contentType the content type (if known)
|
||||
* @param content the content of the file
|
||||
*/
|
||||
public MockMultipartFile(String name, String originalFilename, String contentType, byte[] content) {
|
||||
Assert.hasLength(name, "Name must not be null");
|
||||
this.name = name;
|
||||
this.originalFilename = (originalFilename != null ? originalFilename : "");
|
||||
this.contentType = contentType;
|
||||
this.content = (content != null ? content : new byte[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockMultipartFile with the given content.
|
||||
* @param name the name of the file
|
||||
* @param originalFilename the original filename (as on the client's machine)
|
||||
* @param contentType the content type (if known)
|
||||
* @param contentStream the content of the file as stream
|
||||
* @throws IOException if reading from the stream failed
|
||||
*/
|
||||
public MockMultipartFile(String name, String originalFilename, String contentType, InputStream contentStream)
|
||||
throws IOException {
|
||||
|
||||
this(name, originalFilename, contentType, FileCopyUtils.copyToByteArray(contentStream));
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getOriginalFilename() {
|
||||
return this.originalFilename;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return this.contentType;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return (this.content.length == 0);
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return this.content.length;
|
||||
}
|
||||
|
||||
public byte[] getBytes() throws IOException {
|
||||
return this.content;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return new ByteArrayInputStream(this.content);
|
||||
}
|
||||
|
||||
public void transferTo(File dest) throws IOException, IllegalStateException {
|
||||
FileCopyUtils.copy(this.content, dest);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.mock.web;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
||||
|
||||
/**
|
||||
* Mock implementation of the
|
||||
* {@link org.springframework.web.multipart.MultipartHttpServletRequest} interface.
|
||||
*
|
||||
* <p>Useful for testing application controllers that access multipart uploads.
|
||||
* The {@link MockMultipartFile} can be used to populate these mock requests
|
||||
* with files.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Eric Crampton
|
||||
* @author Arjen Poutsma
|
||||
* @since 2.0
|
||||
* @see MockMultipartFile
|
||||
*/
|
||||
public class MockMultipartHttpServletRequest extends MockHttpServletRequest implements MultipartHttpServletRequest {
|
||||
|
||||
private final MultiValueMap<String, MultipartFile> multipartFiles =
|
||||
new LinkedMultiValueMap<String, MultipartFile>();
|
||||
|
||||
|
||||
public MockMultipartHttpServletRequest() {
|
||||
setMethod("POST");
|
||||
setContentType("multipart/form-data");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a file to this request. The parameter name from the multipart
|
||||
* form is taken from the {@link MultipartFile#getName()}.
|
||||
* @param file multipart file to be added
|
||||
*/
|
||||
public void addFile(MultipartFile file) {
|
||||
Assert.notNull(file, "MultipartFile must not be null");
|
||||
this.multipartFiles.add(file.getName(), file);
|
||||
}
|
||||
|
||||
public Iterator<String> getFileNames() {
|
||||
return this.multipartFiles.keySet().iterator();
|
||||
}
|
||||
|
||||
public MultipartFile getFile(String name) {
|
||||
return this.multipartFiles.getFirst(name);
|
||||
}
|
||||
|
||||
public List<MultipartFile> getFiles(String name) {
|
||||
List<MultipartFile> multipartFiles = this.multipartFiles.get(name);
|
||||
if (multipartFiles != null) {
|
||||
return multipartFiles;
|
||||
}
|
||||
else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, MultipartFile> getFileMap() {
|
||||
return this.multipartFiles.toSingleValueMap();
|
||||
}
|
||||
|
||||
public MultiValueMap<String, MultipartFile> getMultiFileMap() {
|
||||
return new LinkedMultiValueMap<String, MultipartFile>(this.multipartFiles);
|
||||
}
|
||||
|
||||
public String getMultipartContentType(String paramOrFileName) {
|
||||
MultipartFile file = getFile(paramOrFileName);
|
||||
if (file != null) {
|
||||
return file.getContentType();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public HttpMethod getRequestMethod() {
|
||||
return HttpMethod.valueOf(getMethod());
|
||||
}
|
||||
|
||||
public HttpHeaders getRequestHeaders() {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
Enumeration<String> headerNames = getHeaderNames();
|
||||
while (headerNames.hasMoreElements()) {
|
||||
String headerName = headerNames.nextElement();
|
||||
headers.put(headerName, Collections.list(getHeaders(headerName)));
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
public HttpHeaders getMultipartHeaders(String paramOrFileName) {
|
||||
String contentType = getMultipartContentType(paramOrFileName);
|
||||
if (contentType != null) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("Content-Type", contentType);
|
||||
return headers;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.mock.web;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import javax.el.ELContext;
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import javax.servlet.jsp.JspWriter;
|
||||
import javax.servlet.jsp.PageContext;
|
||||
import javax.servlet.jsp.el.ExpressionEvaluator;
|
||||
import javax.servlet.jsp.el.VariableResolver;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.servlet.jsp.PageContext} interface.
|
||||
*
|
||||
* <p>Used for testing the web framework; only necessary for testing
|
||||
* applications when testing custom JSP tags.
|
||||
*
|
||||
* <p>Note: Expects initialization via the constructor rather than via the
|
||||
* <code>PageContext.initialize</code> method. Does not support writing to
|
||||
* a JspWriter, request dispatching, and <code>handlePageException</code> calls.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.0.2
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class MockPageContext extends PageContext {
|
||||
|
||||
private final ServletContext servletContext;
|
||||
|
||||
private final HttpServletRequest request;
|
||||
|
||||
private final HttpServletResponse response;
|
||||
|
||||
private final ServletConfig servletConfig;
|
||||
|
||||
private final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
|
||||
|
||||
private JspWriter out;
|
||||
|
||||
|
||||
/**
|
||||
* Create new MockPageContext with a default {@link MockServletContext},
|
||||
* {@link MockHttpServletRequest}, {@link MockHttpServletResponse},
|
||||
* {@link MockServletConfig}.
|
||||
*/
|
||||
public MockPageContext() {
|
||||
this(null, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new MockPageContext with a default {@link MockHttpServletRequest},
|
||||
* {@link MockHttpServletResponse}, {@link MockServletConfig}.
|
||||
* @param servletContext the ServletContext that the JSP page runs in
|
||||
* (only necessary when actually accessing the ServletContext)
|
||||
*/
|
||||
public MockPageContext(ServletContext servletContext) {
|
||||
this(servletContext, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new MockPageContext with a MockHttpServletResponse,
|
||||
* MockServletConfig.
|
||||
* @param servletContext the ServletContext that the JSP page runs in
|
||||
* @param request the current HttpServletRequest
|
||||
* (only necessary when actually accessing the request)
|
||||
*/
|
||||
public MockPageContext(ServletContext servletContext, HttpServletRequest request) {
|
||||
this(servletContext, request, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new MockPageContext with a MockServletConfig.
|
||||
* @param servletContext the ServletContext that the JSP page runs in
|
||||
* @param request the current HttpServletRequest
|
||||
* @param response the current HttpServletResponse
|
||||
* (only necessary when actually writing to the response)
|
||||
*/
|
||||
public MockPageContext(ServletContext servletContext, HttpServletRequest request, HttpServletResponse response) {
|
||||
this(servletContext, request, response, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new MockServletConfig.
|
||||
* @param servletContext the ServletContext that the JSP page runs in
|
||||
* @param request the current HttpServletRequest
|
||||
* @param response the current HttpServletResponse
|
||||
* @param servletConfig the ServletConfig (hardly ever accessed from within a tag)
|
||||
*/
|
||||
public MockPageContext(ServletContext servletContext, HttpServletRequest request,
|
||||
HttpServletResponse response, ServletConfig servletConfig) {
|
||||
|
||||
this.servletContext = (servletContext != null ? servletContext : new MockServletContext());
|
||||
this.request = (request != null ? request : new MockHttpServletRequest(servletContext));
|
||||
this.response = (response != null ? response : new MockHttpServletResponse());
|
||||
this.servletConfig = (servletConfig != null ? servletConfig : new MockServletConfig(servletContext));
|
||||
}
|
||||
|
||||
|
||||
public void initialize(
|
||||
Servlet servlet, ServletRequest request, ServletResponse response,
|
||||
String errorPageURL, boolean needsSession, int bufferSize, boolean autoFlush) {
|
||||
|
||||
throw new UnsupportedOperationException("Use appropriate constructor");
|
||||
}
|
||||
|
||||
public void release() {
|
||||
}
|
||||
|
||||
public void setAttribute(String name, Object value) {
|
||||
Assert.notNull(name, "Attribute name must not be null");
|
||||
if (value != null) {
|
||||
this.attributes.put(name, value);
|
||||
}
|
||||
else {
|
||||
this.attributes.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
public void setAttribute(String name, Object value, int scope) {
|
||||
Assert.notNull(name, "Attribute name must not be null");
|
||||
switch (scope) {
|
||||
case PAGE_SCOPE:
|
||||
setAttribute(name, value);
|
||||
break;
|
||||
case REQUEST_SCOPE:
|
||||
this.request.setAttribute(name, value);
|
||||
break;
|
||||
case SESSION_SCOPE:
|
||||
this.request.getSession().setAttribute(name, value);
|
||||
break;
|
||||
case APPLICATION_SCOPE:
|
||||
this.servletContext.setAttribute(name, value);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid scope: " + scope);
|
||||
}
|
||||
}
|
||||
|
||||
public Object getAttribute(String name) {
|
||||
Assert.notNull(name, "Attribute name must not be null");
|
||||
return this.attributes.get(name);
|
||||
}
|
||||
|
||||
public Object getAttribute(String name, int scope) {
|
||||
Assert.notNull(name, "Attribute name must not be null");
|
||||
switch (scope) {
|
||||
case PAGE_SCOPE:
|
||||
return getAttribute(name);
|
||||
case REQUEST_SCOPE:
|
||||
return this.request.getAttribute(name);
|
||||
case SESSION_SCOPE:
|
||||
HttpSession session = this.request.getSession(false);
|
||||
return (session != null ? session.getAttribute(name) : null);
|
||||
case APPLICATION_SCOPE:
|
||||
return this.servletContext.getAttribute(name);
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid scope: " + scope);
|
||||
}
|
||||
}
|
||||
|
||||
public Object findAttribute(String name) {
|
||||
Object value = getAttribute(name);
|
||||
if (value == null) {
|
||||
value = getAttribute(name, REQUEST_SCOPE);
|
||||
if (value == null) {
|
||||
value = getAttribute(name, SESSION_SCOPE);
|
||||
if (value == null) {
|
||||
value = getAttribute(name, APPLICATION_SCOPE);
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public void removeAttribute(String name) {
|
||||
Assert.notNull(name, "Attribute name must not be null");
|
||||
this.removeAttribute(name, PageContext.PAGE_SCOPE);
|
||||
this.removeAttribute(name, PageContext.REQUEST_SCOPE);
|
||||
this.removeAttribute(name, PageContext.SESSION_SCOPE);
|
||||
this.removeAttribute(name, PageContext.APPLICATION_SCOPE);
|
||||
}
|
||||
|
||||
public void removeAttribute(String name, int scope) {
|
||||
Assert.notNull(name, "Attribute name must not be null");
|
||||
switch (scope) {
|
||||
case PAGE_SCOPE:
|
||||
this.attributes.remove(name);
|
||||
break;
|
||||
case REQUEST_SCOPE:
|
||||
this.request.removeAttribute(name);
|
||||
break;
|
||||
case SESSION_SCOPE:
|
||||
this.request.getSession().removeAttribute(name);
|
||||
break;
|
||||
case APPLICATION_SCOPE:
|
||||
this.servletContext.removeAttribute(name);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid scope: " + scope);
|
||||
}
|
||||
}
|
||||
|
||||
public int getAttributesScope(String name) {
|
||||
if (getAttribute(name) != null) {
|
||||
return PAGE_SCOPE;
|
||||
}
|
||||
else if (getAttribute(name, REQUEST_SCOPE) != null) {
|
||||
return REQUEST_SCOPE;
|
||||
}
|
||||
else if (getAttribute(name, SESSION_SCOPE) != null) {
|
||||
return SESSION_SCOPE;
|
||||
}
|
||||
else if (getAttribute(name, APPLICATION_SCOPE) != null) {
|
||||
return APPLICATION_SCOPE;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public Enumeration<String> getAttributeNames() {
|
||||
return Collections.enumeration(this.attributes.keySet());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Enumeration<String> getAttributeNamesInScope(int scope) {
|
||||
switch (scope) {
|
||||
case PAGE_SCOPE:
|
||||
return getAttributeNames();
|
||||
case REQUEST_SCOPE:
|
||||
return this.request.getAttributeNames();
|
||||
case SESSION_SCOPE:
|
||||
HttpSession session = this.request.getSession(false);
|
||||
return (session != null ? session.getAttributeNames() : null);
|
||||
case APPLICATION_SCOPE:
|
||||
return this.servletContext.getAttributeNames();
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid scope: " + scope);
|
||||
}
|
||||
}
|
||||
|
||||
public JspWriter getOut() {
|
||||
if (this.out == null) {
|
||||
this.out = new MockJspWriter(this.response);
|
||||
}
|
||||
return this.out;
|
||||
}
|
||||
|
||||
public ExpressionEvaluator getExpressionEvaluator() {
|
||||
return new MockExpressionEvaluator(this);
|
||||
}
|
||||
|
||||
public ELContext getELContext() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public VariableResolver getVariableResolver() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public HttpSession getSession() {
|
||||
return this.request.getSession();
|
||||
}
|
||||
|
||||
public Object getPage() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public ServletRequest getRequest() {
|
||||
return this.request;
|
||||
}
|
||||
|
||||
public ServletResponse getResponse() {
|
||||
return this.response;
|
||||
}
|
||||
|
||||
public Exception getException() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ServletConfig getServletConfig() {
|
||||
return this.servletConfig;
|
||||
}
|
||||
|
||||
public ServletContext getServletContext() {
|
||||
return this.servletContext;
|
||||
}
|
||||
|
||||
public void forward(String path) throws ServletException, IOException {
|
||||
this.request.getRequestDispatcher(path).forward(this.request, this.response);
|
||||
}
|
||||
|
||||
public void include(String path) throws ServletException, IOException {
|
||||
this.request.getRequestDispatcher(path).include(this.request, this.response);
|
||||
}
|
||||
|
||||
public void include(String path, boolean flush) throws ServletException, IOException {
|
||||
this.request.getRequestDispatcher(path).include(this.request, this.response);
|
||||
if (flush) {
|
||||
this.response.flushBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getContentAsByteArray() {
|
||||
Assert.isTrue(this.response instanceof MockHttpServletResponse);
|
||||
return ((MockHttpServletResponse) this.response).getContentAsByteArray();
|
||||
}
|
||||
|
||||
public String getContentAsString() throws UnsupportedEncodingException {
|
||||
Assert.isTrue(this.response instanceof MockHttpServletResponse);
|
||||
return ((MockHttpServletResponse) this.response).getContentAsString();
|
||||
}
|
||||
|
||||
public void handlePageException(Exception ex) throws ServletException, IOException {
|
||||
throw new ServletException("Page exception", ex);
|
||||
}
|
||||
|
||||
public void handlePageException(Throwable ex) throws ServletException, IOException {
|
||||
throw new ServletException("Page exception", ex);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.mock.web;
|
||||
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletResponseWrapper;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.servlet.RequestDispatcher} interface.
|
||||
*
|
||||
* <p>Used for testing the web framework; typically not necessary for
|
||||
* testing application controllers.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.0.2
|
||||
*/
|
||||
public class MockRequestDispatcher implements RequestDispatcher {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final String url;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MockRequestDispatcher for the given URL.
|
||||
* @param url the URL to dispatch to.
|
||||
*/
|
||||
public MockRequestDispatcher(String url) {
|
||||
Assert.notNull(url, "URL must not be null");
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
|
||||
public void forward(ServletRequest request, ServletResponse response) {
|
||||
Assert.notNull(request, "Request must not be null");
|
||||
Assert.notNull(response, "Response must not be null");
|
||||
if (response.isCommitted()) {
|
||||
throw new IllegalStateException("Cannot perform forward - response is already committed");
|
||||
}
|
||||
getMockHttpServletResponse(response).setForwardedUrl(this.url);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("MockRequestDispatcher: forwarding to URL [" + this.url + "]");
|
||||
}
|
||||
}
|
||||
|
||||
public void include(ServletRequest request, ServletResponse response) {
|
||||
Assert.notNull(request, "Request must not be null");
|
||||
Assert.notNull(response, "Response must not be null");
|
||||
getMockHttpServletResponse(response).addIncludedUrl(this.url);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("MockRequestDispatcher: including URL [" + this.url + "]");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the underlying MockHttpServletResponse,
|
||||
* unwrapping {@link HttpServletResponseWrapper} decorators if necessary.
|
||||
*/
|
||||
protected MockHttpServletResponse getMockHttpServletResponse(ServletResponse response) {
|
||||
if (response instanceof MockHttpServletResponse) {
|
||||
return (MockHttpServletResponse) response;
|
||||
}
|
||||
if (response instanceof HttpServletResponseWrapper) {
|
||||
return getMockHttpServletResponse(((HttpServletResponseWrapper) response).getResponse());
|
||||
}
|
||||
throw new IllegalArgumentException("MockRequestDispatcher requires MockHttpServletResponse");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.mock.web;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.servlet.ServletConfig} interface.
|
||||
*
|
||||
* <p>Used for testing the web framework; typically not necessary for
|
||||
* testing application controllers.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.0.2
|
||||
*/
|
||||
public class MockServletConfig implements ServletConfig {
|
||||
|
||||
private final ServletContext servletContext;
|
||||
|
||||
private final String servletName;
|
||||
|
||||
private final Map<String, String> initParameters = new LinkedHashMap<String, String>();
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MockServletConfig with a default {@link MockServletContext}.
|
||||
*/
|
||||
public MockServletConfig() {
|
||||
this(null, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockServletConfig with a default {@link MockServletContext}.
|
||||
* @param servletName the name of the servlet
|
||||
*/
|
||||
public MockServletConfig(String servletName) {
|
||||
this(null, servletName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockServletConfig.
|
||||
* @param servletContext the ServletContext that the servlet runs in
|
||||
*/
|
||||
public MockServletConfig(ServletContext servletContext) {
|
||||
this(servletContext, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockServletConfig.
|
||||
* @param servletContext the ServletContext that the servlet runs in
|
||||
* @param servletName the name of the servlet
|
||||
*/
|
||||
public MockServletConfig(ServletContext servletContext, String servletName) {
|
||||
this.servletContext = (servletContext != null ? servletContext : new MockServletContext());
|
||||
this.servletName = servletName;
|
||||
}
|
||||
|
||||
|
||||
public String getServletName() {
|
||||
return this.servletName;
|
||||
}
|
||||
|
||||
public ServletContext getServletContext() {
|
||||
return this.servletContext;
|
||||
}
|
||||
|
||||
public void addInitParameter(String name, String value) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
this.initParameters.put(name, value);
|
||||
}
|
||||
|
||||
public String getInitParameter(String name) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
return this.initParameters.get(name);
|
||||
}
|
||||
|
||||
public Enumeration<String> getInitParameterNames() {
|
||||
return Collections.enumeration(this.initParameters.keySet());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,423 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.mock.web;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Vector;
|
||||
import javax.activation.FileTypeMap;
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.servlet.ServletContext} interface.
|
||||
*
|
||||
* <p>Compatible with Servlet 2.5 and partially with Servlet 3.0. Can be configured to
|
||||
* expose a specific version through {@link #setMajorVersion}/{@link #setMinorVersion};
|
||||
* default is 2.5. Note that Servlet 3.0 support is limited: servlet, filter and listener
|
||||
* registration methods are not supported; neither is cookie or JSP configuration.
|
||||
* We generally do not recommend to unit-test your ServletContainerInitializers and
|
||||
* WebApplicationInitializers which is where those registration methods would be used.
|
||||
*
|
||||
* <p>Used for testing the Spring web framework; only rarely necessary for testing
|
||||
* application controllers. As long as application components don't explicitly
|
||||
* access the ServletContext, ClassPathXmlApplicationContext or
|
||||
* FileSystemXmlApplicationContext can be used to load the context files for testing,
|
||||
* even for DispatcherServlet context definitions.
|
||||
*
|
||||
* <p>For setting up a full WebApplicationContext in a test environment, you can
|
||||
* use XmlWebApplicationContext (or GenericWebApplicationContext), passing in an
|
||||
* appropriate MockServletContext instance. You might want to configure your
|
||||
* MockServletContext with a FileSystemResourceLoader in that case, to make your
|
||||
* resource paths interpreted as relative file system locations.
|
||||
*
|
||||
* <p>A common setup is to point your JVM working directory to the root of your
|
||||
* web application directory, in combination with filesystem-based resource loading.
|
||||
* This allows to load the context files as used in the web application, with
|
||||
* relative paths getting interpreted correctly. Such a setup will work with both
|
||||
* FileSystemXmlApplicationContext (which will load straight from the file system)
|
||||
* and XmlWebApplicationContext with an underlying MockServletContext (as long as
|
||||
* the MockServletContext has been configured with a FileSystemResourceLoader).
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.0.2
|
||||
* @see #MockServletContext(org.springframework.core.io.ResourceLoader)
|
||||
* @see org.springframework.web.context.support.XmlWebApplicationContext
|
||||
* @see org.springframework.web.context.support.GenericWebApplicationContext
|
||||
* @see org.springframework.context.support.ClassPathXmlApplicationContext
|
||||
* @see org.springframework.context.support.FileSystemXmlApplicationContext
|
||||
*/
|
||||
public class MockServletContext implements ServletContext {
|
||||
|
||||
private static final String TEMP_DIR_SYSTEM_PROPERTY = "java.io.tmpdir";
|
||||
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final ResourceLoader resourceLoader;
|
||||
|
||||
private final String resourceBasePath;
|
||||
|
||||
private String contextPath = "";
|
||||
|
||||
private int majorVersion = 2;
|
||||
|
||||
private int minorVersion = 5;
|
||||
|
||||
private int effectiveMajorVersion = 2;
|
||||
|
||||
private int effectiveMinorVersion = 5;
|
||||
|
||||
private final Map<String, ServletContext> contexts = new HashMap<String, ServletContext>();
|
||||
|
||||
private final Map<String, String> initParameters = new LinkedHashMap<String, String>();
|
||||
|
||||
private final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
|
||||
|
||||
private String servletContextName = "MockServletContext";
|
||||
|
||||
private final Set<String> declaredRoles = new HashSet<String>();
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MockServletContext, using no base path and a
|
||||
* DefaultResourceLoader (i.e. the classpath root as WAR root).
|
||||
* @see org.springframework.core.io.DefaultResourceLoader
|
||||
*/
|
||||
public MockServletContext() {
|
||||
this("", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockServletContext, using a DefaultResourceLoader.
|
||||
* @param resourceBasePath the WAR root directory (should not end with a slash)
|
||||
* @see org.springframework.core.io.DefaultResourceLoader
|
||||
*/
|
||||
public MockServletContext(String resourceBasePath) {
|
||||
this(resourceBasePath, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockServletContext, using the specified ResourceLoader
|
||||
* and no base path.
|
||||
* @param resourceLoader the ResourceLoader to use (or null for the default)
|
||||
*/
|
||||
public MockServletContext(ResourceLoader resourceLoader) {
|
||||
this("", resourceLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockServletContext.
|
||||
* @param resourceBasePath the WAR root directory (should not end with a slash)
|
||||
* @param resourceLoader the ResourceLoader to use (or null for the default)
|
||||
*/
|
||||
public MockServletContext(String resourceBasePath, ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = (resourceLoader != null ? resourceLoader : new DefaultResourceLoader());
|
||||
this.resourceBasePath = (resourceBasePath != null ? resourceBasePath : "");
|
||||
|
||||
// Use JVM temp dir as ServletContext temp dir.
|
||||
String tempDir = System.getProperty(TEMP_DIR_SYSTEM_PROPERTY);
|
||||
if (tempDir != null) {
|
||||
this.attributes.put(WebUtils.TEMP_DIR_CONTEXT_ATTRIBUTE, new File(tempDir));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build a full resource location for the given path,
|
||||
* prepending the resource base path of this MockServletContext.
|
||||
* @param path the path as specified
|
||||
* @return the full resource path
|
||||
*/
|
||||
protected String getResourceLocation(String path) {
|
||||
if (!path.startsWith("/")) {
|
||||
path = "/" + path;
|
||||
}
|
||||
return this.resourceBasePath + path;
|
||||
}
|
||||
|
||||
|
||||
public void setContextPath(String contextPath) {
|
||||
this.contextPath = (contextPath != null ? contextPath : "");
|
||||
}
|
||||
|
||||
/* This is a Servlet API 2.5 method. */
|
||||
public String getContextPath() {
|
||||
return this.contextPath;
|
||||
}
|
||||
|
||||
public void registerContext(String contextPath, ServletContext context) {
|
||||
this.contexts.put(contextPath, context);
|
||||
}
|
||||
|
||||
public ServletContext getContext(String contextPath) {
|
||||
if (this.contextPath.equals(contextPath)) {
|
||||
return this;
|
||||
}
|
||||
return this.contexts.get(contextPath);
|
||||
}
|
||||
|
||||
public void setMajorVersion(int majorVersion) {
|
||||
this.majorVersion = majorVersion;
|
||||
}
|
||||
|
||||
public int getMajorVersion() {
|
||||
return this.majorVersion;
|
||||
}
|
||||
|
||||
public void setMinorVersion(int minorVersion) {
|
||||
this.minorVersion = minorVersion;
|
||||
}
|
||||
|
||||
public int getMinorVersion() {
|
||||
return this.minorVersion;
|
||||
}
|
||||
|
||||
public void setEffectiveMajorVersion(int effectiveMajorVersion) {
|
||||
this.effectiveMajorVersion = effectiveMajorVersion;
|
||||
}
|
||||
|
||||
public int getEffectiveMajorVersion() {
|
||||
return this.effectiveMajorVersion;
|
||||
}
|
||||
|
||||
public void setEffectiveMinorVersion(int effectiveMinorVersion) {
|
||||
this.effectiveMinorVersion = effectiveMinorVersion;
|
||||
}
|
||||
|
||||
public int getEffectiveMinorVersion() {
|
||||
return this.effectiveMinorVersion;
|
||||
}
|
||||
|
||||
public String getMimeType(String filePath) {
|
||||
return MimeTypeResolver.getMimeType(filePath);
|
||||
}
|
||||
|
||||
public Set<String> getResourcePaths(String path) {
|
||||
String actualPath = (path.endsWith("/") ? path : path + "/");
|
||||
Resource resource = this.resourceLoader.getResource(getResourceLocation(actualPath));
|
||||
try {
|
||||
File file = resource.getFile();
|
||||
String[] fileList = file.list();
|
||||
if (ObjectUtils.isEmpty(fileList)) {
|
||||
return null;
|
||||
}
|
||||
Set<String> resourcePaths = new LinkedHashSet<String>(fileList.length);
|
||||
for (String fileEntry : fileList) {
|
||||
String resultPath = actualPath + fileEntry;
|
||||
if (resource.createRelative(fileEntry).getFile().isDirectory()) {
|
||||
resultPath += "/";
|
||||
}
|
||||
resourcePaths.add(resultPath);
|
||||
}
|
||||
return resourcePaths;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
logger.warn("Couldn't get resource paths for " + resource, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public URL getResource(String path) throws MalformedURLException {
|
||||
Resource resource = this.resourceLoader.getResource(getResourceLocation(path));
|
||||
if (!resource.exists()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return resource.getURL();
|
||||
}
|
||||
catch (MalformedURLException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
logger.warn("Couldn't get URL for " + resource, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public InputStream getResourceAsStream(String path) {
|
||||
Resource resource = this.resourceLoader.getResource(getResourceLocation(path));
|
||||
if (!resource.exists()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return resource.getInputStream();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
logger.warn("Couldn't open InputStream for " + resource, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public RequestDispatcher getRequestDispatcher(String path) {
|
||||
if (!path.startsWith("/")) {
|
||||
throw new IllegalArgumentException("RequestDispatcher path at ServletContext level must start with '/'");
|
||||
}
|
||||
return new MockRequestDispatcher(path);
|
||||
}
|
||||
|
||||
public RequestDispatcher getNamedDispatcher(String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Servlet getServlet(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Enumeration<Servlet> getServlets() {
|
||||
return Collections.enumeration(new HashSet<Servlet>());
|
||||
}
|
||||
|
||||
public Enumeration<String> getServletNames() {
|
||||
return Collections.enumeration(new HashSet<String>());
|
||||
}
|
||||
|
||||
public void log(String message) {
|
||||
logger.info(message);
|
||||
}
|
||||
|
||||
public void log(Exception ex, String message) {
|
||||
logger.info(message, ex);
|
||||
}
|
||||
|
||||
public void log(String message, Throwable ex) {
|
||||
logger.info(message, ex);
|
||||
}
|
||||
|
||||
public String getRealPath(String path) {
|
||||
Resource resource = this.resourceLoader.getResource(getResourceLocation(path));
|
||||
try {
|
||||
return resource.getFile().getAbsolutePath();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
logger.warn("Couldn't determine real path of resource " + resource, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getServerInfo() {
|
||||
return "MockServletContext";
|
||||
}
|
||||
|
||||
public String getInitParameter(String name) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
return this.initParameters.get(name);
|
||||
}
|
||||
|
||||
public Enumeration<String> getInitParameterNames() {
|
||||
return Collections.enumeration(this.initParameters.keySet());
|
||||
}
|
||||
|
||||
public boolean setInitParameter(String name, String value) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
if (this.initParameters.containsKey(name)) {
|
||||
return false;
|
||||
}
|
||||
this.initParameters.put(name, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void addInitParameter(String name, String value) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
this.initParameters.put(name, value);
|
||||
}
|
||||
|
||||
public Object getAttribute(String name) {
|
||||
Assert.notNull(name, "Attribute name must not be null");
|
||||
return this.attributes.get(name);
|
||||
}
|
||||
|
||||
public Enumeration<String> getAttributeNames() {
|
||||
return new Vector<String>(this.attributes.keySet()).elements();
|
||||
}
|
||||
|
||||
public void setAttribute(String name, Object value) {
|
||||
Assert.notNull(name, "Attribute name must not be null");
|
||||
if (value != null) {
|
||||
this.attributes.put(name, value);
|
||||
}
|
||||
else {
|
||||
this.attributes.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAttribute(String name) {
|
||||
Assert.notNull(name, "Attribute name must not be null");
|
||||
this.attributes.remove(name);
|
||||
}
|
||||
|
||||
public void setServletContextName(String servletContextName) {
|
||||
this.servletContextName = servletContextName;
|
||||
}
|
||||
|
||||
public String getServletContextName() {
|
||||
return this.servletContextName;
|
||||
}
|
||||
|
||||
public ClassLoader getClassLoader() {
|
||||
return ClassUtils.getDefaultClassLoader();
|
||||
}
|
||||
|
||||
public void declareRoles(String... roleNames) {
|
||||
Assert.notNull(roleNames, "Role names array must not be null");
|
||||
for (String roleName : roleNames) {
|
||||
Assert.hasLength(roleName, "Role name must not be empty");
|
||||
this.declaredRoles.add(roleName);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> getDeclaredRoles() {
|
||||
return Collections.unmodifiableSet(this.declaredRoles);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inner factory class used to just introduce a Java Activation Framework
|
||||
* dependency when actually asked to resolve a MIME type.
|
||||
*/
|
||||
private static class MimeTypeResolver {
|
||||
|
||||
public static String getMimeType(String filePath) {
|
||||
return FileTypeMap.getDefaultFileTypeMap().getContentType(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.mock.web;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Implementation of the {@link javax.servlet.FilterConfig} interface which
|
||||
* simply passes the call through to a given Filter/FilterChain combination
|
||||
* (indicating the next Filter in the chain along with the FilterChain that it is
|
||||
* supposed to work on) or to a given Servlet (indicating the end of the chain).
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0.3
|
||||
* @see javax.servlet.Filter
|
||||
* @see javax.servlet.Servlet
|
||||
* @see MockFilterChain
|
||||
*/
|
||||
public class PassThroughFilterChain implements FilterChain {
|
||||
|
||||
private Filter filter;
|
||||
|
||||
private FilterChain nextFilterChain;
|
||||
|
||||
private Servlet servlet;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new PassThroughFilterChain that delegates to the given Filter,
|
||||
* calling it with the given FilterChain.
|
||||
* @param filter the Filter to delegate to
|
||||
* @param nextFilterChain the FilterChain to use for that next Filter
|
||||
*/
|
||||
public PassThroughFilterChain(Filter filter, FilterChain nextFilterChain) {
|
||||
Assert.notNull(filter, "Filter must not be null");
|
||||
Assert.notNull(nextFilterChain, "'FilterChain must not be null");
|
||||
this.filter = filter;
|
||||
this.nextFilterChain = nextFilterChain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new PassThroughFilterChain that delegates to the given Servlet.
|
||||
* @param servlet the Servlet to delegate to
|
||||
*/
|
||||
public PassThroughFilterChain(Servlet servlet) {
|
||||
Assert.notNull(servlet, "Servlet must not be null");
|
||||
this.servlet = servlet;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pass the call on to the Filter/Servlet.
|
||||
*/
|
||||
public void doFilter(ServletRequest request, ServletResponse response) throws ServletException, IOException {
|
||||
if (this.filter != null) {
|
||||
this.filter.doFilter(request, response, this.nextFilterChain);
|
||||
}
|
||||
else {
|
||||
this.servlet.service(request, response);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* A comprehensive set of Servlet API 2.5 mock objects,
|
||||
* targeted at usage with Spring's web MVC framework.
|
||||
* Useful for testing web contexts and controllers.
|
||||
*
|
||||
* <p>More convenient to use than dynamic mock objects
|
||||
* (<a href="http://www.easymock.org">EasyMock</a>) or
|
||||
* existing Servlet API mock objects
|
||||
* (<a href="http://www.mockobjects.com">MockObjects</a>).
|
||||
*
|
||||
*/
|
||||
package org.springframework.mock.web;
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import javax.portlet.ActionRequest;
|
||||
import javax.portlet.PortalContext;
|
||||
import javax.portlet.PortletContext;
|
||||
import javax.portlet.PortletMode;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.ActionRequest} interface.
|
||||
*
|
||||
* @author John A. Lewis
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MockActionRequest extends MockClientDataRequest implements ActionRequest {
|
||||
|
||||
/**
|
||||
* Create a new MockActionRequest with a default {@link MockPortalContext}
|
||||
* and a default {@link MockPortletContext}.
|
||||
* @see org.springframework.mock.web.portlet.MockPortalContext
|
||||
* @see org.springframework.mock.web.portlet.MockPortletContext
|
||||
*/
|
||||
public MockActionRequest() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockActionRequest with a default {@link MockPortalContext}
|
||||
* and a default {@link MockPortletContext}.
|
||||
* @param actionName the name of the action to trigger
|
||||
*/
|
||||
public MockActionRequest(String actionName) {
|
||||
super();
|
||||
setParameter(ActionRequest.ACTION_NAME, actionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockActionRequest with a default {@link MockPortalContext}
|
||||
* and a default {@link MockPortletContext}.
|
||||
* @param portletMode the mode that the portlet runs in
|
||||
*/
|
||||
public MockActionRequest(PortletMode portletMode) {
|
||||
super();
|
||||
setPortletMode(portletMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockActionRequest with a default {@link MockPortalContext}.
|
||||
* @param portletContext the PortletContext that the request runs in
|
||||
*/
|
||||
public MockActionRequest(PortletContext portletContext) {
|
||||
super(portletContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockActionRequest.
|
||||
* @param portalContext the PortalContext that the request runs in
|
||||
* @param portletContext the PortletContext that the request runs in
|
||||
*/
|
||||
public MockActionRequest(PortalContext portalContext, PortletContext portletContext) {
|
||||
super(portalContext, portletContext);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected String getLifecyclePhase() {
|
||||
return ACTION_PHASE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import javax.portlet.ActionResponse;
|
||||
import javax.portlet.PortalContext;
|
||||
import javax.portlet.PortletMode;
|
||||
import javax.portlet.PortletModeException;
|
||||
import javax.portlet.WindowState;
|
||||
import javax.portlet.WindowStateException;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.ActionResponse} interface.
|
||||
*
|
||||
* @author John A. Lewis
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MockActionResponse extends MockStateAwareResponse implements ActionResponse {
|
||||
|
||||
private boolean redirectAllowed = true;
|
||||
|
||||
private String redirectedUrl;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MockActionResponse with a default {@link MockPortalContext}.
|
||||
* @see MockPortalContext
|
||||
*/
|
||||
public MockActionResponse() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockActionResponse.
|
||||
* @param portalContext the PortalContext defining the supported
|
||||
* PortletModes and WindowStates
|
||||
*/
|
||||
public MockActionResponse(PortalContext portalContext) {
|
||||
super(portalContext);
|
||||
}
|
||||
|
||||
|
||||
public void setWindowState(WindowState windowState) throws WindowStateException {
|
||||
if (this.redirectedUrl != null) {
|
||||
throw new IllegalStateException("Cannot set WindowState after sendRedirect has been called");
|
||||
}
|
||||
super.setWindowState(windowState);
|
||||
this.redirectAllowed = false;
|
||||
}
|
||||
|
||||
public void setPortletMode(PortletMode portletMode) throws PortletModeException {
|
||||
if (this.redirectedUrl != null) {
|
||||
throw new IllegalStateException("Cannot set PortletMode after sendRedirect has been called");
|
||||
}
|
||||
super.setPortletMode(portletMode);
|
||||
this.redirectAllowed = false;
|
||||
}
|
||||
|
||||
public void setRenderParameters(Map<String, String[]> parameters) {
|
||||
if (this.redirectedUrl != null) {
|
||||
throw new IllegalStateException("Cannot set render parameters after sendRedirect has been called");
|
||||
}
|
||||
super.setRenderParameters(parameters);
|
||||
this.redirectAllowed = false;
|
||||
}
|
||||
|
||||
public void setRenderParameter(String key, String value) {
|
||||
if (this.redirectedUrl != null) {
|
||||
throw new IllegalStateException("Cannot set render parameters after sendRedirect has been called");
|
||||
}
|
||||
super.setRenderParameter(key, value);
|
||||
this.redirectAllowed = false;
|
||||
}
|
||||
|
||||
public void setRenderParameter(String key, String[] values) {
|
||||
if (this.redirectedUrl != null) {
|
||||
throw new IllegalStateException("Cannot set render parameters after sendRedirect has been called");
|
||||
}
|
||||
super.setRenderParameter(key, values);
|
||||
this.redirectAllowed = false;
|
||||
}
|
||||
|
||||
public void sendRedirect(String location) throws IOException {
|
||||
if (!this.redirectAllowed) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot call sendRedirect after windowState, portletMode, or renderParameters have been set");
|
||||
}
|
||||
Assert.notNull(location, "Redirect URL must not be null");
|
||||
this.redirectedUrl = location;
|
||||
}
|
||||
|
||||
public void sendRedirect(String location, String renderUrlParamName) throws IOException {
|
||||
sendRedirect(location);
|
||||
if (renderUrlParamName != null) {
|
||||
setRenderParameter(renderUrlParamName, location);
|
||||
}
|
||||
}
|
||||
|
||||
public String getRedirectedUrl() {
|
||||
return this.redirectedUrl;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.Writer;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.portlet.BaseURL;
|
||||
import javax.portlet.PortletSecurityException;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.BaseURL} interface.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class MockBaseURL implements BaseURL {
|
||||
|
||||
public static final String URL_TYPE_RENDER = "render";
|
||||
|
||||
public static final String URL_TYPE_ACTION = "action";
|
||||
|
||||
private static final String ENCODING = "UTF-8";
|
||||
|
||||
|
||||
protected final Map<String, String[]> parameters = new LinkedHashMap<String, String[]>();
|
||||
|
||||
private boolean secure = false;
|
||||
|
||||
private final Map<String, String[]> properties = new LinkedHashMap<String, String[]>();
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// BaseURL methods
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
public void setParameter(String key, String value) {
|
||||
Assert.notNull(key, "Parameter key must be null");
|
||||
Assert.notNull(value, "Parameter value must not be null");
|
||||
this.parameters.put(key, new String[] {value});
|
||||
}
|
||||
|
||||
public void setParameter(String key, String[] values) {
|
||||
Assert.notNull(key, "Parameter key must be null");
|
||||
Assert.notNull(values, "Parameter values must not be null");
|
||||
this.parameters.put(key, values);
|
||||
}
|
||||
|
||||
public void setParameters(Map<String, String[]> parameters) {
|
||||
Assert.notNull(parameters, "Parameters Map must not be null");
|
||||
this.parameters.clear();
|
||||
this.parameters.putAll(parameters);
|
||||
}
|
||||
|
||||
public Set<String> getParameterNames() {
|
||||
return this.parameters.keySet();
|
||||
}
|
||||
|
||||
public String getParameter(String name) {
|
||||
String[] arr = this.parameters.get(name);
|
||||
return (arr != null && arr.length > 0 ? arr[0] : null);
|
||||
}
|
||||
|
||||
public String[] getParameterValues(String name) {
|
||||
return this.parameters.get(name);
|
||||
}
|
||||
|
||||
public Map<String, String[]> getParameterMap() {
|
||||
return Collections.unmodifiableMap(this.parameters);
|
||||
}
|
||||
|
||||
public void setSecure(boolean secure) throws PortletSecurityException {
|
||||
this.secure = secure;
|
||||
}
|
||||
|
||||
public boolean isSecure() {
|
||||
return this.secure;
|
||||
}
|
||||
|
||||
public void write(Writer out) throws IOException {
|
||||
out.write(toString());
|
||||
}
|
||||
|
||||
public void write(Writer out, boolean escapeXML) throws IOException {
|
||||
out.write(toString());
|
||||
}
|
||||
|
||||
public void addProperty(String key, String value) {
|
||||
String[] values = this.properties.get(key);
|
||||
if (values != null) {
|
||||
this.properties.put(key, StringUtils.addStringToArray(values, value));
|
||||
}
|
||||
else {
|
||||
this.properties.put(key, new String[] {value});
|
||||
}
|
||||
}
|
||||
|
||||
public void setProperty(String key, String value) {
|
||||
this.properties.put(key, new String[] {value});
|
||||
}
|
||||
|
||||
public Map<String, String[]> getProperties() {
|
||||
return Collections.unmodifiableMap(this.properties);
|
||||
}
|
||||
|
||||
|
||||
protected String encodeParameter(String name, String value) {
|
||||
try {
|
||||
return URLEncoder.encode(name, ENCODING) + "=" + URLEncoder.encode(value, ENCODING);
|
||||
}
|
||||
catch (UnsupportedEncodingException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected String encodeParameter(String name, String[] values) {
|
||||
try {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0, n = values.length; i < n; i++) {
|
||||
sb.append(i > 0 ? ";" : "").append(URLEncoder.encode(name, ENCODING)).append("=")
|
||||
.append(URLEncoder.encode(values[i], ENCODING));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
catch (UnsupportedEncodingException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import javax.portlet.CacheControl;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.CacheControl} interface.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class MockCacheControl implements CacheControl {
|
||||
|
||||
private int expirationTime = 0;
|
||||
|
||||
private boolean publicScope = false;
|
||||
|
||||
private String etag;
|
||||
|
||||
private boolean useCachedContent = false;
|
||||
|
||||
|
||||
public int getExpirationTime() {
|
||||
return this.expirationTime;
|
||||
}
|
||||
|
||||
public void setExpirationTime(int time) {
|
||||
this.expirationTime = time;
|
||||
}
|
||||
|
||||
public boolean isPublicScope() {
|
||||
return this.publicScope;
|
||||
}
|
||||
|
||||
public void setPublicScope(boolean publicScope) {
|
||||
this.publicScope = publicScope;
|
||||
}
|
||||
|
||||
public String getETag() {
|
||||
return this.etag;
|
||||
}
|
||||
|
||||
public void setETag(String token) {
|
||||
this.etag = token;
|
||||
}
|
||||
|
||||
public boolean useCachedContent() {
|
||||
return this.useCachedContent;
|
||||
}
|
||||
|
||||
public void setUseCachedContent(boolean useCachedContent) {
|
||||
this.useCachedContent = useCachedContent;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import javax.portlet.ClientDataRequest;
|
||||
import javax.portlet.PortalContext;
|
||||
import javax.portlet.PortletContext;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.ClientDataRequest} interface.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class MockClientDataRequest extends MockPortletRequest implements ClientDataRequest {
|
||||
|
||||
private String characterEncoding;
|
||||
|
||||
private byte[] content;
|
||||
|
||||
private String contentType;
|
||||
|
||||
private String method;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MockClientDataRequest with a default {@link MockPortalContext}
|
||||
* and a default {@link MockPortletContext}.
|
||||
* @see org.springframework.mock.web.portlet.MockPortalContext
|
||||
* @see org.springframework.mock.web.portlet.MockPortletContext
|
||||
*/
|
||||
public MockClientDataRequest() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockClientDataRequest with a default {@link MockPortalContext}.
|
||||
* @param portletContext the PortletContext that the request runs in
|
||||
*/
|
||||
public MockClientDataRequest(PortletContext portletContext) {
|
||||
super(portletContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockClientDataRequest.
|
||||
* @param portalContext the PortalContext that the request runs in
|
||||
* @param portletContext the PortletContext that the request runs in
|
||||
*/
|
||||
public MockClientDataRequest(PortalContext portalContext, PortletContext portletContext) {
|
||||
super(portalContext, portletContext);
|
||||
}
|
||||
|
||||
|
||||
public void setContent(byte[] content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public InputStream getPortletInputStream() throws IOException {
|
||||
if (this.content != null) {
|
||||
return new ByteArrayInputStream(this.content);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setCharacterEncoding(String characterEncoding) {
|
||||
this.characterEncoding = characterEncoding;
|
||||
}
|
||||
|
||||
public BufferedReader getReader() throws UnsupportedEncodingException {
|
||||
if (this.content != null) {
|
||||
InputStream sourceStream = new ByteArrayInputStream(this.content);
|
||||
Reader sourceReader = (this.characterEncoding != null) ?
|
||||
new InputStreamReader(sourceStream, this.characterEncoding) : new InputStreamReader(sourceStream);
|
||||
return new BufferedReader(sourceReader);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getCharacterEncoding() {
|
||||
return this.characterEncoding;
|
||||
}
|
||||
|
||||
public void setContentType(String contentType) {
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return this.contentType;
|
||||
}
|
||||
|
||||
public int getContentLength() {
|
||||
return (this.content != null ? content.length : -1);
|
||||
}
|
||||
|
||||
public void setMethod(String method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public String getMethod() {
|
||||
return this.method;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import java.io.Serializable;
|
||||
import javax.portlet.Event;
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.Event} interface.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
* @see MockEventRequest
|
||||
*/
|
||||
public class MockEvent implements Event {
|
||||
|
||||
private final QName name;
|
||||
|
||||
private final Serializable value;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MockEvent with the given name.
|
||||
* @param name the name of the event
|
||||
*/
|
||||
public MockEvent(QName name) {
|
||||
this.name = name;
|
||||
this.value = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockEvent with the given name and value.
|
||||
* @param name the name of the event
|
||||
* @param value the associated payload of the event
|
||||
*/
|
||||
public MockEvent(QName name, Serializable value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockEvent with the given name.
|
||||
* @param name the name of the event
|
||||
*/
|
||||
public MockEvent(String name) {
|
||||
this.name = new QName(name);
|
||||
this.value = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockEvent with the given name and value.
|
||||
* @param name the name of the event
|
||||
* @param value the associated payload of the event
|
||||
*/
|
||||
public MockEvent(String name, Serializable value) {
|
||||
this.name = new QName(name);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
|
||||
public QName getQName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name.getLocalPart();
|
||||
}
|
||||
|
||||
public Serializable getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import javax.portlet.Event;
|
||||
import javax.portlet.EventRequest;
|
||||
import javax.portlet.PortalContext;
|
||||
import javax.portlet.PortletContext;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.EventRequest} interface.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class MockEventRequest extends MockPortletRequest implements EventRequest {
|
||||
|
||||
private final Event event;
|
||||
|
||||
private String method;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MockEventRequest with a default {@link MockPortalContext}
|
||||
* and a default {@link MockPortletContext}.
|
||||
* @param event the event that this request wraps
|
||||
* @see MockEvent
|
||||
*/
|
||||
public MockEventRequest(Event event) {
|
||||
super();
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockEventRequest with a default {@link MockPortalContext}.
|
||||
* @param event the event that this request wraps
|
||||
* @param portletContext the PortletContext that the request runs in
|
||||
* @see MockEvent
|
||||
*/
|
||||
public MockEventRequest(Event event, PortletContext portletContext) {
|
||||
super(portletContext);
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockEventRequest.
|
||||
* @param event the event that this request wraps
|
||||
* @param portalContext the PortletContext that the request runs in
|
||||
* @param portletContext the PortletContext that the request runs in
|
||||
*/
|
||||
public MockEventRequest(Event event, PortalContext portalContext, PortletContext portletContext) {
|
||||
super(portalContext, portletContext);
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected String getLifecyclePhase() {
|
||||
return EVENT_PHASE;
|
||||
}
|
||||
|
||||
public Event getEvent() {
|
||||
return this.event;
|
||||
}
|
||||
|
||||
public void setMethod(String method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public String getMethod() {
|
||||
return this.method;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import javax.portlet.EventRequest;
|
||||
import javax.portlet.EventResponse;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.EventResponse} interface.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class MockEventResponse extends MockStateAwareResponse implements EventResponse {
|
||||
|
||||
public void setRenderParameters(EventRequest request) {
|
||||
setRenderParameters(request.getParameterMap());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.Writer;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Locale;
|
||||
import javax.portlet.CacheControl;
|
||||
import javax.portlet.MimeResponse;
|
||||
import javax.portlet.PortalContext;
|
||||
import javax.portlet.PortletRequest;
|
||||
import javax.portlet.PortletURL;
|
||||
import javax.portlet.ResourceURL;
|
||||
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.MimeResponse} interface.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class MockMimeResponse extends MockPortletResponse implements MimeResponse {
|
||||
|
||||
private PortletRequest request;
|
||||
|
||||
private String contentType;
|
||||
|
||||
private String characterEncoding = WebUtils.DEFAULT_CHARACTER_ENCODING;
|
||||
|
||||
private PrintWriter writer;
|
||||
|
||||
private Locale locale = Locale.getDefault();
|
||||
|
||||
private int bufferSize = 4096;
|
||||
|
||||
private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
|
||||
private final CacheControl cacheControl = new MockCacheControl();
|
||||
|
||||
private boolean committed;
|
||||
|
||||
private String includedUrl;
|
||||
|
||||
private String forwardedUrl;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MockMimeResponse with a default {@link MockPortalContext}.
|
||||
* @see org.springframework.mock.web.portlet.MockPortalContext
|
||||
*/
|
||||
public MockMimeResponse() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockMimeResponse.
|
||||
* @param portalContext the PortalContext defining the supported
|
||||
* PortletModes and WindowStates
|
||||
*/
|
||||
public MockMimeResponse(PortalContext portalContext) {
|
||||
super(portalContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockMimeResponse.
|
||||
* @param portalContext the PortalContext defining the supported
|
||||
* PortletModes and WindowStates
|
||||
* @param request the corresponding render/resource request that this response
|
||||
* is being generated for
|
||||
*/
|
||||
public MockMimeResponse(PortalContext portalContext, PortletRequest request) {
|
||||
super(portalContext);
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// RenderResponse methods
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
public void setContentType(String contentType) {
|
||||
if (this.request != null) {
|
||||
Enumeration<String> supportedTypes = this.request.getResponseContentTypes();
|
||||
if (!CollectionUtils.contains(supportedTypes, contentType)) {
|
||||
throw new IllegalArgumentException("Content type [" + contentType + "] not in supported list: " +
|
||||
Collections.list(supportedTypes));
|
||||
}
|
||||
}
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return this.contentType;
|
||||
}
|
||||
|
||||
public void setCharacterEncoding(String characterEncoding) {
|
||||
this.characterEncoding = characterEncoding;
|
||||
}
|
||||
|
||||
public String getCharacterEncoding() {
|
||||
return this.characterEncoding;
|
||||
}
|
||||
|
||||
public PrintWriter getWriter() throws UnsupportedEncodingException {
|
||||
if (this.writer == null) {
|
||||
Writer targetWriter = (this.characterEncoding != null
|
||||
? new OutputStreamWriter(this.outputStream, this.characterEncoding)
|
||||
: new OutputStreamWriter(this.outputStream));
|
||||
this.writer = new PrintWriter(targetWriter);
|
||||
}
|
||||
return this.writer;
|
||||
}
|
||||
|
||||
public byte[] getContentAsByteArray() {
|
||||
flushBuffer();
|
||||
return this.outputStream.toByteArray();
|
||||
}
|
||||
|
||||
public String getContentAsString() throws UnsupportedEncodingException {
|
||||
flushBuffer();
|
||||
return (this.characterEncoding != null)
|
||||
? this.outputStream.toString(this.characterEncoding)
|
||||
: this.outputStream.toString();
|
||||
}
|
||||
|
||||
public void setLocale(Locale locale) {
|
||||
this.locale = locale;
|
||||
}
|
||||
|
||||
public Locale getLocale() {
|
||||
return this.locale;
|
||||
}
|
||||
|
||||
public void setBufferSize(int bufferSize) {
|
||||
this.bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
public int getBufferSize() {
|
||||
return this.bufferSize;
|
||||
}
|
||||
|
||||
public void flushBuffer() {
|
||||
if (this.writer != null) {
|
||||
this.writer.flush();
|
||||
}
|
||||
if (this.outputStream != null) {
|
||||
try {
|
||||
this.outputStream.flush();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException("Could not flush OutputStream: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
this.committed = true;
|
||||
}
|
||||
|
||||
public void resetBuffer() {
|
||||
if (this.committed) {
|
||||
throw new IllegalStateException("Cannot reset buffer - response is already committed");
|
||||
}
|
||||
this.outputStream.reset();
|
||||
}
|
||||
|
||||
public void setCommitted(boolean committed) {
|
||||
this.committed = committed;
|
||||
}
|
||||
|
||||
public boolean isCommitted() {
|
||||
return this.committed;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
resetBuffer();
|
||||
this.characterEncoding = null;
|
||||
this.contentType = null;
|
||||
this.locale = null;
|
||||
}
|
||||
|
||||
public OutputStream getPortletOutputStream() throws IOException {
|
||||
return this.outputStream;
|
||||
}
|
||||
|
||||
public PortletURL createRenderURL() {
|
||||
return new MockPortletURL(getPortalContext(), MockPortletURL.URL_TYPE_RENDER);
|
||||
}
|
||||
|
||||
public PortletURL createActionURL() {
|
||||
return new MockPortletURL(getPortalContext(), MockPortletURL.URL_TYPE_ACTION);
|
||||
}
|
||||
|
||||
public ResourceURL createResourceURL() {
|
||||
return new MockResourceURL();
|
||||
}
|
||||
|
||||
public CacheControl getCacheControl() {
|
||||
return this.cacheControl;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Methods for MockPortletRequestDispatcher
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
public void setIncludedUrl(String includedUrl) {
|
||||
this.includedUrl = includedUrl;
|
||||
}
|
||||
|
||||
public String getIncludedUrl() {
|
||||
return this.includedUrl;
|
||||
}
|
||||
|
||||
public void setForwardedUrl(String forwardedUrl) {
|
||||
this.forwardedUrl = forwardedUrl;
|
||||
}
|
||||
|
||||
public String getForwardedUrl() {
|
||||
return this.forwardedUrl;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.mock.web.portlet;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.portlet.multipart.MultipartActionRequest;
|
||||
|
||||
/**
|
||||
* Mock implementation of the
|
||||
* {@link org.springframework.web.portlet.multipart.MultipartActionRequest} interface.
|
||||
*
|
||||
* <p>Useful for testing application controllers that access multipart uploads.
|
||||
* The {@link org.springframework.mock.web.MockMultipartFile} can be used to
|
||||
* populate these mock requests with files.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Arjen Poutsma
|
||||
* @since 2.0
|
||||
* @see org.springframework.mock.web.MockMultipartFile
|
||||
*/
|
||||
public class MockMultipartActionRequest extends MockActionRequest implements MultipartActionRequest {
|
||||
|
||||
private final MultiValueMap<String, MultipartFile> multipartFiles =
|
||||
new LinkedMultiValueMap<String, MultipartFile>();
|
||||
|
||||
|
||||
/**
|
||||
* Add a file to this request. The parameter name from the multipart
|
||||
* form is taken from the {@link org.springframework.web.multipart.MultipartFile#getName()}.
|
||||
* @param file multipart file to be added
|
||||
*/
|
||||
public void addFile(MultipartFile file) {
|
||||
Assert.notNull(file, "MultipartFile must not be null");
|
||||
this.multipartFiles.add(file.getName(), file);
|
||||
}
|
||||
|
||||
public Iterator<String> getFileNames() {
|
||||
return this.multipartFiles.keySet().iterator();
|
||||
}
|
||||
|
||||
public MultipartFile getFile(String name) {
|
||||
return this.multipartFiles.getFirst(name);
|
||||
}
|
||||
|
||||
public List<MultipartFile> getFiles(String name) {
|
||||
List<MultipartFile> multipartFiles = this.multipartFiles.get(name);
|
||||
if (multipartFiles != null) {
|
||||
return multipartFiles;
|
||||
}
|
||||
else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, MultipartFile> getFileMap() {
|
||||
return this.multipartFiles.toSingleValueMap();
|
||||
}
|
||||
|
||||
public MultiValueMap<String, MultipartFile> getMultiFileMap() {
|
||||
return new LinkedMultiValueMap<String, MultipartFile>(this.multipartFiles);
|
||||
}
|
||||
|
||||
public String getMultipartContentType(String paramOrFileName) {
|
||||
MultipartFile file = getFile(paramOrFileName);
|
||||
if (file != null) {
|
||||
return file.getContentType();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.portlet.PortalContext;
|
||||
import javax.portlet.PortletMode;
|
||||
import javax.portlet.WindowState;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.PortalContext} interface.
|
||||
*
|
||||
* @author John A. Lewis
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MockPortalContext implements PortalContext {
|
||||
|
||||
private final Map<String, String> properties = new HashMap<String, String>();
|
||||
|
||||
private final List<PortletMode> portletModes;
|
||||
|
||||
private final List<WindowState> windowStates;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MockPortalContext
|
||||
* with default PortletModes (VIEW, EDIT, HELP)
|
||||
* and default WindowStates (NORMAL, MAXIMIZED, MINIMIZED).
|
||||
* @see javax.portlet.PortletMode
|
||||
* @see javax.portlet.WindowState
|
||||
*/
|
||||
public MockPortalContext() {
|
||||
this.portletModes = new ArrayList<PortletMode>(3);
|
||||
this.portletModes.add(PortletMode.VIEW);
|
||||
this.portletModes.add(PortletMode.EDIT);
|
||||
this.portletModes.add(PortletMode.HELP);
|
||||
|
||||
this.windowStates = new ArrayList<WindowState>(3);
|
||||
this.windowStates.add(WindowState.NORMAL);
|
||||
this.windowStates.add(WindowState.MAXIMIZED);
|
||||
this.windowStates.add(WindowState.MINIMIZED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockPortalContext with the given PortletModes and WindowStates.
|
||||
* @param supportedPortletModes the List of supported PortletMode instances
|
||||
* @param supportedWindowStates the List of supported WindowState instances
|
||||
* @see javax.portlet.PortletMode
|
||||
* @see javax.portlet.WindowState
|
||||
*/
|
||||
public MockPortalContext(List<PortletMode> supportedPortletModes, List<WindowState> supportedWindowStates) {
|
||||
this.portletModes = new ArrayList<PortletMode>(supportedPortletModes);
|
||||
this.windowStates = new ArrayList<WindowState>(supportedWindowStates);
|
||||
}
|
||||
|
||||
|
||||
public String getPortalInfo() {
|
||||
return "MockPortal/1.0";
|
||||
}
|
||||
|
||||
public void setProperty(String name, String value) {
|
||||
this.properties.put(name, value);
|
||||
}
|
||||
|
||||
public String getProperty(String name) {
|
||||
return this.properties.get(name);
|
||||
}
|
||||
|
||||
public Enumeration<String> getPropertyNames() {
|
||||
return Collections.enumeration(this.properties.keySet());
|
||||
}
|
||||
|
||||
public Enumeration<PortletMode> getSupportedPortletModes() {
|
||||
return Collections.enumeration(this.portletModes);
|
||||
}
|
||||
|
||||
public Enumeration<WindowState> getSupportedWindowStates() {
|
||||
return Collections.enumeration(this.windowStates);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
import javax.portlet.PortletConfig;
|
||||
import javax.portlet.PortletContext;
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.PortletConfig} interface.
|
||||
*
|
||||
* @author John A. Lewis
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MockPortletConfig implements PortletConfig {
|
||||
|
||||
private final PortletContext portletContext;
|
||||
|
||||
private final String portletName;
|
||||
|
||||
private final Map<Locale, ResourceBundle> resourceBundles = new HashMap<Locale, ResourceBundle>();
|
||||
|
||||
private final Map<String, String> initParameters = new LinkedHashMap<String, String>();
|
||||
|
||||
private final Set<String> publicRenderParameterNames = new LinkedHashSet<String>();
|
||||
|
||||
private String defaultNamespace = XMLConstants.NULL_NS_URI;
|
||||
|
||||
private final Set<QName> publishingEventQNames = new LinkedHashSet<QName>();
|
||||
|
||||
private final Set<QName> processingEventQNames = new LinkedHashSet<QName>();
|
||||
|
||||
private final Set<Locale> supportedLocales = new LinkedHashSet<Locale>();
|
||||
|
||||
private final Map<String, String[]> containerRuntimeOptions = new LinkedHashMap<String, String[]>();
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MockPortletConfig with a default {@link MockPortletContext}.
|
||||
*/
|
||||
public MockPortletConfig() {
|
||||
this(null, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockPortletConfig with a default {@link MockPortletContext}.
|
||||
* @param portletName the name of the portlet
|
||||
*/
|
||||
public MockPortletConfig(String portletName) {
|
||||
this(null, portletName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockPortletConfig.
|
||||
* @param portletContext the PortletContext that the portlet runs in
|
||||
*/
|
||||
public MockPortletConfig(PortletContext portletContext) {
|
||||
this(portletContext, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockPortletConfig.
|
||||
* @param portletContext the PortletContext that the portlet runs in
|
||||
* @param portletName the name of the portlet
|
||||
*/
|
||||
public MockPortletConfig(PortletContext portletContext, String portletName) {
|
||||
this.portletContext = (portletContext != null ? portletContext : new MockPortletContext());
|
||||
this.portletName = portletName;
|
||||
}
|
||||
|
||||
|
||||
public String getPortletName() {
|
||||
return this.portletName;
|
||||
}
|
||||
|
||||
public PortletContext getPortletContext() {
|
||||
return this.portletContext;
|
||||
}
|
||||
|
||||
public void setResourceBundle(Locale locale, ResourceBundle resourceBundle) {
|
||||
Assert.notNull(locale, "Locale must not be null");
|
||||
this.resourceBundles.put(locale, resourceBundle);
|
||||
}
|
||||
|
||||
public ResourceBundle getResourceBundle(Locale locale) {
|
||||
Assert.notNull(locale, "Locale must not be null");
|
||||
return this.resourceBundles.get(locale);
|
||||
}
|
||||
|
||||
public void addInitParameter(String name, String value) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
this.initParameters.put(name, value);
|
||||
}
|
||||
|
||||
public String getInitParameter(String name) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
return this.initParameters.get(name);
|
||||
}
|
||||
|
||||
public Enumeration<String> getInitParameterNames() {
|
||||
return Collections.enumeration(this.initParameters.keySet());
|
||||
}
|
||||
|
||||
public void addPublicRenderParameterName(String name) {
|
||||
this.publicRenderParameterNames.add(name);
|
||||
}
|
||||
|
||||
public Enumeration<String> getPublicRenderParameterNames() {
|
||||
return Collections.enumeration(this.publicRenderParameterNames);
|
||||
}
|
||||
|
||||
public void setDefaultNamespace(String defaultNamespace) {
|
||||
this.defaultNamespace = defaultNamespace;
|
||||
}
|
||||
|
||||
public String getDefaultNamespace() {
|
||||
return this.defaultNamespace;
|
||||
}
|
||||
|
||||
public void addPublishingEventQName(QName name) {
|
||||
this.publishingEventQNames.add(name);
|
||||
}
|
||||
|
||||
public Enumeration<QName> getPublishingEventQNames() {
|
||||
return Collections.enumeration(this.publishingEventQNames);
|
||||
}
|
||||
|
||||
public void addProcessingEventQName(QName name) {
|
||||
this.processingEventQNames.add(name);
|
||||
}
|
||||
|
||||
public Enumeration<QName> getProcessingEventQNames() {
|
||||
return Collections.enumeration(this.processingEventQNames);
|
||||
}
|
||||
|
||||
public void addSupportedLocale(Locale locale) {
|
||||
this.supportedLocales.add(locale);
|
||||
}
|
||||
|
||||
public Enumeration<Locale> getSupportedLocales() {
|
||||
return Collections.enumeration(this.supportedLocales);
|
||||
}
|
||||
|
||||
public void addContainerRuntimeOption(String key, String value) {
|
||||
this.containerRuntimeOptions.put(key, new String[] {value});
|
||||
}
|
||||
|
||||
public void addContainerRuntimeOption(String key, String[] values) {
|
||||
this.containerRuntimeOptions.put(key, values);
|
||||
}
|
||||
|
||||
public Map<String, String[]> getContainerRuntimeOptions() {
|
||||
return Collections.unmodifiableMap(this.containerRuntimeOptions);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.mock.web.portlet;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Vector;
|
||||
import javax.activation.FileTypeMap;
|
||||
import javax.portlet.PortletContext;
|
||||
import javax.portlet.PortletRequestDispatcher;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.PortletContext} interface.
|
||||
*
|
||||
* @author John A. Lewis
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MockPortletContext implements PortletContext {
|
||||
|
||||
private static final String TEMP_DIR_SYSTEM_PROPERTY = "java.io.tmpdir";
|
||||
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final String resourceBasePath;
|
||||
|
||||
private final ResourceLoader resourceLoader;
|
||||
|
||||
private final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
|
||||
|
||||
private final Map<String, String> initParameters = new LinkedHashMap<String, String>();
|
||||
|
||||
private String portletContextName = "MockPortletContext";
|
||||
|
||||
private Set<String> containerRuntimeOptions = new LinkedHashSet<String>();
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MockPortletContext with no base path and a
|
||||
* DefaultResourceLoader (i.e. the classpath root as WAR root).
|
||||
* @see org.springframework.core.io.DefaultResourceLoader
|
||||
*/
|
||||
public MockPortletContext() {
|
||||
this("", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockPortletContext using a DefaultResourceLoader.
|
||||
* @param resourceBasePath the WAR root directory (should not end with a slash)
|
||||
* @see org.springframework.core.io.DefaultResourceLoader
|
||||
*/
|
||||
public MockPortletContext(String resourceBasePath) {
|
||||
this(resourceBasePath, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockPortletContext, using the specified ResourceLoader
|
||||
* and no base path.
|
||||
* @param resourceLoader the ResourceLoader to use (or null for the default)
|
||||
*/
|
||||
public MockPortletContext(ResourceLoader resourceLoader) {
|
||||
this("", resourceLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockPortletContext.
|
||||
* @param resourceBasePath the WAR root directory (should not end with a slash)
|
||||
* @param resourceLoader the ResourceLoader to use (or null for the default)
|
||||
*/
|
||||
public MockPortletContext(String resourceBasePath, ResourceLoader resourceLoader) {
|
||||
this.resourceBasePath = (resourceBasePath != null ? resourceBasePath : "");
|
||||
this.resourceLoader = (resourceLoader != null ? resourceLoader : new DefaultResourceLoader());
|
||||
|
||||
// Use JVM temp dir as PortletContext temp dir.
|
||||
String tempDir = System.getProperty(TEMP_DIR_SYSTEM_PROPERTY);
|
||||
if (tempDir != null) {
|
||||
this.attributes.put(WebUtils.TEMP_DIR_CONTEXT_ATTRIBUTE, new File(tempDir));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a full resource location for the given path,
|
||||
* prepending the resource base path of this MockPortletContext.
|
||||
* @param path the path as specified
|
||||
* @return the full resource path
|
||||
*/
|
||||
protected String getResourceLocation(String path) {
|
||||
if (!path.startsWith("/")) {
|
||||
path = "/" + path;
|
||||
}
|
||||
return this.resourceBasePath + path;
|
||||
}
|
||||
|
||||
|
||||
public String getServerInfo() {
|
||||
return "MockPortal/1.0";
|
||||
}
|
||||
|
||||
public PortletRequestDispatcher getRequestDispatcher(String path) {
|
||||
if (!path.startsWith("/")) {
|
||||
throw new IllegalArgumentException(
|
||||
"PortletRequestDispatcher path at PortletContext level must start with '/'");
|
||||
}
|
||||
return new MockPortletRequestDispatcher(path);
|
||||
}
|
||||
|
||||
public PortletRequestDispatcher getNamedDispatcher(String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public InputStream getResourceAsStream(String path) {
|
||||
Resource resource = this.resourceLoader.getResource(getResourceLocation(path));
|
||||
try {
|
||||
return resource.getInputStream();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
logger.info("Couldn't open InputStream for " + resource, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public int getMajorVersion() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
public int getMinorVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public String getMimeType(String filePath) {
|
||||
return MimeTypeResolver.getMimeType(filePath);
|
||||
}
|
||||
|
||||
public String getRealPath(String path) {
|
||||
Resource resource = this.resourceLoader.getResource(getResourceLocation(path));
|
||||
try {
|
||||
return resource.getFile().getAbsolutePath();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
logger.info("Couldn't determine real path of resource " + resource, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> getResourcePaths(String path) {
|
||||
Resource resource = this.resourceLoader.getResource(getResourceLocation(path));
|
||||
try {
|
||||
File file = resource.getFile();
|
||||
String[] fileList = file.list();
|
||||
String prefix = (path.endsWith("/") ? path : path + "/");
|
||||
Set<String> resourcePaths = new HashSet<String>(fileList.length);
|
||||
for (String fileEntry : fileList) {
|
||||
resourcePaths.add(prefix + fileEntry);
|
||||
}
|
||||
return resourcePaths;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
logger.info("Couldn't get resource paths for " + resource, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public URL getResource(String path) throws MalformedURLException {
|
||||
Resource resource = this.resourceLoader.getResource(getResourceLocation(path));
|
||||
try {
|
||||
return resource.getURL();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
logger.info("Couldn't get URL for " + resource, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Object getAttribute(String name) {
|
||||
return this.attributes.get(name);
|
||||
}
|
||||
|
||||
public Enumeration<String> getAttributeNames() {
|
||||
return new Vector<String>(this.attributes.keySet()).elements();
|
||||
}
|
||||
|
||||
public void setAttribute(String name, Object value) {
|
||||
if (value != null) {
|
||||
this.attributes.put(name, value);
|
||||
}
|
||||
else {
|
||||
this.attributes.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAttribute(String name) {
|
||||
this.attributes.remove(name);
|
||||
}
|
||||
|
||||
public void addInitParameter(String name, String value) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
this.initParameters.put(name, value);
|
||||
}
|
||||
|
||||
public String getInitParameter(String name) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
return this.initParameters.get(name);
|
||||
}
|
||||
|
||||
public Enumeration<String> getInitParameterNames() {
|
||||
return Collections.enumeration(this.initParameters.keySet());
|
||||
}
|
||||
|
||||
public void log(String message) {
|
||||
logger.info(message);
|
||||
}
|
||||
|
||||
public void log(String message, Throwable t) {
|
||||
logger.info(message, t);
|
||||
}
|
||||
|
||||
public void setPortletContextName(String portletContextName) {
|
||||
this.portletContextName = portletContextName;
|
||||
}
|
||||
|
||||
public String getPortletContextName() {
|
||||
return this.portletContextName;
|
||||
}
|
||||
|
||||
public void addContainerRuntimeOption(String key) {
|
||||
this.containerRuntimeOptions.add(key);
|
||||
}
|
||||
|
||||
public Enumeration<String> getContainerRuntimeOptions() {
|
||||
return Collections.enumeration(this.containerRuntimeOptions);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inner factory class used to just introduce a Java Activation Framework
|
||||
* dependency when actually asked to resolve a MIME type.
|
||||
*/
|
||||
private static class MimeTypeResolver {
|
||||
|
||||
public static String getMimeType(String filePath) {
|
||||
return FileTypeMap.getDefaultFileTypeMap().getContentType(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.portlet.PortletPreferences;
|
||||
import javax.portlet.PreferencesValidator;
|
||||
import javax.portlet.ReadOnlyException;
|
||||
import javax.portlet.ValidatorException;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.PortletPreferences} interface.
|
||||
*
|
||||
* @author John A. Lewis
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MockPortletPreferences implements PortletPreferences {
|
||||
|
||||
private PreferencesValidator preferencesValidator;
|
||||
|
||||
private final Map<String, String[]> preferences = new LinkedHashMap<String, String[]>();
|
||||
|
||||
private final Set<String> readOnly = new HashSet<String>();
|
||||
|
||||
|
||||
public void setReadOnly(String key, boolean readOnly) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
if (readOnly) {
|
||||
this.readOnly.add(key);
|
||||
}
|
||||
else {
|
||||
this.readOnly.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isReadOnly(String key) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
return this.readOnly.contains(key);
|
||||
}
|
||||
|
||||
public String getValue(String key, String def) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
String[] values = this.preferences.get(key);
|
||||
return (values != null && values.length > 0 ? values[0] : def);
|
||||
}
|
||||
|
||||
public String[] getValues(String key, String[] def) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
String[] values = this.preferences.get(key);
|
||||
return (values != null && values.length > 0 ? values : def);
|
||||
}
|
||||
|
||||
public void setValue(String key, String value) throws ReadOnlyException {
|
||||
setValues(key, new String[] {value});
|
||||
}
|
||||
|
||||
public void setValues(String key, String[] values) throws ReadOnlyException {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
if (isReadOnly(key)) {
|
||||
throw new ReadOnlyException("Preference '" + key + "' is read-only");
|
||||
}
|
||||
this.preferences.put(key, values);
|
||||
}
|
||||
|
||||
public Enumeration<String> getNames() {
|
||||
return Collections.enumeration(this.preferences.keySet());
|
||||
}
|
||||
|
||||
public Map<String, String[]> getMap() {
|
||||
return Collections.unmodifiableMap(this.preferences);
|
||||
}
|
||||
|
||||
public void reset(String key) throws ReadOnlyException {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
if (isReadOnly(key)) {
|
||||
throw new ReadOnlyException("Preference '" + key + "' is read-only");
|
||||
}
|
||||
this.preferences.remove(key);
|
||||
}
|
||||
|
||||
public void setPreferencesValidator(PreferencesValidator preferencesValidator) {
|
||||
this.preferencesValidator = preferencesValidator;
|
||||
}
|
||||
|
||||
public void store() throws IOException, ValidatorException {
|
||||
if (this.preferencesValidator != null) {
|
||||
this.preferencesValidator.validate(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,532 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.mock.web.portlet;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Vector;
|
||||
|
||||
import javax.portlet.PortalContext;
|
||||
import javax.portlet.PortletContext;
|
||||
import javax.portlet.PortletMode;
|
||||
import javax.portlet.PortletPreferences;
|
||||
import javax.portlet.PortletRequest;
|
||||
import javax.portlet.PortletSession;
|
||||
import javax.portlet.WindowState;
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.PortletRequest} interface.
|
||||
*
|
||||
* @author John A. Lewis
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MockPortletRequest implements PortletRequest {
|
||||
|
||||
private boolean active = true;
|
||||
|
||||
private final PortalContext portalContext;
|
||||
|
||||
private final PortletContext portletContext;
|
||||
|
||||
private PortletSession session;
|
||||
|
||||
private WindowState windowState = WindowState.NORMAL;
|
||||
|
||||
private PortletMode portletMode = PortletMode.VIEW;
|
||||
|
||||
private PortletPreferences portletPreferences = new MockPortletPreferences();
|
||||
|
||||
private final Map<String, List<String>> properties = new LinkedHashMap<String, List<String>>();
|
||||
|
||||
private final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
|
||||
|
||||
private final Map<String, String[]> parameters = new LinkedHashMap<String, String[]>();
|
||||
|
||||
private String authType = null;
|
||||
|
||||
private String contextPath = "";
|
||||
|
||||
private String remoteUser = null;
|
||||
|
||||
private Principal userPrincipal = null;
|
||||
|
||||
private final Set<String> userRoles = new HashSet<String>();
|
||||
|
||||
private boolean secure = false;
|
||||
|
||||
private boolean requestedSessionIdValid = true;
|
||||
|
||||
private final List<String> responseContentTypes = new LinkedList<String>();
|
||||
|
||||
private final List<Locale> locales = new LinkedList<Locale>();
|
||||
|
||||
private String scheme = "http";
|
||||
|
||||
private String serverName = "localhost";
|
||||
|
||||
private int serverPort = 80;
|
||||
|
||||
private String windowID;
|
||||
|
||||
private Cookie[] cookies;
|
||||
|
||||
private final Set<String> publicParameterNames = new HashSet<String>();
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MockPortletRequest with a default {@link MockPortalContext}
|
||||
* and a default {@link MockPortletContext}.
|
||||
*
|
||||
* @see MockPortalContext
|
||||
* @see MockPortletContext
|
||||
*/
|
||||
public MockPortletRequest() {
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockPortletRequest with a default {@link MockPortalContext}.
|
||||
*
|
||||
* @param portletContext the PortletContext that the request runs in
|
||||
* @see MockPortalContext
|
||||
*/
|
||||
public MockPortletRequest(PortletContext portletContext) {
|
||||
this(null, portletContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockPortletRequest.
|
||||
*
|
||||
* @param portalContext the PortalContext that the request runs in
|
||||
* @param portletContext the PortletContext that the request runs in
|
||||
*/
|
||||
public MockPortletRequest(PortalContext portalContext, PortletContext portletContext) {
|
||||
this.portalContext = (portalContext != null ? portalContext : new MockPortalContext());
|
||||
this.portletContext = (portletContext != null ? portletContext : new MockPortletContext());
|
||||
this.responseContentTypes.add("text/html");
|
||||
this.locales.add(Locale.ENGLISH);
|
||||
this.attributes.put(LIFECYCLE_PHASE, getLifecyclePhase());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Lifecycle methods
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Return the Portlet 2.0 lifecycle id for the current phase.
|
||||
*/
|
||||
protected String getLifecyclePhase() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this request is still active (that is, not completed yet).
|
||||
*/
|
||||
public boolean isActive() {
|
||||
return this.active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this request as completed.
|
||||
*/
|
||||
public void close() {
|
||||
this.active = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this request is still active (that is, not completed yet),
|
||||
* throwing an IllegalStateException if not active anymore.
|
||||
*/
|
||||
protected void checkActive() throws IllegalStateException {
|
||||
if (!this.active) {
|
||||
throw new IllegalStateException("Request is not active anymore");
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// PortletRequest methods
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
public boolean isWindowStateAllowed(WindowState windowState) {
|
||||
return CollectionUtils.contains(this.portalContext.getSupportedWindowStates(), windowState);
|
||||
}
|
||||
|
||||
public boolean isPortletModeAllowed(PortletMode portletMode) {
|
||||
return CollectionUtils.contains(this.portalContext.getSupportedPortletModes(), portletMode);
|
||||
}
|
||||
|
||||
public void setPortletMode(PortletMode portletMode) {
|
||||
Assert.notNull(portletMode, "PortletMode must not be null");
|
||||
this.portletMode = portletMode;
|
||||
}
|
||||
|
||||
public PortletMode getPortletMode() {
|
||||
return this.portletMode;
|
||||
}
|
||||
|
||||
public void setWindowState(WindowState windowState) {
|
||||
Assert.notNull(windowState, "WindowState must not be null");
|
||||
this.windowState = windowState;
|
||||
}
|
||||
|
||||
public WindowState getWindowState() {
|
||||
return this.windowState;
|
||||
}
|
||||
|
||||
public void setPreferences(PortletPreferences preferences) {
|
||||
Assert.notNull(preferences, "PortletPreferences must not be null");
|
||||
this.portletPreferences = preferences;
|
||||
}
|
||||
|
||||
public PortletPreferences getPreferences() {
|
||||
return this.portletPreferences;
|
||||
}
|
||||
|
||||
public void setSession(PortletSession session) {
|
||||
this.session = session;
|
||||
if (session instanceof MockPortletSession) {
|
||||
MockPortletSession mockSession = ((MockPortletSession) session);
|
||||
mockSession.access();
|
||||
}
|
||||
}
|
||||
|
||||
public PortletSession getPortletSession() {
|
||||
return getPortletSession(true);
|
||||
}
|
||||
|
||||
public PortletSession getPortletSession(boolean create) {
|
||||
checkActive();
|
||||
// Reset session if invalidated.
|
||||
if (this.session instanceof MockPortletSession && ((MockPortletSession) this.session).isInvalid()) {
|
||||
this.session = null;
|
||||
}
|
||||
// Create new session if necessary.
|
||||
if (this.session == null && create) {
|
||||
this.session = new MockPortletSession(this.portletContext);
|
||||
}
|
||||
return this.session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a single value for the specified property.
|
||||
* <p>
|
||||
* If there are already one or more values registered for the given property
|
||||
* key, they will be replaced.
|
||||
*/
|
||||
public void setProperty(String key, String value) {
|
||||
Assert.notNull(key, "Property key must not be null");
|
||||
List<String> list = new LinkedList<String>();
|
||||
list.add(value);
|
||||
this.properties.put(key, list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single value for the specified property.
|
||||
* <p>
|
||||
* If there are already one or more values registered for the given property
|
||||
* key, the given value will be added to the end of the list.
|
||||
*/
|
||||
public void addProperty(String key, String value) {
|
||||
Assert.notNull(key, "Property key must not be null");
|
||||
List<String> oldList = this.properties.get(key);
|
||||
if (oldList != null) {
|
||||
oldList.add(value);
|
||||
}
|
||||
else {
|
||||
List<String> list = new LinkedList<String>();
|
||||
list.add(value);
|
||||
this.properties.put(key, list);
|
||||
}
|
||||
}
|
||||
|
||||
public String getProperty(String key) {
|
||||
Assert.notNull(key, "Property key must not be null");
|
||||
List<String> list = this.properties.get(key);
|
||||
return (list != null && list.size() > 0 ? (String) list.get(0) : null);
|
||||
}
|
||||
|
||||
public Enumeration<String> getProperties(String key) {
|
||||
Assert.notNull(key, "property key must not be null");
|
||||
return Collections.enumeration(this.properties.get(key));
|
||||
}
|
||||
|
||||
public Enumeration<String> getPropertyNames() {
|
||||
return Collections.enumeration(this.properties.keySet());
|
||||
}
|
||||
|
||||
public PortalContext getPortalContext() {
|
||||
return this.portalContext;
|
||||
}
|
||||
|
||||
public void setAuthType(String authType) {
|
||||
this.authType = authType;
|
||||
}
|
||||
|
||||
public String getAuthType() {
|
||||
return this.authType;
|
||||
}
|
||||
|
||||
public void setContextPath(String contextPath) {
|
||||
this.contextPath = contextPath;
|
||||
}
|
||||
|
||||
public String getContextPath() {
|
||||
return this.contextPath;
|
||||
}
|
||||
|
||||
public void setRemoteUser(String remoteUser) {
|
||||
this.remoteUser = remoteUser;
|
||||
}
|
||||
|
||||
public String getRemoteUser() {
|
||||
return this.remoteUser;
|
||||
}
|
||||
|
||||
public void setUserPrincipal(Principal userPrincipal) {
|
||||
this.userPrincipal = userPrincipal;
|
||||
}
|
||||
|
||||
public Principal getUserPrincipal() {
|
||||
return this.userPrincipal;
|
||||
}
|
||||
|
||||
public void addUserRole(String role) {
|
||||
this.userRoles.add(role);
|
||||
}
|
||||
|
||||
public boolean isUserInRole(String role) {
|
||||
return this.userRoles.contains(role);
|
||||
}
|
||||
|
||||
public Object getAttribute(String name) {
|
||||
checkActive();
|
||||
return this.attributes.get(name);
|
||||
}
|
||||
|
||||
public Enumeration<String> getAttributeNames() {
|
||||
checkActive();
|
||||
return new Vector<String>(this.attributes.keySet()).elements();
|
||||
}
|
||||
|
||||
public void setParameters(Map<String, String[]> parameters) {
|
||||
Assert.notNull(parameters, "Parameters Map must not be null");
|
||||
this.parameters.clear();
|
||||
this.parameters.putAll(parameters);
|
||||
}
|
||||
|
||||
public void setParameter(String key, String value) {
|
||||
Assert.notNull(key, "Parameter key must be null");
|
||||
Assert.notNull(value, "Parameter value must not be null");
|
||||
this.parameters.put(key, new String[] { value });
|
||||
}
|
||||
|
||||
public void setParameter(String key, String[] values) {
|
||||
Assert.notNull(key, "Parameter key must be null");
|
||||
Assert.notNull(values, "Parameter values must not be null");
|
||||
this.parameters.put(key, values);
|
||||
}
|
||||
|
||||
public void addParameter(String name, String value) {
|
||||
addParameter(name, new String[] { value });
|
||||
}
|
||||
|
||||
public void addParameter(String name, String[] values) {
|
||||
String[] oldArr = this.parameters.get(name);
|
||||
if (oldArr != null) {
|
||||
String[] newArr = new String[oldArr.length + values.length];
|
||||
System.arraycopy(oldArr, 0, newArr, 0, oldArr.length);
|
||||
System.arraycopy(values, 0, newArr, oldArr.length, values.length);
|
||||
this.parameters.put(name, newArr);
|
||||
}
|
||||
else {
|
||||
this.parameters.put(name, values);
|
||||
}
|
||||
}
|
||||
|
||||
public String getParameter(String name) {
|
||||
String[] arr = this.parameters.get(name);
|
||||
return (arr != null && arr.length > 0 ? arr[0] : null);
|
||||
}
|
||||
|
||||
public Enumeration<String> getParameterNames() {
|
||||
return Collections.enumeration(this.parameters.keySet());
|
||||
}
|
||||
|
||||
public String[] getParameterValues(String name) {
|
||||
return this.parameters.get(name);
|
||||
}
|
||||
|
||||
public Map<String, String[]> getParameterMap() {
|
||||
return Collections.unmodifiableMap(this.parameters);
|
||||
}
|
||||
|
||||
public void setSecure(boolean secure) {
|
||||
this.secure = secure;
|
||||
}
|
||||
|
||||
public boolean isSecure() {
|
||||
return this.secure;
|
||||
}
|
||||
|
||||
public void setAttribute(String name, Object value) {
|
||||
checkActive();
|
||||
if (value != null) {
|
||||
this.attributes.put(name, value);
|
||||
}
|
||||
else {
|
||||
this.attributes.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAttribute(String name) {
|
||||
checkActive();
|
||||
this.attributes.remove(name);
|
||||
}
|
||||
|
||||
public String getRequestedSessionId() {
|
||||
PortletSession session = this.getPortletSession();
|
||||
return (session != null ? session.getId() : null);
|
||||
}
|
||||
|
||||
public void setRequestedSessionIdValid(boolean requestedSessionIdValid) {
|
||||
this.requestedSessionIdValid = requestedSessionIdValid;
|
||||
}
|
||||
|
||||
public boolean isRequestedSessionIdValid() {
|
||||
return this.requestedSessionIdValid;
|
||||
}
|
||||
|
||||
public void addResponseContentType(String responseContentType) {
|
||||
this.responseContentTypes.add(responseContentType);
|
||||
}
|
||||
|
||||
public void addPreferredResponseContentType(String responseContentType) {
|
||||
this.responseContentTypes.add(0, responseContentType);
|
||||
}
|
||||
|
||||
public String getResponseContentType() {
|
||||
return this.responseContentTypes.get(0);
|
||||
}
|
||||
|
||||
public Enumeration<String> getResponseContentTypes() {
|
||||
return Collections.enumeration(this.responseContentTypes);
|
||||
}
|
||||
|
||||
public void addLocale(Locale locale) {
|
||||
this.locales.add(locale);
|
||||
}
|
||||
|
||||
public void addPreferredLocale(Locale locale) {
|
||||
this.locales.add(0, locale);
|
||||
}
|
||||
|
||||
public Locale getLocale() {
|
||||
return this.locales.get(0);
|
||||
}
|
||||
|
||||
public Enumeration<Locale> getLocales() {
|
||||
return Collections.enumeration(this.locales);
|
||||
}
|
||||
|
||||
public void setScheme(String scheme) {
|
||||
this.scheme = scheme;
|
||||
}
|
||||
|
||||
public String getScheme() {
|
||||
return this.scheme;
|
||||
}
|
||||
|
||||
public void setServerName(String serverName) {
|
||||
this.serverName = serverName;
|
||||
}
|
||||
|
||||
public String getServerName() {
|
||||
return this.serverName;
|
||||
}
|
||||
|
||||
public void setServerPort(int serverPort) {
|
||||
this.serverPort = serverPort;
|
||||
}
|
||||
|
||||
public int getServerPort() {
|
||||
return this.serverPort;
|
||||
}
|
||||
|
||||
public void setWindowID(String windowID) {
|
||||
this.windowID = windowID;
|
||||
}
|
||||
|
||||
public String getWindowID() {
|
||||
return this.windowID;
|
||||
}
|
||||
|
||||
public void setCookies(Cookie... cookies) {
|
||||
this.cookies = cookies;
|
||||
}
|
||||
|
||||
public Cookie[] getCookies() {
|
||||
return this.cookies;
|
||||
}
|
||||
|
||||
public Map<String, String[]> getPrivateParameterMap() {
|
||||
if (!this.publicParameterNames.isEmpty()) {
|
||||
Map<String, String[]> filtered = new LinkedHashMap<String, String[]>();
|
||||
for (String key : this.parameters.keySet()) {
|
||||
if (!this.publicParameterNames.contains(key)) {
|
||||
filtered.put(key, this.parameters.get(key));
|
||||
}
|
||||
}
|
||||
return filtered;
|
||||
}
|
||||
else {
|
||||
return Collections.unmodifiableMap(this.parameters);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, String[]> getPublicParameterMap() {
|
||||
if (!this.publicParameterNames.isEmpty()) {
|
||||
Map<String, String[]> filtered = new LinkedHashMap<String, String[]>();
|
||||
for (String key : this.parameters.keySet()) {
|
||||
if (this.publicParameterNames.contains(key)) {
|
||||
filtered.put(key, this.parameters.get(key));
|
||||
}
|
||||
}
|
||||
return filtered;
|
||||
}
|
||||
else {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
public void registerPublicParameter(String name) {
|
||||
this.publicParameterNames.add(name);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.portlet.PortletException;
|
||||
import javax.portlet.PortletRequest;
|
||||
import javax.portlet.PortletRequestDispatcher;
|
||||
import javax.portlet.PortletResponse;
|
||||
import javax.portlet.RenderRequest;
|
||||
import javax.portlet.RenderResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.PortletRequestDispatcher} interface.
|
||||
*
|
||||
* @author John A. Lewis
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MockPortletRequestDispatcher implements PortletRequestDispatcher {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final String url;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MockPortletRequestDispatcher for the given URL.
|
||||
* @param url the URL to dispatch to.
|
||||
*/
|
||||
public MockPortletRequestDispatcher(String url) {
|
||||
Assert.notNull(url, "URL must not be null");
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
|
||||
public void include(RenderRequest request, RenderResponse response) throws PortletException, IOException {
|
||||
include((PortletRequest) request, (PortletResponse) response);
|
||||
}
|
||||
|
||||
public void include(PortletRequest request, PortletResponse response) throws PortletException, IOException {
|
||||
Assert.notNull(request, "Request must not be null");
|
||||
Assert.notNull(response, "Response must not be null");
|
||||
if (!(response instanceof MockMimeResponse)) {
|
||||
throw new IllegalArgumentException("MockPortletRequestDispatcher requires MockMimeResponse");
|
||||
}
|
||||
((MockMimeResponse) response).setIncludedUrl(this.url);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("MockPortletRequestDispatcher: including URL [" + this.url + "]");
|
||||
}
|
||||
}
|
||||
|
||||
public void forward(PortletRequest request, PortletResponse response) throws PortletException, IOException {
|
||||
Assert.notNull(request, "Request must not be null");
|
||||
Assert.notNull(response, "Response must not be null");
|
||||
if (!(response instanceof MockMimeResponse)) {
|
||||
throw new IllegalArgumentException("MockPortletRequestDispatcher requires MockMimeResponse");
|
||||
}
|
||||
((MockMimeResponse) response).setForwardedUrl(this.url);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("MockPortletRequestDispatcher: forwarding to URL [" + this.url + "]");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.portlet.PortalContext;
|
||||
import javax.portlet.PortletResponse;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.w3c.dom.DOMException;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.PortletResponse} interface.
|
||||
*
|
||||
* @author John A. Lewis
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MockPortletResponse implements PortletResponse {
|
||||
|
||||
private final PortalContext portalContext;
|
||||
|
||||
private final Map<String, String[]> properties = new LinkedHashMap<String, String[]>();
|
||||
|
||||
private String namespace = "";
|
||||
|
||||
private final Set<Cookie> cookies = new LinkedHashSet<Cookie>();
|
||||
|
||||
private final Map<String, Element[]> xmlProperties = new LinkedHashMap<String, Element[]>();
|
||||
|
||||
private Document xmlDocument;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MockPortletResponse with a default {@link MockPortalContext}.
|
||||
* @see MockPortalContext
|
||||
*/
|
||||
public MockPortletResponse() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockPortletResponse.
|
||||
* @param portalContext the PortalContext defining the supported
|
||||
* PortletModes and WindowStates
|
||||
*/
|
||||
public MockPortletResponse(PortalContext portalContext) {
|
||||
this.portalContext = (portalContext != null ? portalContext : new MockPortalContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the PortalContext that this MockPortletResponse runs in,
|
||||
* defining the supported PortletModes and WindowStates.
|
||||
*/
|
||||
public PortalContext getPortalContext() {
|
||||
return this.portalContext;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// PortletResponse methods
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
public void addProperty(String key, String value) {
|
||||
Assert.notNull(key, "Property key must not be null");
|
||||
String[] oldArr = this.properties.get(key);
|
||||
if (oldArr != null) {
|
||||
String[] newArr = new String[oldArr.length + 1];
|
||||
System.arraycopy(oldArr, 0, newArr, 0, oldArr.length);
|
||||
newArr[oldArr.length] = value;
|
||||
this.properties.put(key, newArr);
|
||||
}
|
||||
else {
|
||||
this.properties.put(key, new String[] {value});
|
||||
}
|
||||
}
|
||||
|
||||
public void setProperty(String key, String value) {
|
||||
Assert.notNull(key, "Property key must not be null");
|
||||
this.properties.put(key, new String[] {value});
|
||||
}
|
||||
|
||||
public Set<String> getPropertyNames() {
|
||||
return Collections.unmodifiableSet(this.properties.keySet());
|
||||
}
|
||||
|
||||
public String getProperty(String key) {
|
||||
Assert.notNull(key, "Property key must not be null");
|
||||
String[] arr = this.properties.get(key);
|
||||
return (arr != null && arr.length > 0 ? arr[0] : null);
|
||||
}
|
||||
|
||||
public String[] getProperties(String key) {
|
||||
Assert.notNull(key, "Property key must not be null");
|
||||
return this.properties.get(key);
|
||||
}
|
||||
|
||||
public String encodeURL(String path) {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setNamespace(String namespace) {
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return this.namespace;
|
||||
}
|
||||
|
||||
public void addProperty(Cookie cookie) {
|
||||
Assert.notNull(cookie, "Cookie must not be null");
|
||||
this.cookies.add(cookie);
|
||||
}
|
||||
|
||||
public Cookie[] getCookies() {
|
||||
return this.cookies.toArray(new Cookie[this.cookies.size()]);
|
||||
}
|
||||
|
||||
public Cookie getCookie(String name) {
|
||||
Assert.notNull(name, "Cookie name must not be null");
|
||||
for (Cookie cookie : this.cookies) {
|
||||
if (name.equals(cookie.getName())) {
|
||||
return cookie;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void addProperty(String key, Element value) {
|
||||
Assert.notNull(key, "Property key must not be null");
|
||||
Element[] oldArr = this.xmlProperties.get(key);
|
||||
if (oldArr != null) {
|
||||
Element[] newArr = new Element[oldArr.length + 1];
|
||||
System.arraycopy(oldArr, 0, newArr, 0, oldArr.length);
|
||||
newArr[oldArr.length] = value;
|
||||
this.xmlProperties.put(key, newArr);
|
||||
}
|
||||
else {
|
||||
this.xmlProperties.put(key, new Element[] {value});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Set<String> getXmlPropertyNames() {
|
||||
return Collections.unmodifiableSet(this.xmlProperties.keySet());
|
||||
}
|
||||
|
||||
public Element getXmlProperty(String key) {
|
||||
Assert.notNull(key, "Property key must not be null");
|
||||
Element[] arr = this.xmlProperties.get(key);
|
||||
return (arr != null && arr.length > 0 ? arr[0] : null);
|
||||
}
|
||||
|
||||
public Element[] getXmlProperties(String key) {
|
||||
Assert.notNull(key, "Property key must not be null");
|
||||
return this.xmlProperties.get(key);
|
||||
}
|
||||
|
||||
public Element createElement(String tagName) throws DOMException {
|
||||
if (this.xmlDocument == null) {
|
||||
try {
|
||||
this.xmlDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
|
||||
}
|
||||
catch (ParserConfigurationException ex) {
|
||||
throw new DOMException(DOMException.INVALID_STATE_ERR, ex.toString());
|
||||
}
|
||||
}
|
||||
return this.xmlDocument.createElement(tagName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.mock.web.portlet;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Vector;
|
||||
import javax.portlet.PortletContext;
|
||||
import javax.portlet.PortletSession;
|
||||
import javax.servlet.http.HttpSessionBindingEvent;
|
||||
import javax.servlet.http.HttpSessionBindingListener;
|
||||
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.PortletSession} interface.
|
||||
*
|
||||
* @author John A. Lewis
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MockPortletSession implements PortletSession {
|
||||
|
||||
private static int nextId = 1;
|
||||
|
||||
|
||||
private final String id = Integer.toString(nextId++);
|
||||
|
||||
private final long creationTime = System.currentTimeMillis();
|
||||
|
||||
private int maxInactiveInterval;
|
||||
|
||||
private long lastAccessedTime = System.currentTimeMillis();
|
||||
|
||||
private final PortletContext portletContext;
|
||||
|
||||
private final Map<String, Object> portletAttributes = new HashMap<String, Object>();
|
||||
|
||||
private final Map<String, Object> applicationAttributes = new HashMap<String, Object>();
|
||||
|
||||
private boolean invalid = false;
|
||||
|
||||
private boolean isNew = true;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MockPortletSession with a default {@link MockPortletContext}.
|
||||
* @see MockPortletContext
|
||||
*/
|
||||
public MockPortletSession() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockPortletSession.
|
||||
* @param portletContext the PortletContext that the session runs in
|
||||
*/
|
||||
public MockPortletSession(PortletContext portletContext) {
|
||||
this.portletContext = (portletContext != null ? portletContext : new MockPortletContext());
|
||||
}
|
||||
|
||||
|
||||
public Object getAttribute(String name) {
|
||||
return this.portletAttributes.get(name);
|
||||
}
|
||||
|
||||
public Object getAttribute(String name, int scope) {
|
||||
if (scope == PortletSession.PORTLET_SCOPE) {
|
||||
return this.portletAttributes.get(name);
|
||||
}
|
||||
else if (scope == PortletSession.APPLICATION_SCOPE) {
|
||||
return this.applicationAttributes.get(name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Enumeration<String> getAttributeNames() {
|
||||
return new Vector<String>(this.portletAttributes.keySet()).elements();
|
||||
}
|
||||
|
||||
public Enumeration<String> getAttributeNames(int scope) {
|
||||
if (scope == PortletSession.PORTLET_SCOPE) {
|
||||
return new Vector<String>(this.portletAttributes.keySet()).elements();
|
||||
}
|
||||
else if (scope == PortletSession.APPLICATION_SCOPE) {
|
||||
return new Vector<String>(this.applicationAttributes.keySet()).elements();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public long getCreationTime() {
|
||||
return this.creationTime;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void access() {
|
||||
this.lastAccessedTime = System.currentTimeMillis();
|
||||
setNew(false);
|
||||
}
|
||||
|
||||
public long getLastAccessedTime() {
|
||||
return this.lastAccessedTime;
|
||||
}
|
||||
|
||||
public int getMaxInactiveInterval() {
|
||||
return this.maxInactiveInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all of this session's attributes.
|
||||
*/
|
||||
public void clearAttributes() {
|
||||
doClearAttributes(this.portletAttributes);
|
||||
doClearAttributes(this.applicationAttributes);
|
||||
}
|
||||
|
||||
protected void doClearAttributes(Map<String, Object> attributes) {
|
||||
for (Iterator<Map.Entry<String, Object>> it = attributes.entrySet().iterator(); it.hasNext();) {
|
||||
Map.Entry<String, Object> entry = it.next();
|
||||
String name = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
it.remove();
|
||||
if (value instanceof HttpSessionBindingListener) {
|
||||
((HttpSessionBindingListener) value).valueUnbound(
|
||||
new HttpSessionBindingEvent(new MockHttpSession(), name, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void invalidate() {
|
||||
this.invalid = true;
|
||||
clearAttributes();
|
||||
}
|
||||
|
||||
public boolean isInvalid() {
|
||||
return this.invalid;
|
||||
}
|
||||
|
||||
public void setNew(boolean value) {
|
||||
this.isNew = value;
|
||||
}
|
||||
|
||||
public boolean isNew() {
|
||||
return this.isNew;
|
||||
}
|
||||
|
||||
public void removeAttribute(String name) {
|
||||
this.portletAttributes.remove(name);
|
||||
}
|
||||
|
||||
public void removeAttribute(String name, int scope) {
|
||||
if (scope == PortletSession.PORTLET_SCOPE) {
|
||||
this.portletAttributes.remove(name);
|
||||
}
|
||||
else if (scope == PortletSession.APPLICATION_SCOPE) {
|
||||
this.applicationAttributes.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
public void setAttribute(String name, Object value) {
|
||||
if (value != null) {
|
||||
this.portletAttributes.put(name, value);
|
||||
}
|
||||
else {
|
||||
this.portletAttributes.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
public void setAttribute(String name, Object value, int scope) {
|
||||
if (scope == PortletSession.PORTLET_SCOPE) {
|
||||
if (value != null) {
|
||||
this.portletAttributes.put(name, value);
|
||||
}
|
||||
else {
|
||||
this.portletAttributes.remove(name);
|
||||
}
|
||||
}
|
||||
else if (scope == PortletSession.APPLICATION_SCOPE) {
|
||||
if (value != null) {
|
||||
this.applicationAttributes.put(name, value);
|
||||
}
|
||||
else {
|
||||
this.applicationAttributes.remove(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setMaxInactiveInterval(int interval) {
|
||||
this.maxInactiveInterval = interval;
|
||||
}
|
||||
|
||||
public PortletContext getPortletContext() {
|
||||
return this.portletContext;
|
||||
}
|
||||
|
||||
public Map<String, Object> getAttributeMap() {
|
||||
return Collections.unmodifiableMap(this.portletAttributes);
|
||||
}
|
||||
|
||||
public Map<String, Object> getAttributeMap(int scope) {
|
||||
if (scope == PortletSession.PORTLET_SCOPE) {
|
||||
return Collections.unmodifiableMap(this.portletAttributes);
|
||||
}
|
||||
else if (scope == PortletSession.APPLICATION_SCOPE) {
|
||||
return Collections.unmodifiableMap(this.applicationAttributes);
|
||||
}
|
||||
else {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import java.util.Map;
|
||||
import javax.portlet.PortalContext;
|
||||
import javax.portlet.PortletMode;
|
||||
import javax.portlet.PortletModeException;
|
||||
import javax.portlet.PortletURL;
|
||||
import javax.portlet.WindowState;
|
||||
import javax.portlet.WindowStateException;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.PortletURL} interface.
|
||||
*
|
||||
* @author John A. Lewis
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MockPortletURL extends MockBaseURL implements PortletURL {
|
||||
|
||||
public static final String URL_TYPE_RENDER = "render";
|
||||
|
||||
public static final String URL_TYPE_ACTION = "action";
|
||||
|
||||
|
||||
private final PortalContext portalContext;
|
||||
|
||||
private final String urlType;
|
||||
|
||||
private WindowState windowState;
|
||||
|
||||
private PortletMode portletMode;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MockPortletURL for the given URL type.
|
||||
* @param portalContext the PortalContext defining the supported
|
||||
* PortletModes and WindowStates
|
||||
* @param urlType the URL type, for example "render" or "action"
|
||||
* @see #URL_TYPE_RENDER
|
||||
* @see #URL_TYPE_ACTION
|
||||
*/
|
||||
public MockPortletURL(PortalContext portalContext, String urlType) {
|
||||
Assert.notNull(portalContext, "PortalContext is required");
|
||||
this.portalContext = portalContext;
|
||||
this.urlType = urlType;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// PortletURL methods
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
public void setWindowState(WindowState windowState) throws WindowStateException {
|
||||
if (!CollectionUtils.contains(this.portalContext.getSupportedWindowStates(), windowState)) {
|
||||
throw new WindowStateException("WindowState not supported", windowState);
|
||||
}
|
||||
this.windowState = windowState;
|
||||
}
|
||||
|
||||
public WindowState getWindowState() {
|
||||
return this.windowState;
|
||||
}
|
||||
|
||||
public void setPortletMode(PortletMode portletMode) throws PortletModeException {
|
||||
if (!CollectionUtils.contains(this.portalContext.getSupportedPortletModes(), portletMode)) {
|
||||
throw new PortletModeException("PortletMode not supported", portletMode);
|
||||
}
|
||||
this.portletMode = portletMode;
|
||||
}
|
||||
|
||||
public PortletMode getPortletMode() {
|
||||
return this.portletMode;
|
||||
}
|
||||
|
||||
public void removePublicRenderParameter(String name) {
|
||||
this.parameters.remove(name);
|
||||
}
|
||||
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(encodeParameter("urlType", this.urlType));
|
||||
if (this.windowState != null) {
|
||||
sb.append(";").append(encodeParameter("windowState", this.windowState.toString()));
|
||||
}
|
||||
if (this.portletMode != null) {
|
||||
sb.append(";").append(encodeParameter("portletMode", this.portletMode.toString()));
|
||||
}
|
||||
for (Map.Entry<String, String[]> entry : this.parameters.entrySet()) {
|
||||
sb.append(";").append(encodeParameter("param_" + entry.getKey(), entry.getValue()));
|
||||
}
|
||||
return (isSecure() ? "https:" : "http:") +
|
||||
"//localhost/mockportlet?" + sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import javax.portlet.PortalContext;
|
||||
import javax.portlet.PortletContext;
|
||||
import javax.portlet.PortletMode;
|
||||
import javax.portlet.RenderRequest;
|
||||
import javax.portlet.WindowState;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.RenderRequest} interface.
|
||||
*
|
||||
* @author John A. Lewis
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MockRenderRequest extends MockPortletRequest implements RenderRequest {
|
||||
|
||||
/**
|
||||
* Create a new MockRenderRequest with a default {@link MockPortalContext}
|
||||
* and a default {@link MockPortletContext}.
|
||||
* @see MockPortalContext
|
||||
* @see MockPortletContext
|
||||
*/
|
||||
public MockRenderRequest() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockRenderRequest with a default {@link MockPortalContext}
|
||||
* and a default {@link MockPortletContext}.
|
||||
* @param portletMode the mode that the portlet runs in
|
||||
*/
|
||||
public MockRenderRequest(PortletMode portletMode) {
|
||||
super();
|
||||
setPortletMode(portletMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockRenderRequest with a default {@link MockPortalContext}
|
||||
* and a default {@link MockPortletContext}.
|
||||
* @param portletMode the mode that the portlet runs in
|
||||
* @param windowState the window state to run the portlet in
|
||||
*/
|
||||
public MockRenderRequest(PortletMode portletMode, WindowState windowState) {
|
||||
super();
|
||||
setPortletMode(portletMode);
|
||||
setWindowState(windowState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockRenderRequest with a default {@link MockPortalContext}.
|
||||
* @param portletContext the PortletContext that the request runs in
|
||||
*/
|
||||
public MockRenderRequest(PortletContext portletContext) {
|
||||
super(portletContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockRenderRequest.
|
||||
* @param portalContext the PortletContext that the request runs in
|
||||
* @param portletContext the PortletContext that the request runs in
|
||||
*/
|
||||
public MockRenderRequest(PortalContext portalContext, PortletContext portletContext) {
|
||||
super(portalContext, portletContext);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected String getLifecyclePhase() {
|
||||
return RENDER_PHASE;
|
||||
}
|
||||
|
||||
public String getETag() {
|
||||
return getProperty(RenderRequest.ETAG);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import java.util.Collection;
|
||||
import javax.portlet.PortalContext;
|
||||
import javax.portlet.PortletMode;
|
||||
import javax.portlet.RenderRequest;
|
||||
import javax.portlet.RenderResponse;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.RenderResponse} interface.
|
||||
*
|
||||
* @author John A. Lewis
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MockRenderResponse extends MockMimeResponse implements RenderResponse {
|
||||
|
||||
private String title;
|
||||
|
||||
private Collection<PortletMode> nextPossiblePortletModes;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MockRenderResponse with a default {@link MockPortalContext}.
|
||||
* @see MockPortalContext
|
||||
*/
|
||||
public MockRenderResponse() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockRenderResponse.
|
||||
* @param portalContext the PortalContext defining the supported
|
||||
* PortletModes and WindowStates
|
||||
*/
|
||||
public MockRenderResponse(PortalContext portalContext) {
|
||||
super(portalContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockRenderResponse.
|
||||
* @param portalContext the PortalContext defining the supported
|
||||
* PortletModes and WindowStates
|
||||
* @param request the corresponding render request that this response
|
||||
* is generated for
|
||||
*/
|
||||
public MockRenderResponse(PortalContext portalContext, RenderRequest request) {
|
||||
super(portalContext, request);
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// RenderResponse methods
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return this.title;
|
||||
}
|
||||
|
||||
public void setNextPossiblePortletModes(Collection<PortletMode> portletModes) {
|
||||
this.nextPossiblePortletModes = portletModes;
|
||||
}
|
||||
|
||||
public Collection<PortletMode> getNextPossiblePortletModes() {
|
||||
return this.nextPossiblePortletModes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import javax.portlet.PortalContext;
|
||||
import javax.portlet.PortletContext;
|
||||
import javax.portlet.RenderRequest;
|
||||
import javax.portlet.ResourceRequest;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.ResourceRequest} interface.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class MockResourceRequest extends MockClientDataRequest implements ResourceRequest {
|
||||
|
||||
private String resourceID;
|
||||
|
||||
private String cacheability;
|
||||
|
||||
private final Map<String, String[]> privateRenderParameterMap = new LinkedHashMap<String, String[]>();
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MockResourceRequest with a default {@link MockPortalContext}
|
||||
* and a default {@link MockPortletContext}.
|
||||
* @see org.springframework.mock.web.portlet.MockPortalContext
|
||||
* @see org.springframework.mock.web.portlet.MockPortletContext
|
||||
*/
|
||||
public MockResourceRequest() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockResourceRequest with a default {@link MockPortalContext}
|
||||
* and a default {@link MockPortletContext}.
|
||||
* @param resourceID the resource id for this request
|
||||
*/
|
||||
public MockResourceRequest(String resourceID) {
|
||||
super();
|
||||
this.resourceID = resourceID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockResourceRequest with a default {@link MockPortalContext}
|
||||
* and a default {@link MockPortletContext}.
|
||||
* @param url the resource URL for this request
|
||||
*/
|
||||
public MockResourceRequest(MockResourceURL url) {
|
||||
super();
|
||||
this.resourceID = url.getResourceID();
|
||||
this.cacheability = url.getCacheability();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockResourceRequest with a default {@link MockPortalContext}.
|
||||
* @param portletContext the PortletContext that the request runs in
|
||||
*/
|
||||
public MockResourceRequest(PortletContext portletContext) {
|
||||
super(portletContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockResourceRequest.
|
||||
* @param portalContext the PortalContext that the request runs in
|
||||
* @param portletContext the PortletContext that the request runs in
|
||||
*/
|
||||
public MockResourceRequest(PortalContext portalContext, PortletContext portletContext) {
|
||||
super(portalContext, portletContext);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected String getLifecyclePhase() {
|
||||
return RESOURCE_PHASE;
|
||||
}
|
||||
|
||||
public void setResourceID(String resourceID) {
|
||||
this.resourceID = resourceID;
|
||||
}
|
||||
|
||||
public String getResourceID() {
|
||||
return this.resourceID;
|
||||
}
|
||||
|
||||
public void setCacheability(String cacheLevel) {
|
||||
this.cacheability = cacheLevel;
|
||||
}
|
||||
|
||||
public String getCacheability() {
|
||||
return this.cacheability;
|
||||
}
|
||||
|
||||
public String getETag() {
|
||||
return getProperty(RenderRequest.ETAG);
|
||||
}
|
||||
|
||||
public void addPrivateRenderParameter(String key, String value) {
|
||||
this.privateRenderParameterMap.put(key, new String[] {value});
|
||||
}
|
||||
|
||||
public void addPrivateRenderParameter(String key, String[] values) {
|
||||
this.privateRenderParameterMap.put(key, values);
|
||||
}
|
||||
|
||||
public Map<String, String[]> getPrivateRenderParameterMap() {
|
||||
return Collections.unmodifiableMap(this.privateRenderParameterMap);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import javax.portlet.ResourceResponse;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.ResourceResponse} interface.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class MockResourceResponse extends MockMimeResponse implements ResourceResponse {
|
||||
|
||||
private int contentLength = 0;
|
||||
|
||||
|
||||
public void setContentLength(int len) {
|
||||
this.contentLength = len;
|
||||
}
|
||||
|
||||
public int getContentLength() {
|
||||
return this.contentLength;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import java.util.Map;
|
||||
import javax.portlet.ResourceURL;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.ResourceURL} interface.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class MockResourceURL extends MockBaseURL implements ResourceURL {
|
||||
|
||||
private String resourceID;
|
||||
|
||||
private String cacheability;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// ResourceURL methods
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
public void setResourceID(String resourceID) {
|
||||
this.resourceID = resourceID;
|
||||
}
|
||||
|
||||
public String getResourceID() {
|
||||
return this.resourceID;
|
||||
}
|
||||
|
||||
public void setCacheability(String cacheLevel) {
|
||||
this.cacheability = cacheLevel;
|
||||
}
|
||||
|
||||
public String getCacheability() {
|
||||
return this.cacheability;
|
||||
}
|
||||
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(encodeParameter("resourceID", this.resourceID));
|
||||
if (this.cacheability != null) {
|
||||
sb.append(";").append(encodeParameter("cacheability", this.cacheability));
|
||||
}
|
||||
for (Map.Entry<String, String[]> entry : this.parameters.entrySet()) {
|
||||
sb.append(";").append(encodeParameter("param_" + entry.getKey(), entry.getValue()));
|
||||
}
|
||||
return (isSecure() ? "https:" : "http:") +
|
||||
"//localhost/mockportlet?" + sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import javax.portlet.PortalContext;
|
||||
import javax.portlet.PortletMode;
|
||||
import javax.portlet.PortletModeException;
|
||||
import javax.portlet.StateAwareResponse;
|
||||
import javax.portlet.WindowState;
|
||||
import javax.portlet.WindowStateException;
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.StateAwareResponse} interface.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class MockStateAwareResponse extends MockPortletResponse implements StateAwareResponse {
|
||||
|
||||
private WindowState windowState;
|
||||
|
||||
private PortletMode portletMode;
|
||||
|
||||
private final Map<String, String[]> renderParameters = new LinkedHashMap<String, String[]>();
|
||||
|
||||
private final Map<QName, Serializable> events = new HashMap<QName, Serializable>();
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MockActionResponse with a default {@link MockPortalContext}.
|
||||
* @see org.springframework.mock.web.portlet.MockPortalContext
|
||||
*/
|
||||
public MockStateAwareResponse() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MockActionResponse.
|
||||
* @param portalContext the PortalContext defining the supported
|
||||
* PortletModes and WindowStates
|
||||
*/
|
||||
public MockStateAwareResponse(PortalContext portalContext) {
|
||||
super(portalContext);
|
||||
}
|
||||
|
||||
|
||||
public void setWindowState(WindowState windowState) throws WindowStateException {
|
||||
if (!CollectionUtils.contains(getPortalContext().getSupportedWindowStates(), windowState)) {
|
||||
throw new WindowStateException("WindowState not supported", windowState);
|
||||
}
|
||||
this.windowState = windowState;
|
||||
}
|
||||
|
||||
public WindowState getWindowState() {
|
||||
return this.windowState;
|
||||
}
|
||||
|
||||
public void setPortletMode(PortletMode portletMode) throws PortletModeException {
|
||||
if (!CollectionUtils.contains(getPortalContext().getSupportedPortletModes(), portletMode)) {
|
||||
throw new PortletModeException("PortletMode not supported", portletMode);
|
||||
}
|
||||
this.portletMode = portletMode;
|
||||
}
|
||||
|
||||
public PortletMode getPortletMode() {
|
||||
return this.portletMode;
|
||||
}
|
||||
|
||||
public void setRenderParameters(Map<String, String[]> parameters) {
|
||||
Assert.notNull(parameters, "Parameters Map must not be null");
|
||||
this.renderParameters.clear();
|
||||
this.renderParameters.putAll(parameters);
|
||||
}
|
||||
|
||||
public void setRenderParameter(String key, String value) {
|
||||
Assert.notNull(key, "Parameter key must not be null");
|
||||
Assert.notNull(value, "Parameter value must not be null");
|
||||
this.renderParameters.put(key, new String[] {value});
|
||||
}
|
||||
|
||||
public void setRenderParameter(String key, String[] values) {
|
||||
Assert.notNull(key, "Parameter key must not be null");
|
||||
Assert.notNull(values, "Parameter values must not be null");
|
||||
this.renderParameters.put(key, values);
|
||||
}
|
||||
|
||||
public String getRenderParameter(String key) {
|
||||
Assert.notNull(key, "Parameter key must not be null");
|
||||
String[] arr = this.renderParameters.get(key);
|
||||
return (arr != null && arr.length > 0 ? arr[0] : null);
|
||||
}
|
||||
|
||||
public String[] getRenderParameterValues(String key) {
|
||||
Assert.notNull(key, "Parameter key must not be null");
|
||||
return this.renderParameters.get(key);
|
||||
}
|
||||
|
||||
public Iterator<String> getRenderParameterNames() {
|
||||
return this.renderParameters.keySet().iterator();
|
||||
}
|
||||
|
||||
public Map<String, String[]> getRenderParameterMap() {
|
||||
return Collections.unmodifiableMap(this.renderParameters);
|
||||
}
|
||||
|
||||
public void removePublicRenderParameter(String name) {
|
||||
this.renderParameters.remove(name);
|
||||
}
|
||||
|
||||
public void setEvent(QName name, Serializable value) {
|
||||
this.events.put(name, value);
|
||||
}
|
||||
|
||||
public void setEvent(String name, Serializable value) {
|
||||
this.events.put(new QName(name), value);
|
||||
}
|
||||
|
||||
public Iterator<QName> getEventNames() {
|
||||
return this.events.keySet().iterator();
|
||||
}
|
||||
|
||||
public Serializable getEvent(QName name) {
|
||||
return this.events.get(name);
|
||||
}
|
||||
|
||||
public Serializable getEvent(String name) {
|
||||
return this.events.get(new QName(name));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import javax.portlet.PortletContext;
|
||||
import javax.portlet.PortletRequestDispatcher;
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.PortletContext} interface,
|
||||
* wrapping an underlying {@link javax.servlet.ServletContext}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
* @see MockPortletContext
|
||||
*/
|
||||
public class ServletWrappingPortletContext implements PortletContext {
|
||||
|
||||
private final ServletContext servletContext;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new PortletContext wrapping the given ServletContext.
|
||||
* @param servletContext the ServletContext to wrap
|
||||
*/
|
||||
public ServletWrappingPortletContext(ServletContext servletContext) {
|
||||
Assert.notNull(servletContext, "ServletContext must not be null");
|
||||
this.servletContext = servletContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying ServletContext that this PortletContext wraps.
|
||||
*/
|
||||
public final ServletContext getServletContext() {
|
||||
return this.servletContext;
|
||||
}
|
||||
|
||||
|
||||
public String getServerInfo() {
|
||||
return this.servletContext.getServerInfo();
|
||||
}
|
||||
|
||||
public PortletRequestDispatcher getRequestDispatcher(String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public PortletRequestDispatcher getNamedDispatcher(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public InputStream getResourceAsStream(String path) {
|
||||
return this.servletContext.getResourceAsStream(path);
|
||||
}
|
||||
|
||||
public int getMajorVersion() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
public int getMinorVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public String getMimeType(String file) {
|
||||
return this.servletContext.getMimeType(file);
|
||||
}
|
||||
|
||||
public String getRealPath(String path) {
|
||||
return this.servletContext.getRealPath(path);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Set<String> getResourcePaths(String path) {
|
||||
return this.servletContext.getResourcePaths(path);
|
||||
}
|
||||
|
||||
public URL getResource(String path) throws MalformedURLException {
|
||||
return this.servletContext.getResource(path);
|
||||
}
|
||||
|
||||
public Object getAttribute(String name) {
|
||||
return this.servletContext.getAttribute(name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Enumeration<String> getAttributeNames() {
|
||||
return this.servletContext.getAttributeNames();
|
||||
}
|
||||
|
||||
public String getInitParameter(String name) {
|
||||
return this.servletContext.getInitParameter(name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Enumeration<String> getInitParameterNames() {
|
||||
return this.servletContext.getInitParameterNames();
|
||||
}
|
||||
|
||||
public void log(String msg) {
|
||||
this.servletContext.log(msg);
|
||||
}
|
||||
|
||||
public void log(String message, Throwable throwable) {
|
||||
this.servletContext.log(message, throwable);
|
||||
}
|
||||
|
||||
public void removeAttribute(String name) {
|
||||
this.servletContext.removeAttribute(name);
|
||||
}
|
||||
|
||||
public void setAttribute(String name, Object object) {
|
||||
this.servletContext.setAttribute(name, object);
|
||||
}
|
||||
|
||||
public String getPortletContextName() {
|
||||
return this.servletContext.getServletContextName();
|
||||
}
|
||||
|
||||
public Enumeration<String> getContainerRuntimeOptions() {
|
||||
return Collections.enumeration(new HashSet<String>());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* A comprehensive set of Portlet API 2.0 mock objects,
|
||||
* targeted at usage with Spring's web MVC framework.
|
||||
* Useful for testing web contexts and controllers.
|
||||
*
|
||||
* <p>More convenient to use than dynamic mock objects
|
||||
* (<a href="http://www.easymock.org">EasyMock</a>) or
|
||||
* existing Portlet API mock objects.
|
||||
*
|
||||
*/
|
||||
package org.springframework.mock.web.portlet;
|
||||
|
||||
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Convenient superclass for JUnit 3.8 based tests depending on a Spring
|
||||
* context. The test instance itself is populated by Dependency Injection.
|
||||
* </p>
|
||||
* <p>
|
||||
* Really for integration testing, not unit testing. You should <i>not</i>
|
||||
* normally use the Spring container for unit tests: simply populate your POJOs
|
||||
* in plain JUnit tests!
|
||||
* </p>
|
||||
* <p>
|
||||
* This supports two modes of populating the test:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>Via Setter Dependency Injection. Simply express dependencies on objects
|
||||
* in the test fixture, and they will be satisfied by autowiring by type.
|
||||
* <li>Via Field Injection. Declare protected variables of the required type
|
||||
* which match named beans in the context. This is autowire by name, rather than
|
||||
* type. This approach is based on an approach originated by Ara Abrahmian.
|
||||
* Setter Dependency Injection is the default: set the
|
||||
* <code>populateProtectedVariables</code> property to <code>true</code> in
|
||||
* the constructor to switch on Field Injection.
|
||||
* </ul>
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Rob Harrop
|
||||
* @author Rick Evans
|
||||
* @author Sam Brannen
|
||||
* @since 1.1.1
|
||||
* @see #setDirty
|
||||
* @see #contextKey
|
||||
* @see #getContext
|
||||
* @see #getConfigLocations
|
||||
* @deprecated as of Spring 3.0, in favor of using the listener-based test context framework
|
||||
* ({@link org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests})
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public abstract class AbstractDependencyInjectionSpringContextTests extends AbstractSingleSpringContextTests {
|
||||
|
||||
/**
|
||||
* Constant that indicates no autowiring at all.
|
||||
*
|
||||
* @see #setAutowireMode
|
||||
*/
|
||||
public static final int AUTOWIRE_NO = 0;
|
||||
|
||||
/**
|
||||
* Constant that indicates autowiring bean properties by name.
|
||||
*
|
||||
* @see #setAutowireMode
|
||||
*/
|
||||
public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
|
||||
|
||||
/**
|
||||
* Constant that indicates autowiring bean properties by type.
|
||||
*
|
||||
* @see #setAutowireMode
|
||||
*/
|
||||
public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;
|
||||
|
||||
private boolean populateProtectedVariables = false;
|
||||
|
||||
private int autowireMode = AUTOWIRE_BY_TYPE;
|
||||
|
||||
private boolean dependencyCheck = true;
|
||||
|
||||
private String[] managedVariableNames;
|
||||
|
||||
|
||||
/**
|
||||
* Default constructor for AbstractDependencyInjectionSpringContextTests.
|
||||
*/
|
||||
public AbstractDependencyInjectionSpringContextTests() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for AbstractDependencyInjectionSpringContextTests with a
|
||||
* JUnit name.
|
||||
* @param name the name of this text fixture
|
||||
*/
|
||||
public AbstractDependencyInjectionSpringContextTests(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set whether to populate protected variables of this test case. Default is
|
||||
* <code>false</code>.
|
||||
*/
|
||||
public final void setPopulateProtectedVariables(boolean populateFields) {
|
||||
this.populateProtectedVariables = populateFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether to populate protected variables of this test case.
|
||||
*/
|
||||
public final boolean isPopulateProtectedVariables() {
|
||||
return this.populateProtectedVariables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the autowire mode for test properties set by Dependency Injection.
|
||||
* <p>The default is {@link #AUTOWIRE_BY_TYPE}. Can be set to
|
||||
* {@link #AUTOWIRE_BY_NAME} or {@link #AUTOWIRE_NO} instead.
|
||||
* @see #AUTOWIRE_BY_TYPE
|
||||
* @see #AUTOWIRE_BY_NAME
|
||||
* @see #AUTOWIRE_NO
|
||||
*/
|
||||
public final void setAutowireMode(final int autowireMode) {
|
||||
this.autowireMode = autowireMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the autowire mode for test properties set by Dependency Injection.
|
||||
*/
|
||||
public final int getAutowireMode() {
|
||||
return this.autowireMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not dependency checking should be performed for test
|
||||
* properties set by Dependency Injection.
|
||||
* <p>The default is <code>true</code>, meaning that tests cannot be run
|
||||
* unless all properties are populated.
|
||||
*/
|
||||
public final void setDependencyCheck(final boolean dependencyCheck) {
|
||||
this.dependencyCheck = dependencyCheck;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether or not dependency checking should be performed for test
|
||||
* properties set by Dependency Injection.
|
||||
*/
|
||||
public final boolean isDependencyCheck() {
|
||||
return this.dependencyCheck;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare this test instance, injecting dependencies into its protected
|
||||
* fields and its bean properties.
|
||||
* <p>Note: if the {@link ApplicationContext} for this test instance has not
|
||||
* been configured (e.g., is <code>null</code>), dependency injection
|
||||
* will naturally <strong>not</strong> be performed, but an informational
|
||||
* message will be written to the log.
|
||||
* @see #injectDependencies()
|
||||
*/
|
||||
protected void prepareTestInstance() throws Exception {
|
||||
if (getApplicationContext() == null) {
|
||||
if (this.logger.isInfoEnabled()) {
|
||||
this.logger.info("ApplicationContext has not been configured for test [" + getClass().getName()
|
||||
+ "]: dependency injection will NOT be performed.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
injectDependencies();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject dependencies into 'this' instance (that is, this test instance).
|
||||
* <p>The default implementation populates protected variables if the
|
||||
* {@link #populateProtectedVariables() appropriate flag is set}, else uses
|
||||
* autowiring if autowiring is switched on (which it is by default).
|
||||
* <p>Override this method if you need full control over how dependencies are
|
||||
* injected into the test instance.
|
||||
* @throws Exception in case of dependency injection failure
|
||||
* @throws IllegalStateException if the {@link ApplicationContext} for this
|
||||
* test instance has not been configured
|
||||
* @see #populateProtectedVariables()
|
||||
*/
|
||||
protected void injectDependencies() throws Exception {
|
||||
Assert.state(getApplicationContext() != null,
|
||||
"injectDependencies() called without first configuring an ApplicationContext");
|
||||
if (isPopulateProtectedVariables()) {
|
||||
if (this.managedVariableNames == null) {
|
||||
initManagedVariableNames();
|
||||
}
|
||||
populateProtectedVariables();
|
||||
}
|
||||
getApplicationContext().getBeanFactory().autowireBeanProperties(this, getAutowireMode(), isDependencyCheck());
|
||||
}
|
||||
|
||||
private void initManagedVariableNames() throws IllegalAccessException {
|
||||
List managedVarNames = new LinkedList();
|
||||
Class clazz = getClass();
|
||||
do {
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Found " + fields.length + " fields on " + clazz);
|
||||
}
|
||||
for (int i = 0; i < fields.length; i++) {
|
||||
Field field = fields[i];
|
||||
field.setAccessible(true);
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Candidate field: " + field);
|
||||
}
|
||||
if (isProtectedInstanceField(field)) {
|
||||
Object oldValue = field.get(this);
|
||||
if (oldValue == null) {
|
||||
managedVarNames.add(field.getName());
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Added managed variable '" + field.getName() + "'");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Rejected managed variable '" + field.getName() + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
clazz = clazz.getSuperclass();
|
||||
} while (!clazz.equals(AbstractDependencyInjectionSpringContextTests.class));
|
||||
|
||||
this.managedVariableNames = (String[]) managedVarNames.toArray(new String[managedVarNames.size()]);
|
||||
}
|
||||
|
||||
private boolean isProtectedInstanceField(Field field) {
|
||||
int modifiers = field.getModifiers();
|
||||
return !Modifier.isStatic(modifiers) && Modifier.isProtected(modifiers);
|
||||
}
|
||||
|
||||
private void populateProtectedVariables() throws IllegalAccessException {
|
||||
for (int i = 0; i < this.managedVariableNames.length; i++) {
|
||||
String varName = this.managedVariableNames[i];
|
||||
Object bean = null;
|
||||
try {
|
||||
Field field = findField(getClass(), varName);
|
||||
bean = getApplicationContext().getBean(varName, field.getType());
|
||||
field.setAccessible(true);
|
||||
field.set(this, bean);
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Populated field: " + field);
|
||||
}
|
||||
}
|
||||
catch (NoSuchFieldException ex) {
|
||||
if (this.logger.isWarnEnabled()) {
|
||||
this.logger.warn("No field with name '" + varName + "'");
|
||||
}
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
if (this.logger.isWarnEnabled()) {
|
||||
this.logger.warn("No bean with name '" + varName + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Field findField(Class clazz, String name) throws NoSuchFieldException {
|
||||
try {
|
||||
return clazz.getDeclaredField(name);
|
||||
}
|
||||
catch (NoSuchFieldException ex) {
|
||||
Class superclass = clazz.getSuperclass();
|
||||
if (superclass != AbstractSpringContextTests.class) {
|
||||
return findField(superclass, name);
|
||||
}
|
||||
else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,362 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReader;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Abstract JUnit 3.8 test class that holds and exposes a single Spring
|
||||
* {@link org.springframework.context.ApplicationContext ApplicationContext}.
|
||||
* </p>
|
||||
* <p>
|
||||
* This class will cache contexts based on a <i>context key</i>: normally the
|
||||
* config locations String array describing the Spring resource descriptors
|
||||
* making up the context. Unless the {@link #setDirty()} method is called by a
|
||||
* test, the context will not be reloaded, even across different subclasses of
|
||||
* this test. This is particularly beneficial if your context is slow to
|
||||
* construct, for example if you are using Hibernate and the time taken to load
|
||||
* the mappings is an issue.
|
||||
* </p>
|
||||
* <p>
|
||||
* For such standard usage, simply override the {@link #getConfigLocations()}
|
||||
* method and provide the desired config files. For alternative configuration
|
||||
* options, see {@link #getConfigPath()} and {@link #getConfigPaths()}.
|
||||
* </p>
|
||||
* <p>
|
||||
* If you don't want to load a standard context from an array of config
|
||||
* locations, you can override the {@link #contextKey()} method. In conjunction
|
||||
* with this you typically need to override the {@link #loadContext(Object)}
|
||||
* method, which by default loads the locations specified in the
|
||||
* {@link #getConfigLocations()} method.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>WARNING:</b> When doing integration tests from within Eclipse, only use
|
||||
* classpath resource URLs. Else, you may see misleading failures when changing
|
||||
* context locations.
|
||||
* </p>
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rod Johnson
|
||||
* @author Sam Brannen
|
||||
* @since 2.0
|
||||
* @see #getConfigLocations()
|
||||
* @see #contextKey()
|
||||
* @see #loadContext(Object)
|
||||
* @see #getApplicationContext()
|
||||
* @deprecated as of Spring 3.0, in favor of using the listener-based test context framework
|
||||
* ({@link org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests})
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class AbstractSingleSpringContextTests extends AbstractSpringContextTests {
|
||||
|
||||
/** Application context this test will run against */
|
||||
protected ConfigurableApplicationContext applicationContext;
|
||||
|
||||
private int loadCount = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Default constructor for AbstractSingleSpringContextTests.
|
||||
*/
|
||||
public AbstractSingleSpringContextTests() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for AbstractSingleSpringContextTests with a JUnit name.
|
||||
* @param name the name of this text fixture
|
||||
*/
|
||||
public AbstractSingleSpringContextTests(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation is final. Override <code>onSetUp</code> for custom behavior.
|
||||
* @see #onSetUp()
|
||||
*/
|
||||
protected final void setUp() throws Exception {
|
||||
// lazy load, in case getApplicationContext() has not yet been called.
|
||||
if (this.applicationContext == null) {
|
||||
this.applicationContext = getContext(contextKey());
|
||||
}
|
||||
prepareTestInstance();
|
||||
onSetUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare this test instance, for example populating its fields.
|
||||
* The context has already been loaded at the time of this callback.
|
||||
* <p>The default implementation does nothing.
|
||||
* @throws Exception in case of preparation failure
|
||||
*/
|
||||
protected void prepareTestInstance() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can override this method in place of the <code>setUp()</code>
|
||||
* method, which is final in this class.
|
||||
* <p>The default implementation does nothing.
|
||||
* @throws Exception simply let any exception propagate
|
||||
*/
|
||||
protected void onSetUp() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to say that the "applicationContext" instance variable is dirty
|
||||
* and should be reloaded. We need to do this if a test has modified the
|
||||
* context (for example, by replacing a bean definition).
|
||||
*/
|
||||
protected void setDirty() {
|
||||
setDirty(contextKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation is final. Override <code>onTearDown</code> for
|
||||
* custom behavior.
|
||||
* @see #onTearDown()
|
||||
*/
|
||||
protected final void tearDown() throws Exception {
|
||||
onTearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can override this to add custom behavior on teardown.
|
||||
* @throws Exception simply let any exception propagate
|
||||
*/
|
||||
protected void onTearDown() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a key for this context. Default is the config location array as
|
||||
* determined by {@link #getConfigLocations()}.
|
||||
* <p>If you override this method, you will typically have to override
|
||||
* {@link #loadContext(Object)} as well, being able to handle the key type
|
||||
* that this method returns.
|
||||
* @return the context key
|
||||
* @see #getConfigLocations()
|
||||
*/
|
||||
protected Object contextKey() {
|
||||
return getConfigLocations();
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation assumes a key of type String array and loads a
|
||||
* context from the given locations.
|
||||
* <p>If you override {@link #contextKey()}, you will typically have to
|
||||
* override this method as well, being able to handle the key type that
|
||||
* <code>contextKey()</code> returns.
|
||||
* @see #getConfigLocations()
|
||||
*/
|
||||
protected ConfigurableApplicationContext loadContext(Object key) throws Exception {
|
||||
return loadContextLocations((String[]) key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a Spring ApplicationContext from the given config locations.
|
||||
* <p>The default implementation creates a standard
|
||||
* {@link #createApplicationContext GenericApplicationContext}, allowing
|
||||
* for customizing the internal bean factory through
|
||||
* {@link #customizeBeanFactory}.
|
||||
* @param locations the config locations (as Spring resource locations,
|
||||
* e.g. full classpath locations or any kind of URL)
|
||||
* @return the corresponding ApplicationContext instance (potentially cached)
|
||||
* @throws Exception if context loading failed
|
||||
* @see #createApplicationContext(String[])
|
||||
*/
|
||||
protected ConfigurableApplicationContext loadContextLocations(String[] locations) throws Exception {
|
||||
++this.loadCount;
|
||||
if (this.logger.isInfoEnabled()) {
|
||||
this.logger.info("Loading context for locations: " + StringUtils.arrayToCommaDelimitedString(locations));
|
||||
}
|
||||
return createApplicationContext(locations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Spring {@link ConfigurableApplicationContext} for use by this test.
|
||||
* <p>The default implementation creates a standard {@link GenericApplicationContext}
|
||||
* instance, calls the {@link #prepareApplicationContext} prepareApplicationContext}
|
||||
* method and the {@link #customizeBeanFactory customizeBeanFactory} method to allow
|
||||
* for customizing the context and its DefaultListableBeanFactory, populates the
|
||||
* context from the specified config <code>locations</code> through the configured
|
||||
* {@link #createBeanDefinitionReader(GenericApplicationContext) BeanDefinitionReader},
|
||||
* and finally {@link ConfigurableApplicationContext#refresh() refreshes} the context.
|
||||
* @param locations the config locations (as Spring resource locations,
|
||||
* e.g. full classpath locations or any kind of URL)
|
||||
* @return the GenericApplicationContext instance
|
||||
* @see #loadContextLocations(String[])
|
||||
* @see #customizeBeanFactory(DefaultListableBeanFactory)
|
||||
* @see #createBeanDefinitionReader(GenericApplicationContext)
|
||||
*/
|
||||
protected ConfigurableApplicationContext createApplicationContext(String[] locations) {
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
prepareApplicationContext(context);
|
||||
customizeBeanFactory(context.getDefaultListableBeanFactory());
|
||||
createBeanDefinitionReader(context).loadBeanDefinitions(locations);
|
||||
context.refresh();
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the GenericApplicationContext used by this test.
|
||||
* Called before bean definitions are read.
|
||||
* <p>The default implementation is empty. Can be overridden in subclasses to
|
||||
* customize GenericApplicationContext's standard settings.
|
||||
* @param context the context for which the BeanDefinitionReader should be created
|
||||
* @see #createApplicationContext
|
||||
* @see org.springframework.context.support.GenericApplicationContext#setResourceLoader
|
||||
* @see org.springframework.context.support.GenericApplicationContext#setId
|
||||
*/
|
||||
protected void prepareApplicationContext(GenericApplicationContext context) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize the internal bean factory of the ApplicationContext used by
|
||||
* this test. Called before bean definitions are read.
|
||||
* <p>The default implementation is empty. Can be overridden in subclasses to
|
||||
* customize DefaultListableBeanFactory's standard settings.
|
||||
* @param beanFactory the newly created bean factory for this context
|
||||
* @see #loadContextLocations
|
||||
* @see #createApplicationContext
|
||||
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
|
||||
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowEagerClassLoading
|
||||
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowCircularReferences
|
||||
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping
|
||||
*/
|
||||
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for creating new {@link BeanDefinitionReader}s for
|
||||
* loading bean definitions into the supplied
|
||||
* {@link GenericApplicationContext context}.
|
||||
* <p>The default implementation creates a new {@link XmlBeanDefinitionReader}.
|
||||
* Can be overridden in subclasses to provide a different
|
||||
* BeanDefinitionReader implementation.
|
||||
* @param context the context for which the BeanDefinitionReader should be created
|
||||
* @return a BeanDefinitionReader for the supplied context
|
||||
* @see #createApplicationContext(String[])
|
||||
* @see BeanDefinitionReader
|
||||
* @see XmlBeanDefinitionReader
|
||||
*/
|
||||
protected BeanDefinitionReader createBeanDefinitionReader(GenericApplicationContext context) {
|
||||
return new XmlBeanDefinitionReader(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can override this method to return the locations of their
|
||||
* config files, unless they override {@link #contextKey()} and
|
||||
* {@link #loadContext(Object)} instead.
|
||||
* <p>A plain path will be treated as class path location, e.g.:
|
||||
* "org/springframework/whatever/foo.xml". Note however that you may prefix
|
||||
* path locations with standard Spring resource prefixes. Therefore, a
|
||||
* config location path prefixed with "classpath:" with behave the same as a
|
||||
* plain path, but a config location such as
|
||||
* "file:/some/path/path/location/appContext.xml" will be treated as a
|
||||
* filesystem location.
|
||||
* <p>The default implementation builds config locations for the config paths
|
||||
* specified through {@link #getConfigPaths()}.
|
||||
* @return an array of config locations
|
||||
* @see #getConfigPaths()
|
||||
* @see org.springframework.core.io.ResourceLoader#getResource(String)
|
||||
*/
|
||||
protected String[] getConfigLocations() {
|
||||
String[] paths = getConfigPaths();
|
||||
String[] locations = new String[paths.length];
|
||||
for (int i = 0; i < paths.length; i++) {
|
||||
String path = paths[i];
|
||||
if (path.startsWith("/")) {
|
||||
locations[i] = ResourceUtils.CLASSPATH_URL_PREFIX + path;
|
||||
}
|
||||
else {
|
||||
locations[i] = ResourceUtils.CLASSPATH_URL_PREFIX +
|
||||
StringUtils.cleanPath(ClassUtils.classPackageAsResourcePath(getClass()) + "/" + path);
|
||||
}
|
||||
}
|
||||
return locations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can override this method to return paths to their config
|
||||
* files, relative to the concrete test class.
|
||||
* <p>A plain path, e.g. "context.xml", will be loaded as classpath resource
|
||||
* from the same package that the concrete test class is defined in. A path
|
||||
* starting with a slash is treated as fully qualified class path location,
|
||||
* e.g.: "/org/springframework/whatever/foo.xml".
|
||||
* <p>The default implementation builds an array for the config path specified
|
||||
* through {@link #getConfigPath()}.
|
||||
* @return an array of config locations
|
||||
* @see #getConfigPath()
|
||||
* @see java.lang.Class#getResource(String)
|
||||
*/
|
||||
protected String[] getConfigPaths() {
|
||||
String path = getConfigPath();
|
||||
return (path != null ? new String[] { path } : new String[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can override this method to return a single path to a config
|
||||
* file, relative to the concrete test class.
|
||||
* <p>A plain path, e.g. "context.xml", will be loaded as classpath resource
|
||||
* from the same package that the concrete test class is defined in. A path
|
||||
* starting with a slash is treated as fully qualified class path location,
|
||||
* e.g.: "/org/springframework/whatever/foo.xml".
|
||||
* <p>The default implementation simply returns <code>null</code>.
|
||||
* @return an array of config locations
|
||||
* @see #getConfigPath()
|
||||
* @see java.lang.Class#getResource(String)
|
||||
*/
|
||||
protected String getConfigPath() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ApplicationContext that this base class manages; may be
|
||||
* <code>null</code>.
|
||||
*/
|
||||
public final ConfigurableApplicationContext getApplicationContext() {
|
||||
// lazy load, in case setUp() has not yet been called.
|
||||
if (this.applicationContext == null) {
|
||||
try {
|
||||
this.applicationContext = getContext(contextKey());
|
||||
}
|
||||
catch (Exception e) {
|
||||
// log and continue...
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Caught exception while retrieving the ApplicationContext for test [" +
|
||||
getClass().getName() + "." + getName() + "].", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current number of context load attempts.
|
||||
*/
|
||||
public final int getLoadCount() {
|
||||
return this.loadCount;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Superclass for JUnit 3.8 test cases using Spring
|
||||
* {@link org.springframework.context.ApplicationContext ApplicationContexts}.
|
||||
* </p>
|
||||
* <p>
|
||||
* Maintains a static cache of contexts by key. This has significant performance
|
||||
* benefit if initializing the context would take time. While initializing a
|
||||
* Spring context itself is very quick, some beans in a context, such as a
|
||||
* LocalSessionFactoryBean for working with Hibernate, may take some time to
|
||||
* initialize. Hence it often makes sense to do that initializing once.
|
||||
* </p>
|
||||
* <p>
|
||||
* Any ApplicationContext created by this class will be asked to register a JVM
|
||||
* shutdown hook for itself. Unless the context gets closed early, all context
|
||||
* instances will be automatically closed on JVM shutdown. This allows for
|
||||
* freeing external resources held by beans within the context, e.g. temporary
|
||||
* files.
|
||||
* </p>
|
||||
* <p>
|
||||
* Normally you won't extend this class directly but rather one of its
|
||||
* subclasses.
|
||||
* </p>
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 1.1.1
|
||||
* @see AbstractSingleSpringContextTests
|
||||
* @see AbstractDependencyInjectionSpringContextTests
|
||||
* @see AbstractTransactionalSpringContextTests
|
||||
* @see AbstractTransactionalDataSourceSpringContextTests
|
||||
* @deprecated as of Spring 3.0, in favor of using the listener-based test context framework
|
||||
* ({@link org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests})
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class AbstractSpringContextTests extends ConditionalTestCase {
|
||||
|
||||
/**
|
||||
* Map of context keys returned by subclasses of this class, to Spring
|
||||
* contexts. This needs to be static, as JUnit tests are destroyed and
|
||||
* recreated between running individual test methods.
|
||||
*/
|
||||
private static Map<String, ConfigurableApplicationContext> contextKeyToContextMap =
|
||||
new HashMap<String, ConfigurableApplicationContext>();
|
||||
|
||||
|
||||
/**
|
||||
* Default constructor for AbstractSpringContextTests.
|
||||
*/
|
||||
public AbstractSpringContextTests() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for AbstractSpringContextTests with a JUnit name.
|
||||
*/
|
||||
public AbstractSpringContextTests(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Explicitly add an ApplicationContext instance under a given key.
|
||||
* <p>This is not meant to be used by subclasses. It is rather exposed for
|
||||
* special test suite environments.
|
||||
* @param key the context key
|
||||
* @param context the ApplicationContext instance
|
||||
*/
|
||||
public final void addContext(Object key, ConfigurableApplicationContext context) {
|
||||
Assert.notNull(context, "ApplicationContext must not be null");
|
||||
contextKeyToContextMap.put(contextKeyString(key), context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether there is a cached context for the given key.
|
||||
* @param key the context key
|
||||
*/
|
||||
protected final boolean hasCachedContext(Object key) {
|
||||
return contextKeyToContextMap.containsKey(contextKeyString(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the supplied context <code>key</code> is <em>empty</em>.
|
||||
* <p>By default, <code>null</code> values, empty strings, and zero-length
|
||||
* arrays are considered <em>empty</em>.
|
||||
* @param key the context key to check
|
||||
* @return <code>true</code> if the supplied context key is empty
|
||||
*/
|
||||
protected boolean isContextKeyEmpty(Object key) {
|
||||
return (key == null) || ((key instanceof String) && !StringUtils.hasText((String) key)) ||
|
||||
((key instanceof Object[]) && ObjectUtils.isEmpty((Object[]) key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain an ApplicationContext for the given key, potentially cached.
|
||||
* @param key the context key; may be <code>null</code>.
|
||||
* @return the corresponding ApplicationContext instance (potentially cached),
|
||||
* or <code>null</code> if the provided <code>key</code> is <em>empty</em>
|
||||
*/
|
||||
protected final ConfigurableApplicationContext getContext(Object key) throws Exception {
|
||||
if (isContextKeyEmpty(key)) {
|
||||
return null;
|
||||
}
|
||||
String keyString = contextKeyString(key);
|
||||
ConfigurableApplicationContext ctx = contextKeyToContextMap.get(keyString);
|
||||
if (ctx == null) {
|
||||
ctx = loadContext(key);
|
||||
ctx.registerShutdownHook();
|
||||
contextKeyToContextMap.put(keyString, ctx);
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the context with the given key as dirty. This will cause the cached
|
||||
* context to be reloaded before the next test case is executed.
|
||||
* <p>Call this method only if you change the state of a singleton bean,
|
||||
* potentially affecting future tests.
|
||||
*/
|
||||
protected final void setDirty(Object contextKey) {
|
||||
String keyString = contextKeyString(contextKey);
|
||||
ConfigurableApplicationContext ctx = contextKeyToContextMap.remove(keyString);
|
||||
if (ctx != null) {
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can override this to return a String representation of their
|
||||
* context key for use in caching and logging.
|
||||
* @param contextKey the context key
|
||||
*/
|
||||
protected String contextKeyString(Object contextKey) {
|
||||
return ObjectUtils.nullSafeToString(contextKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a new ApplicationContext for the given key.
|
||||
* <p>To be implemented by subclasses.
|
||||
* @param key the context key
|
||||
* @return the corresponding ApplicationContext instance (new)
|
||||
*/
|
||||
protected abstract ConfigurableApplicationContext loadContext(Object key) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.LineNumberReader;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.core.io.support.EncodedResource;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.test.jdbc.JdbcTestUtils;
|
||||
|
||||
/**
|
||||
* Subclass of AbstractTransactionalSpringContextTests that adds some convenience
|
||||
* functionality for JDBC access. Expects a {@link javax.sql.DataSource} bean
|
||||
* to be defined in the Spring application context.
|
||||
*
|
||||
* <p>This class exposes a {@link org.springframework.jdbc.core.JdbcTemplate}
|
||||
* and provides an easy way to delete from the database in a new transaction.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Thomas Risberg
|
||||
* @since 1.1.1
|
||||
* @see #setDataSource(javax.sql.DataSource)
|
||||
* @see #getJdbcTemplate()
|
||||
* @deprecated as of Spring 3.0, in favor of using the listener-based test context framework
|
||||
* ({@link org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests})
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public abstract class AbstractTransactionalDataSourceSpringContextTests
|
||||
extends AbstractTransactionalSpringContextTests {
|
||||
|
||||
protected JdbcTemplate jdbcTemplate;
|
||||
|
||||
private String sqlScriptEncoding;
|
||||
|
||||
/**
|
||||
* Did this test delete any tables? If so, we forbid transaction completion,
|
||||
* and only allow rollback.
|
||||
*/
|
||||
private boolean zappedTables;
|
||||
|
||||
|
||||
/**
|
||||
* Default constructor for AbstractTransactionalDataSourceSpringContextTests.
|
||||
*/
|
||||
public AbstractTransactionalDataSourceSpringContextTests() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for AbstractTransactionalDataSourceSpringContextTests with a JUnit name.
|
||||
*/
|
||||
public AbstractTransactionalDataSourceSpringContextTests(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Setter: DataSource is provided by Dependency Injection.
|
||||
*/
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JdbcTemplate that this base class manages.
|
||||
*/
|
||||
public final JdbcTemplate getJdbcTemplate() {
|
||||
return this.jdbcTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the encoding for SQL scripts, if different from the platform encoding.
|
||||
* @see #executeSqlScript
|
||||
*/
|
||||
public void setSqlScriptEncoding(String sqlScriptEncoding) {
|
||||
this.sqlScriptEncoding = sqlScriptEncoding;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convenient method to delete all rows from these tables.
|
||||
* Calling this method will make avoidance of rollback by calling
|
||||
* <code>setComplete()</code> impossible.
|
||||
* @see #setComplete
|
||||
*/
|
||||
protected void deleteFromTables(String[] names) {
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
int rowCount = this.jdbcTemplate.update("DELETE FROM " + names[i]);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Deleted " + rowCount + " rows from table " + names[i]);
|
||||
}
|
||||
}
|
||||
this.zappedTables = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden to prevent the transaction committing if a number of tables have been
|
||||
* cleared, as a defensive measure against accidental <i>permanent</i> wiping of a database.
|
||||
* @see org.springframework.test.AbstractTransactionalSpringContextTests#setComplete()
|
||||
*/
|
||||
protected final void setComplete() {
|
||||
if (this.zappedTables) {
|
||||
throw new IllegalStateException("Cannot set complete after deleting tables");
|
||||
}
|
||||
super.setComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the rows in the given table
|
||||
* @param tableName table name to count rows in
|
||||
* @return the number of rows in the table
|
||||
*/
|
||||
protected int countRowsInTable(String tableName) {
|
||||
return this.jdbcTemplate.queryForInt("SELECT COUNT(0) FROM " + tableName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execute the given SQL script. Will be rolled back by default,
|
||||
* according to the fate of the current transaction.
|
||||
* @param sqlResourcePath Spring resource path for the SQL script.
|
||||
* Should normally be loaded by classpath.
|
||||
* <p>Statements should be delimited with a semicolon. If statements are not delimited with
|
||||
* a semicolon then there should be one statement per line. Statements are allowed to span
|
||||
* lines only if they are delimited with a semicolon.
|
||||
* <p><b>Do not use this method to execute DDL if you expect rollback.</b>
|
||||
* @param continueOnError whether or not to continue without throwing
|
||||
* an exception in the event of an error
|
||||
* @throws DataAccessException if there is an error executing a statement
|
||||
* and continueOnError was false
|
||||
*/
|
||||
protected void executeSqlScript(String sqlResourcePath, boolean continueOnError) throws DataAccessException {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Executing SQL script '" + sqlResourcePath + "'");
|
||||
}
|
||||
|
||||
EncodedResource resource =
|
||||
new EncodedResource(getApplicationContext().getResource(sqlResourcePath), this.sqlScriptEncoding);
|
||||
long startTime = System.currentTimeMillis();
|
||||
List statements = new LinkedList();
|
||||
try {
|
||||
LineNumberReader lnr = new LineNumberReader(resource.getReader());
|
||||
String script = JdbcTestUtils.readScript(lnr);
|
||||
char delimiter = ';';
|
||||
if (!JdbcTestUtils.containsSqlScriptDelimiters(script, delimiter)) {
|
||||
delimiter = '\n';
|
||||
}
|
||||
JdbcTestUtils.splitSqlScript(script, delimiter, statements);
|
||||
for (Iterator itr = statements.iterator(); itr.hasNext(); ) {
|
||||
String statement = (String) itr.next();
|
||||
try {
|
||||
int rowsAffected = this.jdbcTemplate.update(statement);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(rowsAffected + " rows affected by SQL: " + statement);
|
||||
}
|
||||
}
|
||||
catch (DataAccessException ex) {
|
||||
if (continueOnError) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("SQL: " + statement + " failed", ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
long elapsedTime = System.currentTimeMillis() - startTime;
|
||||
logger.info("Done executing SQL scriptBuilder '" + sqlResourcePath + "' in " + elapsedTime + " ms");
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new DataAccessResourceFailureException("Failed to open SQL script '" + sqlResourcePath + "'", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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;
|
||||
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionException;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.DefaultTransactionDefinition;
|
||||
|
||||
/**
|
||||
* Convenient base class for JUnit 3.8 based tests that should occur in a
|
||||
* transaction, but normally will roll the transaction back on the completion of
|
||||
* each test.
|
||||
*
|
||||
* <p>This is useful in a range of circumstances, allowing the following benefits:
|
||||
* <ul>
|
||||
* <li>Ability to delete or insert any data in the database, without affecting
|
||||
* other tests
|
||||
* <li>Providing a transactional context for any code requiring a transaction
|
||||
* <li>Ability to write anything to the database without any need to clean up.
|
||||
* </ul>
|
||||
*
|
||||
* <p>This class is typically very fast, compared to traditional setup/teardown
|
||||
* scripts.
|
||||
*
|
||||
* <p>If data should be left in the database, call the {@link #setComplete()}
|
||||
* method in each test. The {@link #setDefaultRollback "defaultRollback"}
|
||||
* property, which defaults to "true", determines whether transactions will
|
||||
* complete by default.
|
||||
*
|
||||
* <p>It is even possible to end the transaction early; for example, to verify lazy
|
||||
* loading behavior of an O/R mapping tool. (This is a valuable away to avoid
|
||||
* unexpected errors when testing a web UI, for example.) Simply call the
|
||||
* {@link #endTransaction()} method. Execution will then occur without a
|
||||
* transactional context.
|
||||
*
|
||||
* <p>The {@link #startNewTransaction()} method may be called after a call to
|
||||
* {@link #endTransaction()} if you wish to create a new transaction, quite
|
||||
* independent of the old transaction. The new transaction's default fate will
|
||||
* be to roll back, unless {@link #setComplete()} is called again during the
|
||||
* scope of the new transaction. Any number of transactions may be created and
|
||||
* ended in this way. The final transaction will automatically be rolled back
|
||||
* when the test case is torn down.
|
||||
*
|
||||
* <p>Transactional behavior requires a single bean in the context implementing the
|
||||
* {@link PlatformTransactionManager} interface. This will be set by the
|
||||
* superclass's Dependency Injection mechanism. If using the superclass's Field
|
||||
* Injection mechanism, the implementation should be named "transactionManager".
|
||||
* This mechanism allows the use of the
|
||||
* {@link AbstractDependencyInjectionSpringContextTests} superclass even when
|
||||
* there is more than one transaction manager in the context.
|
||||
*
|
||||
* <p><b>This base class can also be used without transaction management, if no
|
||||
* PlatformTransactionManager bean is found in the context provided.</b> Be
|
||||
* careful about using this mode, as it allows the potential to permanently
|
||||
* modify data. This mode is available only if dependency checking is turned off
|
||||
* in the {@link AbstractDependencyInjectionSpringContextTests} superclass. The
|
||||
* non-transactional capability is provided to enable use of the same subclass
|
||||
* in different environments.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 1.1.1
|
||||
* @deprecated as of Spring 3.0, in favor of using the listener-based test context framework
|
||||
* ({@link org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests})
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class AbstractTransactionalSpringContextTests extends AbstractDependencyInjectionSpringContextTests {
|
||||
|
||||
/** The transaction manager to use */
|
||||
protected PlatformTransactionManager transactionManager;
|
||||
|
||||
/** Should we roll back by default? */
|
||||
private boolean defaultRollback = true;
|
||||
|
||||
/** Should we commit the current transaction? */
|
||||
private boolean complete = false;
|
||||
|
||||
/** Number of transactions started */
|
||||
private int transactionsStarted = 0;
|
||||
|
||||
/**
|
||||
* Transaction definition used by this test class: by default, a plain
|
||||
* DefaultTransactionDefinition. Subclasses can change this to cause
|
||||
* different behavior.
|
||||
*/
|
||||
protected TransactionDefinition transactionDefinition= new DefaultTransactionDefinition();
|
||||
|
||||
/**
|
||||
* TransactionStatus for this test. Typical subclasses won't need to use it.
|
||||
*/
|
||||
protected TransactionStatus transactionStatus;
|
||||
|
||||
|
||||
/**
|
||||
* Default constructor for AbstractTransactionalSpringContextTests.
|
||||
*/
|
||||
public AbstractTransactionalSpringContextTests() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for AbstractTransactionalSpringContextTests with a JUnit name.
|
||||
*/
|
||||
public AbstractTransactionalSpringContextTests(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specify the transaction manager to use. No transaction management will be
|
||||
* available if this is not set. Populated through dependency injection by
|
||||
* the superclass.
|
||||
* <p>
|
||||
* This mode works only if dependency checking is turned off in the
|
||||
* {@link AbstractDependencyInjectionSpringContextTests} superclass.
|
||||
*/
|
||||
public void setTransactionManager(PlatformTransactionManager transactionManager) {
|
||||
this.transactionManager = transactionManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can set this value in their constructor to change the default,
|
||||
* which is always to roll the transaction back.
|
||||
*/
|
||||
public void setDefaultRollback(final boolean defaultRollback) {
|
||||
this.defaultRollback = defaultRollback;
|
||||
}
|
||||
/**
|
||||
* Get the <em>default rollback</em> flag for this test.
|
||||
* @see #setDefaultRollback(boolean)
|
||||
* @return The <em>default rollback</em> flag.
|
||||
*/
|
||||
protected boolean isDefaultRollback() {
|
||||
return this.defaultRollback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not to rollback transactions for the current test.
|
||||
* <p>The default implementation delegates to {@link #isDefaultRollback()}.
|
||||
* Subclasses can override as necessary.
|
||||
*/
|
||||
protected boolean isRollback() {
|
||||
return isDefaultRollback();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method in an overridden {@link #runBare()} method to prevent
|
||||
* transactional execution.
|
||||
*/
|
||||
protected void preventTransaction() {
|
||||
this.transactionDefinition = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method in an overridden {@link #runBare()} method to override
|
||||
* the transaction attributes that will be used, so that {@link #setUp()}
|
||||
* and {@link #tearDown()} behavior is modified.
|
||||
* @param customDefinition the custom transaction definition
|
||||
*/
|
||||
protected void setTransactionDefinition(TransactionDefinition customDefinition) {
|
||||
this.transactionDefinition = customDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation creates a transaction before test execution.
|
||||
* <p>Override {@link #onSetUpBeforeTransaction()} and/or
|
||||
* {@link #onSetUpInTransaction()} to add custom set-up behavior for
|
||||
* transactional execution. Alternatively, override this method for general
|
||||
* set-up behavior, calling <code>super.onSetUp()</code> as part of your
|
||||
* method implementation.
|
||||
* @throws Exception simply let any exception propagate
|
||||
* @see #onTearDown()
|
||||
*/
|
||||
protected void onSetUp() throws Exception {
|
||||
this.complete = !this.isRollback();
|
||||
|
||||
if (this.transactionManager == null) {
|
||||
this.logger.info("No transaction manager set: test will NOT run within a transaction");
|
||||
}
|
||||
else if (this.transactionDefinition == null) {
|
||||
this.logger.info("No transaction definition set: test will NOT run within a transaction");
|
||||
}
|
||||
else {
|
||||
onSetUpBeforeTransaction();
|
||||
startNewTransaction();
|
||||
try {
|
||||
onSetUpInTransaction();
|
||||
}
|
||||
catch (final Exception ex) {
|
||||
endTransaction();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can override this method to perform any setup operations, such
|
||||
* as populating a database table, <i>before</i> the transaction created by
|
||||
* this class. Only invoked if there <i>is</i> a transaction: that is, if
|
||||
* {@link #preventTransaction()} has not been invoked in an overridden
|
||||
* {@link #runTest()} method.
|
||||
* @throws Exception simply let any exception propagate
|
||||
*/
|
||||
protected void onSetUpBeforeTransaction() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can override this method to perform any setup operations, such
|
||||
* as populating a database table, <i>within</i> the transaction created by
|
||||
* this class.
|
||||
* <p><b>NB:</b> Not called if there is no transaction management, due to no
|
||||
* transaction manager being provided in the context.
|
||||
* <p>If any {@link Throwable} is thrown, the transaction that has been started
|
||||
* prior to the execution of this method will be
|
||||
* {@link #endTransaction() ended} (or rather an attempt will be made to
|
||||
* {@link #endTransaction() end it gracefully}); The offending
|
||||
* {@link Throwable} will then be rethrown.
|
||||
* @throws Exception simply let any exception propagate
|
||||
*/
|
||||
protected void onSetUpInTransaction() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation ends the transaction after test execution.
|
||||
* <p>Override {@link #onTearDownInTransaction()} and/or
|
||||
* {@link #onTearDownAfterTransaction()} to add custom tear-down behavior
|
||||
* for transactional execution. Alternatively, override this method for
|
||||
* general tear-down behavior, calling <code>super.onTearDown()</code> as
|
||||
* part of your method implementation.
|
||||
* <p>Note that {@link #onTearDownInTransaction()} will only be called if a
|
||||
* transaction is still active at the time of the test shutdown. In
|
||||
* particular, it will <i>not</i> be called if the transaction has been
|
||||
* completed with an explicit {@link #endTransaction()} call before.
|
||||
* @throws Exception simply let any exception propagate
|
||||
* @see #onSetUp()
|
||||
*/
|
||||
protected void onTearDown() throws Exception {
|
||||
// Call onTearDownInTransaction and end transaction if the transaction
|
||||
// is still active.
|
||||
if (this.transactionStatus != null && !this.transactionStatus.isCompleted()) {
|
||||
try {
|
||||
onTearDownInTransaction();
|
||||
}
|
||||
finally {
|
||||
endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
// Call onTearDownAfterTransaction if there was at least one
|
||||
// transaction, even if it has been completed early through an
|
||||
// endTransaction() call.
|
||||
if (this.transactionsStarted > 0) {
|
||||
onTearDownAfterTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can override this method to run invariant tests here. The
|
||||
* transaction is <i>still active</i> at this point, so any changes made in
|
||||
* the transaction will still be visible. However, there is no need to clean
|
||||
* up the database, as a rollback will follow automatically.
|
||||
* <p><b>NB:</b> Not called if there is no actual transaction, for example due
|
||||
* to no transaction manager being provided in the application context.
|
||||
* @throws Exception simply let any exception propagate
|
||||
*/
|
||||
protected void onTearDownInTransaction() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can override this method to perform cleanup after a
|
||||
* transaction here. At this point, the transaction is <i>not active anymore</i>.
|
||||
* @throws Exception simply let any exception propagate
|
||||
*/
|
||||
protected void onTearDownAfterTransaction() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Cause the transaction to commit for this test method, even if the test
|
||||
* method is configured to {@link #isRollback() rollback}.
|
||||
* @throws IllegalStateException if the operation cannot be set to complete
|
||||
* as no transaction manager was provided
|
||||
*/
|
||||
protected void setComplete() {
|
||||
if (this.transactionManager == null) {
|
||||
throw new IllegalStateException("No transaction manager set");
|
||||
}
|
||||
this.complete = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Immediately force a commit or rollback of the transaction, according to
|
||||
* the <code>complete</code> and {@link #isRollback() rollback} flags.
|
||||
* <p>Can be used to explicitly let the transaction end early, for example to
|
||||
* check whether lazy associations of persistent objects work outside of a
|
||||
* transaction (that is, have been initialized properly).
|
||||
* @see #setComplete()
|
||||
*/
|
||||
protected void endTransaction() {
|
||||
final boolean commit = this.complete || !isRollback();
|
||||
if (this.transactionStatus != null) {
|
||||
try {
|
||||
if (commit) {
|
||||
this.transactionManager.commit(this.transactionStatus);
|
||||
this.logger.debug("Committed transaction after execution of test [" + getName() + "].");
|
||||
}
|
||||
else {
|
||||
this.transactionManager.rollback(this.transactionStatus);
|
||||
this.logger.debug("Rolled back transaction after execution of test [" + getName() + "].");
|
||||
}
|
||||
}
|
||||
finally {
|
||||
this.transactionStatus = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a new transaction. Only call this method if
|
||||
* {@link #endTransaction()} has been called. {@link #setComplete()} can be
|
||||
* used again in the new transaction. The fate of the new transaction, by
|
||||
* default, will be the usual rollback.
|
||||
* @throws TransactionException if starting the transaction failed
|
||||
*/
|
||||
protected void startNewTransaction() throws TransactionException {
|
||||
if (this.transactionStatus != null) {
|
||||
throw new IllegalStateException("Cannot start new transaction without ending existing transaction: "
|
||||
+ "Invoke endTransaction() before startNewTransaction()");
|
||||
}
|
||||
if (this.transactionManager == null) {
|
||||
throw new IllegalStateException("No transaction manager set");
|
||||
}
|
||||
|
||||
this.transactionStatus = this.transactionManager.getTransaction(this.transactionDefinition);
|
||||
++this.transactionsStarted;
|
||||
this.complete = !this.isRollback();
|
||||
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Began transaction (" + this.transactionsStarted + "): transaction manager ["
|
||||
+ this.transactionManager + "]; rollback [" + this.isRollback() + "].");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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;
|
||||
|
||||
/**
|
||||
* Simple method object encapsulation of the 'test-for-Exception' scenario (for JUnit).
|
||||
*
|
||||
* <p>Used like so:
|
||||
*
|
||||
* <pre class="code">
|
||||
* // the class under test
|
||||
* public class Foo {
|
||||
* public void someBusinessLogic(String name) {
|
||||
* if (name == null) {
|
||||
* throw new IllegalArgumentException("The 'name' argument is required");
|
||||
* }
|
||||
* // rest of business logic here...
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* The test for the above bad argument path can be expressed using the
|
||||
* {@link AssertThrows} class like so:
|
||||
*
|
||||
* <pre class="code">
|
||||
* public class FooTest {
|
||||
* public void testSomeBusinessLogicBadArgumentPath() {
|
||||
* new AssertThrows(IllegalArgumentException.class) {
|
||||
* public void test() {
|
||||
* new Foo().someBusinessLogic(null);
|
||||
* }
|
||||
* }.runTest();
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* This will result in the test passing if the <code>Foo.someBusinessLogic(..)</code>
|
||||
* method threw an {@link java.lang.IllegalArgumentException}; if it did not, the
|
||||
* test would fail with the following message:
|
||||
*
|
||||
* <pre class="code">
|
||||
* "Must have thrown a [class java.lang.IllegalArgumentException]"</pre>
|
||||
*
|
||||
* If the <b>wrong</b> type of {@link java.lang.Exception} was thrown, the
|
||||
* test will also fail, this time with a message similar to the following:
|
||||
*
|
||||
* <pre class="code">
|
||||
* "junit.framework.AssertionFailedError: Was expecting a [class java.lang.UnsupportedOperationException] to be thrown, but instead a [class java.lang.IllegalArgumentException] was thrown"</pre>
|
||||
*
|
||||
* The test for the correct {@link java.lang.Exception} respects polymorphism,
|
||||
* so you can test that any old {@link java.lang.Exception} is thrown like so:
|
||||
*
|
||||
* <pre class="code">
|
||||
* public class FooTest {
|
||||
* public void testSomeBusinessLogicBadArgumentPath() {
|
||||
* // any Exception will do...
|
||||
* new AssertThrows(Exception.class) {
|
||||
* public void test() {
|
||||
* new Foo().someBusinessLogic(null);
|
||||
* }
|
||||
* }.runTest();
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* Intended for use with JUnit 4 and TestNG (as of Spring 3.0).
|
||||
* You might want to compare this class with the
|
||||
* {@link junit.extensions.ExceptionTestCase} class.
|
||||
*
|
||||
* @author Rick Evans
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @deprecated favor use of JUnit 4's {@code @Test(expected=...)} support
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public abstract class AssertThrows {
|
||||
|
||||
private final Class expectedException;
|
||||
|
||||
private String failureMessage;
|
||||
|
||||
private Exception actualException;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new instance of the {@link AssertThrows} class.
|
||||
* @param expectedException the {@link java.lang.Exception} expected to be
|
||||
* thrown during the execution of the surrounding test
|
||||
* @throws IllegalArgumentException if the supplied <code>expectedException</code> is
|
||||
* <code>null</code>; or if said argument is not an {@link java.lang.Exception}-derived class
|
||||
*/
|
||||
public AssertThrows(Class expectedException) {
|
||||
this(expectedException, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of the {@link AssertThrows} class.
|
||||
* @param expectedException the {@link java.lang.Exception} expected to be
|
||||
* thrown during the execution of the surrounding test
|
||||
* @param failureMessage the extra, contextual failure message that will be
|
||||
* included in the failure text if the text fails (can be <code>null</code>)
|
||||
* @throws IllegalArgumentException if the supplied <code>expectedException</code> is
|
||||
* <code>null</code>; or if said argument is not an {@link java.lang.Exception}-derived class
|
||||
*/
|
||||
public AssertThrows(Class expectedException, String failureMessage) {
|
||||
if (expectedException == null) {
|
||||
throw new IllegalArgumentException("The 'expectedException' argument is required");
|
||||
}
|
||||
if (!Exception.class.isAssignableFrom(expectedException)) {
|
||||
throw new IllegalArgumentException(
|
||||
"The 'expectedException' argument is not an Exception type (it obviously must be)");
|
||||
}
|
||||
this.expectedException = expectedException;
|
||||
this.failureMessage = failureMessage;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the {@link java.lang.Exception} expected to be thrown during
|
||||
* the execution of the surrounding test.
|
||||
*/
|
||||
protected Class getExpectedException() {
|
||||
return this.expectedException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the extra, contextual failure message that will be included
|
||||
* in the failure text if the text fails.
|
||||
*/
|
||||
public void setFailureMessage(String failureMessage) {
|
||||
this.failureMessage = failureMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the extra, contextual failure message that will be included
|
||||
* in the failure text if the text fails.
|
||||
*/
|
||||
protected String getFailureMessage() {
|
||||
return this.failureMessage;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Subclass must override this <code>abstract</code> method and
|
||||
* provide the test logic.
|
||||
* @throws Exception if an error occurs during the execution of the
|
||||
* aformentioned test logic
|
||||
*/
|
||||
public abstract void test() throws Exception;
|
||||
|
||||
|
||||
/**
|
||||
* The main template method that drives the running of the
|
||||
* {@link #test() test logic} and the
|
||||
* {@link #checkExceptionExpectations(Exception) checking} of the
|
||||
* resulting (expected) {@link java.lang.Exception}.
|
||||
* @see #test()
|
||||
* @see #doFail()
|
||||
* @see #checkExceptionExpectations(Exception)
|
||||
*/
|
||||
public void runTest() {
|
||||
try {
|
||||
test();
|
||||
doFail();
|
||||
}
|
||||
catch (Exception actualException) {
|
||||
this.actualException = actualException;
|
||||
checkExceptionExpectations(actualException);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method called when the test fails; i.e. the expected
|
||||
* {@link java.lang.Exception} is <b>not</b> thrown.
|
||||
* <p>The default implementation simply fails the test via a call to
|
||||
* {@link junit.framework.Assert#fail(String)}.
|
||||
* <p>If you want to customise the failure message, consider overriding
|
||||
* {@link #createMessageForNoExceptionThrown()}, and / or supplying an
|
||||
* extra, contextual failure message via the appropriate constructor overload.
|
||||
* @see #getFailureMessage()
|
||||
*/
|
||||
protected void doFail() {
|
||||
throw new AssertionError(createMessageForNoExceptionThrown());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the failure message used if the test fails
|
||||
* (i.e. the expected exception is not thrown in the body of the test).
|
||||
* @return the failure message used if the test fails
|
||||
* @see #getFailureMessage()
|
||||
*/
|
||||
protected String createMessageForNoExceptionThrown() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Should have thrown a [").append(this.getExpectedException()).append("]");
|
||||
if (getFailureMessage() != null) {
|
||||
sb.append(": ").append(getFailureMessage());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the donkey work of checking (verifying) that the
|
||||
* {@link java.lang.Exception} that was thrown in the body of a test is
|
||||
* an instance of the {@link #getExpectedException()} class (or an
|
||||
* instance of a subclass).
|
||||
* <p>If you want to customise the failure message, consider overriding
|
||||
* {@link #createMessageForWrongThrownExceptionType(Exception)}.
|
||||
* @param actualException the {@link java.lang.Exception} that has been thrown
|
||||
* in the body of a test method (will never be <code>null</code>)
|
||||
*/
|
||||
protected void checkExceptionExpectations(Exception actualException) {
|
||||
if (!getExpectedException().isAssignableFrom(actualException.getClass())) {
|
||||
AssertionError error =
|
||||
new AssertionError(createMessageForWrongThrownExceptionType(actualException));
|
||||
error.initCause(actualException);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the failure message used if the wrong type
|
||||
* of {@link java.lang.Exception} is thrown in the body of the test.
|
||||
* @param actualException the actual exception thrown
|
||||
* @return the message for the given exception
|
||||
*/
|
||||
protected String createMessageForWrongThrownExceptionType(Exception actualException) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Was expecting a [").append(getExpectedException().getName());
|
||||
sb.append("] to be thrown, but instead a [").append(actualException.getClass().getName());
|
||||
sb.append("] was thrown.");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Expose the actual exception thrown from {@link #test}, if any.
|
||||
* @return the actual exception, or <code>null</code> if none
|
||||
*/
|
||||
public final Exception getActualException() {
|
||||
return this.actualException;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.test;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Superclass for JUnit 3.8 based tests that allows conditional test execution
|
||||
* at the individual test method level. The
|
||||
* {@link #isDisabledInThisEnvironment(String) isDisabledInThisEnvironment()}
|
||||
* method is invoked before the execution of each test method. Subclasses can
|
||||
* override that method to return whether or not the given test should be
|
||||
* executed. Note that the tests will still appear to have executed and passed;
|
||||
* however, log output will show that the test was not executed.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @since 2.0
|
||||
* @see #isDisabledInThisEnvironment
|
||||
* @deprecated as of Spring 3.0, in favor of using the listener-based test context framework
|
||||
* ({@link org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests})
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class ConditionalTestCase extends TestCase {
|
||||
|
||||
private static int disabledTestCount;
|
||||
|
||||
|
||||
/**
|
||||
* Return the number of tests disabled in this environment.
|
||||
*/
|
||||
public static int getDisabledTestCount() {
|
||||
return disabledTestCount;
|
||||
}
|
||||
|
||||
|
||||
/** Logger available to subclasses */
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
|
||||
/**
|
||||
* Default constructor for ConditionalTestCase.
|
||||
*/
|
||||
public ConditionalTestCase() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for ConditionalTestCase with a JUnit name.
|
||||
*/
|
||||
public ConditionalTestCase(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
public void runBare() throws Throwable {
|
||||
// getName will return the name of the method being run
|
||||
if (isDisabledInThisEnvironment(getName())) {
|
||||
recordDisabled();
|
||||
this.logger.info("**** " + getClass().getName() + "." + getName() + " is disabled in this environment: "
|
||||
+ "Total disabled tests = " + getDisabledTestCount());
|
||||
return;
|
||||
}
|
||||
|
||||
// Let JUnit handle execution
|
||||
super.runBare();
|
||||
}
|
||||
|
||||
/**
|
||||
* Should this test run?
|
||||
* @param testMethodName name of the test method
|
||||
* @return whether the test should execute in the current environment
|
||||
*/
|
||||
protected boolean isDisabledInThisEnvironment(String testMethodName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a disabled test.
|
||||
* @return the current disabled test count
|
||||
*/
|
||||
protected int recordDisabled() {
|
||||
return ++disabledTestCount;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
|
||||
import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
|
||||
import org.springframework.transaction.interceptor.TransactionAttributeSource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Java 5 specific subclass of
|
||||
* {@link AbstractTransactionalDataSourceSpringContextTests}, exposing a
|
||||
* {@link SimpleJdbcTemplate} and obeying annotations for transaction control.
|
||||
* </p>
|
||||
* <p>
|
||||
* For example, test methods can be annotated with the regular Spring
|
||||
* {@link org.springframework.transaction.annotation.Transactional @Transactional}
|
||||
* annotation (e.g., to force execution in a read-only transaction) or with the
|
||||
* {@link NotTransactional @NotTransactional} annotation to prevent any
|
||||
* transaction being created at all. In addition, individual test methods can be
|
||||
* annotated with {@link Rollback @Rollback} to override the
|
||||
* {@link #isDefaultRollback() default rollback} settings.
|
||||
* </p>
|
||||
* <p>
|
||||
* The following list constitutes all annotations currently supported by
|
||||
* AbstractAnnotationAwareTransactionalTests:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>{@link DirtiesContext @DirtiesContext}</li>
|
||||
* <li>{@link ProfileValueSourceConfiguration @ProfileValueSourceConfiguration}</li>
|
||||
* <li>{@link IfProfileValue @IfProfileValue}</li>
|
||||
* <li>{@link ExpectedException @ExpectedException}</li>
|
||||
* <li>{@link Timed @Timed}</li>
|
||||
* <li>{@link Repeat @Repeat}</li>
|
||||
* <li>{@link org.springframework.transaction.annotation.Transactional @Transactional}</li>
|
||||
* <li>{@link NotTransactional @NotTransactional}</li>
|
||||
* <li>{@link Rollback @Rollback}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @deprecated as of Spring 3.0, in favor of using the listener-based test context framework
|
||||
* ({@link org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests})
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class AbstractAnnotationAwareTransactionalTests extends
|
||||
AbstractTransactionalDataSourceSpringContextTests {
|
||||
|
||||
protected SimpleJdbcTemplate simpleJdbcTemplate;
|
||||
|
||||
private final TransactionAttributeSource transactionAttributeSource = new AnnotationTransactionAttributeSource();
|
||||
|
||||
/**
|
||||
* {@link ProfileValueSource} available to subclasses but primarily intended
|
||||
* for use in {@link #isDisabledInThisEnvironment(Method)}.
|
||||
* <p>Set to {@link SystemProfileValueSource} by default for backwards
|
||||
* compatibility; however, the value may be changed in the
|
||||
* {@link #AbstractAnnotationAwareTransactionalTests(String)} constructor.
|
||||
*/
|
||||
protected ProfileValueSource profileValueSource = SystemProfileValueSource.getInstance();
|
||||
|
||||
|
||||
/**
|
||||
* Default constructor for AbstractAnnotationAwareTransactionalTests, which
|
||||
* delegates to {@link #AbstractAnnotationAwareTransactionalTests(String)}.
|
||||
*/
|
||||
public AbstractAnnotationAwareTransactionalTests() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new AbstractAnnotationAwareTransactionalTests instance with
|
||||
* the specified JUnit <code>name</code> and retrieves the configured (or
|
||||
* default) {@link ProfileValueSource}.
|
||||
* @param name the name of the current test
|
||||
* @see ProfileValueUtils#retrieveProfileValueSource(Class)
|
||||
*/
|
||||
public AbstractAnnotationAwareTransactionalTests(String name) {
|
||||
super(name);
|
||||
this.profileValueSource = ProfileValueUtils.retrieveProfileValueSource(getClass());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
super.setDataSource(dataSource);
|
||||
// JdbcTemplate will be identically configured
|
||||
this.simpleJdbcTemplate = new SimpleJdbcTemplate(this.jdbcTemplate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for a unique {@link ProfileValueSource} in the supplied
|
||||
* {@link ApplicationContext}. If found, the
|
||||
* <code>profileValueSource</code> for this test will be set to the unique
|
||||
* {@link ProfileValueSource}.
|
||||
* @param applicationContext the ApplicationContext in which to search for
|
||||
* the ProfileValueSource; may not be <code>null</code>
|
||||
* @deprecated Use {@link ProfileValueSourceConfiguration @ProfileValueSourceConfiguration} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
protected void findUniqueProfileValueSourceFromContext(ApplicationContext applicationContext) {
|
||||
Assert.notNull(applicationContext, "Can not search for a ProfileValueSource in a null ApplicationContext.");
|
||||
ProfileValueSource uniqueProfileValueSource = null;
|
||||
Map<?, ?> beans = applicationContext.getBeansOfType(ProfileValueSource.class);
|
||||
if (beans.size() == 1) {
|
||||
uniqueProfileValueSource = (ProfileValueSource) beans.values().iterator().next();
|
||||
}
|
||||
if (uniqueProfileValueSource != null) {
|
||||
this.profileValueSource = uniqueProfileValueSource;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden to populate transaction definition from annotations.
|
||||
*/
|
||||
@Override
|
||||
public void runBare() throws Throwable {
|
||||
|
||||
// getName will return the name of the method being run.
|
||||
if (isDisabledInThisEnvironment(getName())) {
|
||||
// Let superclass log that we didn't run the test.
|
||||
super.runBare();
|
||||
return;
|
||||
}
|
||||
|
||||
final Method testMethod = getTestMethod();
|
||||
|
||||
if (isDisabledInThisEnvironment(testMethod)) {
|
||||
recordDisabled();
|
||||
this.logger.info("**** " + getClass().getName() + "." + getName() + " is disabled in this environment: "
|
||||
+ "Total disabled tests=" + getDisabledTestCount());
|
||||
return;
|
||||
}
|
||||
|
||||
TransactionDefinition explicitTransactionDefinition =
|
||||
this.transactionAttributeSource.getTransactionAttribute(testMethod, getClass());
|
||||
if (explicitTransactionDefinition != null) {
|
||||
this.logger.info("Custom transaction definition [" + explicitTransactionDefinition + "] for test method ["
|
||||
+ getName() + "].");
|
||||
setTransactionDefinition(explicitTransactionDefinition);
|
||||
}
|
||||
else if (testMethod.isAnnotationPresent(NotTransactional.class)) {
|
||||
// Don't have any transaction...
|
||||
preventTransaction();
|
||||
}
|
||||
|
||||
// Let JUnit handle execution. We're just changing the state of the test class first.
|
||||
runTestTimed(new TestExecutionCallback() {
|
||||
public void run() throws Throwable {
|
||||
try {
|
||||
AbstractAnnotationAwareTransactionalTests.super.runBare();
|
||||
}
|
||||
finally {
|
||||
// Mark the context to be blown away if the test was
|
||||
// annotated to result in setDirty being invoked
|
||||
// automatically.
|
||||
if (testMethod.isAnnotationPresent(DirtiesContext.class)) {
|
||||
AbstractAnnotationAwareTransactionalTests.this.setDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
}, testMethod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the test for the supplied <code>testMethod</code> should
|
||||
* run in the current environment.
|
||||
* <p>The default implementation is based on
|
||||
* {@link IfProfileValue @IfProfileValue} semantics.
|
||||
* @param testMethod the test method
|
||||
* @return <code>true</code> if the test is <em>disabled</em> in the current environment
|
||||
* @see ProfileValueUtils#isTestEnabledInThisEnvironment
|
||||
*/
|
||||
protected boolean isDisabledInThisEnvironment(Method testMethod) {
|
||||
return !ProfileValueUtils.isTestEnabledInThisEnvironment(this.profileValueSource, testMethod, getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current test method.
|
||||
*/
|
||||
protected Method getTestMethod() {
|
||||
assertNotNull("TestCase.getName() cannot be null", getName());
|
||||
Method testMethod = null;
|
||||
try {
|
||||
// Use same algorithm as JUnit itself to retrieve the test method
|
||||
// about to be executed (the method name is returned by getName). It
|
||||
// has to be public so we can retrieve it.
|
||||
testMethod = getClass().getMethod(getName(), (Class[]) null);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
fail("Method '" + getName() + "' not found");
|
||||
}
|
||||
if (!Modifier.isPublic(testMethod.getModifiers())) {
|
||||
fail("Method '" + getName() + "' should be public");
|
||||
}
|
||||
return testMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not to rollback transactions for the current test
|
||||
* by taking into consideration the
|
||||
* {@link #isDefaultRollback() default rollback} flag and a possible
|
||||
* method-level override via the {@link Rollback @Rollback} annotation.
|
||||
* @return the <em>rollback</em> flag for the current test
|
||||
*/
|
||||
@Override
|
||||
protected boolean isRollback() {
|
||||
boolean rollback = isDefaultRollback();
|
||||
Rollback rollbackAnnotation = getTestMethod().getAnnotation(Rollback.class);
|
||||
if (rollbackAnnotation != null) {
|
||||
boolean rollbackOverride = rollbackAnnotation.value();
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Method-level @Rollback(" + rollbackOverride + ") overrides default rollback ["
|
||||
+ rollback + "] for test [" + getName() + "].");
|
||||
}
|
||||
rollback = rollbackOverride;
|
||||
}
|
||||
else {
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("No method-level @Rollback override: using default rollback [" + rollback
|
||||
+ "] for test [" + getName() + "].");
|
||||
}
|
||||
}
|
||||
return rollback;
|
||||
}
|
||||
|
||||
private void runTestTimed(TestExecutionCallback tec, Method testMethod) throws Throwable {
|
||||
Timed timed = testMethod.getAnnotation(Timed.class);
|
||||
if (timed == null) {
|
||||
runTest(tec, testMethod);
|
||||
}
|
||||
else {
|
||||
long startTime = System.currentTimeMillis();
|
||||
try {
|
||||
runTest(tec, testMethod);
|
||||
}
|
||||
finally {
|
||||
long elapsed = System.currentTimeMillis() - startTime;
|
||||
if (elapsed > timed.millis()) {
|
||||
fail("Took " + elapsed + " ms; limit was " + timed.millis());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void runTest(TestExecutionCallback tec, Method testMethod) throws Throwable {
|
||||
ExpectedException expectedExceptionAnnotation = testMethod.getAnnotation(ExpectedException.class);
|
||||
boolean exceptionIsExpected = (expectedExceptionAnnotation != null && expectedExceptionAnnotation.value() != null);
|
||||
Class<? extends Throwable> expectedException = (exceptionIsExpected ? expectedExceptionAnnotation.value() : null);
|
||||
|
||||
Repeat repeat = testMethod.getAnnotation(Repeat.class);
|
||||
int runs = ((repeat != null) && (repeat.value() > 1)) ? repeat.value() : 1;
|
||||
|
||||
for (int i = 0; i < runs; i++) {
|
||||
try {
|
||||
if (runs > 1 && this.logger != null && this.logger.isInfoEnabled()) {
|
||||
this.logger.info("Repetition " + (i + 1) + " of test " + testMethod.getName());
|
||||
}
|
||||
tec.run();
|
||||
if (exceptionIsExpected) {
|
||||
fail("Expected exception: " + expectedException.getName());
|
||||
}
|
||||
}
|
||||
catch (Throwable t) {
|
||||
if (!exceptionIsExpected) {
|
||||
throw t;
|
||||
}
|
||||
if (!expectedException.isAssignableFrom(t.getClass())) {
|
||||
// Wrap the unexpected throwable with an explicit message.
|
||||
AssertionFailedError assertionError = new AssertionFailedError("Unexpected exception, expected<" +
|
||||
expectedException.getName() + "> but was<" + t.getClass().getName() + ">");
|
||||
assertionError.initCause(t);
|
||||
throw assertionError;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static interface TestExecutionCallback {
|
||||
|
||||
void run() throws Throwable;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Test annotation which indicates that the
|
||||
* {@link org.springframework.context.ApplicationContext ApplicationContext}
|
||||
* associated with a test is <em>dirty</em> and should be closed:
|
||||
* <ul>
|
||||
* <li>after the current test, when declared at the method level</li>
|
||||
* <li>after each test method in the current test class, when declared at the
|
||||
* class level with class mode set to {@link ClassMode#AFTER_EACH_TEST_METHOD
|
||||
* AFTER_EACH_TEST_METHOD}</li>
|
||||
* <li>after the current test class, when declared at the class level with class
|
||||
* mode set to {@link ClassMode#AFTER_CLASS AFTER_CLASS}</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Use this annotation if a test has modified the context (for example, by
|
||||
* replacing a bean definition). Subsequent tests will be supplied a new
|
||||
* context.
|
||||
* </p>
|
||||
* <p>
|
||||
* <code>@DirtiesContext</code> may be used as a class-level and
|
||||
* method-level annotation within the same class. In such scenarios, the
|
||||
* <code>ApplicationContext</code> will be marked as <em>dirty</em> after any
|
||||
* such annotated method as well as after the entire class. If the
|
||||
* {@link ClassMode} is set to {@link ClassMode#AFTER_EACH_TEST_METHOD
|
||||
* AFTER_EACH_TEST_METHOD}, the context will be marked dirty after each test
|
||||
* method in the class.
|
||||
* </p>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Rod Johnson
|
||||
* @since 2.0
|
||||
*/
|
||||
@Documented
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
public @interface DirtiesContext {
|
||||
|
||||
/**
|
||||
* Defines <i>modes</i> which determine how <code>@DirtiesContext</code>
|
||||
* is interpreted when used to annotate a test class.
|
||||
*/
|
||||
static enum ClassMode {
|
||||
|
||||
/**
|
||||
* The associated <code>ApplicationContext</code> will be marked as
|
||||
* <em>dirty</em> after the test class.
|
||||
*/
|
||||
AFTER_CLASS,
|
||||
|
||||
/**
|
||||
* The associated <code>ApplicationContext</code> will be marked as
|
||||
* <em>dirty</em> after each test method in the class.
|
||||
*/
|
||||
AFTER_EACH_TEST_METHOD
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The <i>mode</i> to use when a test class is annotated with
|
||||
* <code>@DirtiesContext</code>.
|
||||
* <p>Defaults to {@link ClassMode#AFTER_CLASS AFTER_CLASS}.
|
||||
* <p>Note: Setting the class mode on an annotated test method has no meaning,
|
||||
* since the mere presence of the <code>@DirtiesContext</code>
|
||||
* annotation on a test method is sufficient.
|
||||
*/
|
||||
ClassMode classMode() default ClassMode.AFTER_CLASS;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Test annotation to indicate that a test method is required to throw the
|
||||
* specified exception.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Sam Brannen
|
||||
* @since 2.0
|
||||
* @deprecated as of Spring 3.1 in favor of using built-in support for declaring
|
||||
* expected exceptions in the underlying testing framework (e.g., JUnit, TestNG, etc.)
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Deprecated
|
||||
public @interface ExpectedException {
|
||||
|
||||
Class<? extends Throwable> value();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Test annotation to indicate that a test is enabled for a specific testing
|
||||
* profile or environment. If the configured {@link ProfileValueSource} returns
|
||||
* a matching {@link #value() value} for the provided {@link #name() name}, the
|
||||
* test will be enabled.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note: <code>@IfProfileValue</code> can be applied at the class level,
|
||||
* the method level, or both. <code>@IfProfileValue</code> at the class
|
||||
* level overrides method-level usage of <code>@IfProfileValue</code> for
|
||||
* any methods within that class.
|
||||
* </p>
|
||||
* <p>
|
||||
* Examples: when using {@link SystemProfileValueSource} as the
|
||||
* {@link ProfileValueSource} implementation, you can configure a test method to
|
||||
* run only on Java VMs from Sun Microsystems as follows:
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* @IfProfileValue(name = "java.vendor", value = "Sun Microsystems Inc.")
|
||||
* public void testSomething() {
|
||||
* // ...
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
* You can alternatively configure <code>@IfProfileValue</code> with
|
||||
* <em>OR</em> semantics for multiple {@link #values() values} as follows
|
||||
* (assuming a {@link ProfileValueSource} has been appropriately configured for
|
||||
* the "test-groups" name):
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* @IfProfileValue(name = "test-groups", values = { "unit-tests", "integration-tests" })
|
||||
* public void testWhichRunsForUnitOrIntegrationTestGroups() {
|
||||
* // ...
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Sam Brannen
|
||||
* @since 2.0
|
||||
* @see ProfileValueSource
|
||||
* @see ProfileValueSourceConfiguration
|
||||
* @see ProfileValueUtils
|
||||
* @see org.springframework.test.context.junit38.AbstractJUnit38SpringContextTests
|
||||
* @see org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests
|
||||
* @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
|
||||
*/
|
||||
@Documented
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target( { ElementType.TYPE, ElementType.METHOD })
|
||||
public @interface IfProfileValue {
|
||||
|
||||
/**
|
||||
* The <code>name</code> of the <em>profile value</em> against which to
|
||||
* test.
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* A single, permissible <code>value</code> of the <em>profile value</em>
|
||||
* for the given {@link #name() name}.
|
||||
* <p>
|
||||
* Note: Assigning values to both {@link #value()} and {@link #values()}
|
||||
* will lead to a configuration conflict.
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* A list of all permissible <code>values</code> of the
|
||||
* <em>profile value</em> for the given {@link #name() name}.
|
||||
* <p>
|
||||
* Note: Assigning values to both {@link #value()} and {@link #values()}
|
||||
* will lead to a configuration conflict.
|
||||
*/
|
||||
String[] values() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Test annotation to indicate that a method is <i>not transactional</i>.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Sam Brannen
|
||||
* @since 2.0
|
||||
* @deprecated as of Spring 3.0, in favor of moving the non-transactional test
|
||||
* method to a separate (non-transactional) test class or to a
|
||||
* {@link org.springframework.test.context.transaction.BeforeTransaction
|
||||
* @BeforeTransaction} or
|
||||
* {@link org.springframework.test.context.transaction.AfterTransaction
|
||||
* @AfterTransaction} method.
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Deprecated
|
||||
public @interface NotTransactional {
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.annotation;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Strategy interface for retrieving <em>profile values</em> for a given
|
||||
* testing environment.
|
||||
* </p>
|
||||
* <p>
|
||||
* Concrete implementations must provide a <code>public</code> no-args
|
||||
* constructor.
|
||||
* </p>
|
||||
* <p>
|
||||
* Spring provides the following out-of-the-box implementations:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>{@link SystemProfileValueSource}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Sam Brannen
|
||||
* @since 2.0
|
||||
* @see ProfileValueSourceConfiguration
|
||||
* @see IfProfileValue
|
||||
* @see ProfileValueUtils
|
||||
*/
|
||||
public interface ProfileValueSource {
|
||||
|
||||
/**
|
||||
* Get the <em>profile value</em> indicated by the specified key.
|
||||
* @param key the name of the <em>profile value</em>
|
||||
* @return the String value of the <em>profile value</em>, or <code>null</code>
|
||||
* if there is no <em>profile value</em> with that key
|
||||
*/
|
||||
String get(String key);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* ProfileValueSourceConfiguration is a class-level annotation which is used to
|
||||
* specify what type of {@link ProfileValueSource} to use when retrieving
|
||||
* <em>profile values</em> configured via the {@link IfProfileValue
|
||||
* @IfProfileValue} annotation.
|
||||
* </p>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 2.5
|
||||
* @see ProfileValueSource
|
||||
* @see IfProfileValue
|
||||
* @see ProfileValueUtils
|
||||
*/
|
||||
@Documented
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface ProfileValueSourceConfiguration {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The type of {@link ProfileValueSource} to use when retrieving
|
||||
* <em>profile values</em>.
|
||||
* </p>
|
||||
*
|
||||
* @see SystemProfileValueSource
|
||||
*/
|
||||
Class<? extends ProfileValueSource> value() default SystemProfileValueSource.class;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* General utility methods for working with <em>profile values</em>.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see ProfileValueSource
|
||||
* @see ProfileValueSourceConfiguration
|
||||
* @see IfProfileValue
|
||||
*/
|
||||
public abstract class ProfileValueUtils {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ProfileValueUtils.class);
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the {@link ProfileValueSource} type for the specified
|
||||
* {@link Class test class} as configured via the
|
||||
* {@link ProfileValueSourceConfiguration
|
||||
* @ProfileValueSourceConfiguration} annotation and instantiates a new
|
||||
* instance of that type.
|
||||
* <p>
|
||||
* If {@link ProfileValueSourceConfiguration
|
||||
* @ProfileValueSourceConfiguration} is not present on the specified
|
||||
* class or if a custom {@link ProfileValueSource} is not declared, the
|
||||
* default {@link SystemProfileValueSource} will be returned instead.
|
||||
*
|
||||
* @param testClass The test class for which the ProfileValueSource should
|
||||
* be retrieved
|
||||
* @return the configured (or default) ProfileValueSource for the specified
|
||||
* class
|
||||
* @see SystemProfileValueSource
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static ProfileValueSource retrieveProfileValueSource(Class<?> testClass) {
|
||||
Assert.notNull(testClass, "testClass must not be null");
|
||||
|
||||
Class<ProfileValueSourceConfiguration> annotationType = ProfileValueSourceConfiguration.class;
|
||||
ProfileValueSourceConfiguration config = testClass.getAnnotation(annotationType);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Retrieved @ProfileValueSourceConfiguration [" + config + "] for test class ["
|
||||
+ testClass.getName() + "]");
|
||||
}
|
||||
|
||||
Class<? extends ProfileValueSource> profileValueSourceType;
|
||||
if (config != null) {
|
||||
profileValueSourceType = config.value();
|
||||
}
|
||||
else {
|
||||
profileValueSourceType = (Class<? extends ProfileValueSource>) AnnotationUtils.getDefaultValue(annotationType);
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Retrieved ProfileValueSource type [" + profileValueSourceType + "] for class ["
|
||||
+ testClass.getName() + "]");
|
||||
}
|
||||
|
||||
ProfileValueSource profileValueSource;
|
||||
if (SystemProfileValueSource.class.equals(profileValueSourceType)) {
|
||||
profileValueSource = SystemProfileValueSource.getInstance();
|
||||
}
|
||||
else {
|
||||
try {
|
||||
profileValueSource = profileValueSourceType.newInstance();
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Could not instantiate a ProfileValueSource of type [" + profileValueSourceType
|
||||
+ "] for class [" + testClass.getName() + "]: using default.", e);
|
||||
}
|
||||
profileValueSource = SystemProfileValueSource.getInstance();
|
||||
}
|
||||
}
|
||||
|
||||
return profileValueSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the supplied <code>testClass</code> is <em>enabled</em> in
|
||||
* the current environment, as specified by the {@link IfProfileValue
|
||||
* @IfProfileValue} annotation at the class level.
|
||||
* <p>
|
||||
* Defaults to <code>true</code> if no {@link IfProfileValue
|
||||
* @IfProfileValue} annotation is declared.
|
||||
*
|
||||
* @param testClass the test class
|
||||
* @return <code>true</code> if the test is <em>enabled</em> in the current
|
||||
* environment
|
||||
*/
|
||||
public static boolean isTestEnabledInThisEnvironment(Class<?> testClass) {
|
||||
IfProfileValue ifProfileValue = testClass.getAnnotation(IfProfileValue.class);
|
||||
return isTestEnabledInThisEnvironment(retrieveProfileValueSource(testClass), ifProfileValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the supplied <code>testMethod</code> is <em>enabled</em> in
|
||||
* the current environment, as specified by the {@link IfProfileValue
|
||||
* @IfProfileValue} annotation, which may be declared on the test
|
||||
* method itself or at the class level. Class-level usage overrides
|
||||
* method-level usage.
|
||||
* <p>
|
||||
* Defaults to <code>true</code> if no {@link IfProfileValue
|
||||
* @IfProfileValue} annotation is declared.
|
||||
*
|
||||
* @param testMethod the test method
|
||||
* @param testClass the test class
|
||||
* @return <code>true</code> if the test is <em>enabled</em> in the current
|
||||
* environment
|
||||
*/
|
||||
public static boolean isTestEnabledInThisEnvironment(Method testMethod, Class<?> testClass) {
|
||||
return isTestEnabledInThisEnvironment(retrieveProfileValueSource(testClass), testMethod, testClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the supplied <code>testMethod</code> is <em>enabled</em> in
|
||||
* the current environment, as specified by the {@link IfProfileValue
|
||||
* @IfProfileValue} annotation, which may be declared on the test
|
||||
* method itself or at the class level. Class-level usage overrides
|
||||
* method-level usage.
|
||||
* <p>
|
||||
* Defaults to <code>true</code> if no {@link IfProfileValue
|
||||
* @IfProfileValue} annotation is declared.
|
||||
*
|
||||
* @param profileValueSource the ProfileValueSource to use to determine if
|
||||
* the test is enabled
|
||||
* @param testMethod the test method
|
||||
* @param testClass the test class
|
||||
* @return <code>true</code> if the test is <em>enabled</em> in the current
|
||||
* environment
|
||||
*/
|
||||
public static boolean isTestEnabledInThisEnvironment(ProfileValueSource profileValueSource, Method testMethod,
|
||||
Class<?> testClass) {
|
||||
|
||||
IfProfileValue ifProfileValue = testClass.getAnnotation(IfProfileValue.class);
|
||||
boolean classLevelEnabled = isTestEnabledInThisEnvironment(profileValueSource, ifProfileValue);
|
||||
|
||||
if (classLevelEnabled) {
|
||||
ifProfileValue = testMethod.getAnnotation(IfProfileValue.class);
|
||||
return isTestEnabledInThisEnvironment(profileValueSource, ifProfileValue);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the <code>value</code> (or one of the <code>values</code>)
|
||||
* in the supplied {@link IfProfileValue @IfProfileValue} annotation is
|
||||
* <em>enabled</em> in the current environment.
|
||||
*
|
||||
* @param profileValueSource the ProfileValueSource to use to determine if
|
||||
* the test is enabled
|
||||
* @param ifProfileValue the annotation to introspect; may be
|
||||
* <code>null</code>
|
||||
* @return <code>true</code> if the test is <em>enabled</em> in the current
|
||||
* environment or if the supplied <code>ifProfileValue</code> is
|
||||
* <code>null</code>
|
||||
*/
|
||||
private static boolean isTestEnabledInThisEnvironment(ProfileValueSource profileValueSource,
|
||||
IfProfileValue ifProfileValue) {
|
||||
|
||||
if (ifProfileValue == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String environmentValue = profileValueSource.get(ifProfileValue.name());
|
||||
String[] annotatedValues = ifProfileValue.values();
|
||||
if (StringUtils.hasLength(ifProfileValue.value())) {
|
||||
if (annotatedValues.length > 0) {
|
||||
throw new IllegalArgumentException("Setting both the 'value' and 'values' attributes "
|
||||
+ "of @IfProfileValue is not allowed: choose one or the other.");
|
||||
}
|
||||
annotatedValues = new String[] { ifProfileValue.value() };
|
||||
}
|
||||
|
||||
for (String value : annotatedValues) {
|
||||
if (ObjectUtils.nullSafeEquals(value, environmentValue)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Test annotation to indicate that a test method should be invoked repeatedly.
|
||||
* <p />
|
||||
* Note that the scope of execution to be repeated includes execution of the
|
||||
* test method itself as well as any <em>set up</em> or <em>tear down</em> of
|
||||
* the test fixture.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Sam Brannen
|
||||
* @since 2.0
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Repeat {
|
||||
|
||||
int value() default 1;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Test annotation to indicate whether or not the transaction for the annotated
|
||||
* test method should be <em>rolled back</em> after the test method has
|
||||
* completed. If <code>true</code>, the transaction will be rolled back;
|
||||
* otherwise, the transaction will be committed.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 2.5
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Rollback {
|
||||
|
||||
/**
|
||||
* Whether or not the transaction for the annotated method should be rolled
|
||||
* back after the method has completed.
|
||||
*/
|
||||
boolean value() default true;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.test.annotation;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ProfileValueSource} which uses system properties as
|
||||
* the underlying source.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Sam Brannen
|
||||
* @since 2.0
|
||||
*/
|
||||
public class SystemProfileValueSource implements ProfileValueSource {
|
||||
|
||||
private static final SystemProfileValueSource INSTANCE = new SystemProfileValueSource();
|
||||
|
||||
|
||||
/**
|
||||
* Obtain the canonical instance of this ProfileValueSource.
|
||||
*/
|
||||
public static final SystemProfileValueSource getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Private constructor, enforcing the singleton pattern.
|
||||
*/
|
||||
private SystemProfileValueSource() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the <em>profile value</em> indicated by the specified key from the
|
||||
* system properties.
|
||||
* @see System#getProperty(String)
|
||||
*/
|
||||
public String get(String key) {
|
||||
Assert.hasText(key, "'key' must not be empty");
|
||||
return System.getProperty(key);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Test-specific annotation to indicate that a test method has to finish
|
||||
* execution in a {@link #millis() specified time period}.
|
||||
* </p>
|
||||
* <p>
|
||||
* If the text execution takes longer than the specified time period, then the
|
||||
* test is to be considered failed.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that the time period includes execution of the test method itself, any
|
||||
* {@link Repeat repetitions} of the test, and any <em>set up</em> or
|
||||
* <em>tear down</em> of the test fixture.
|
||||
* </p>
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Sam Brannen
|
||||
* @since 2.0
|
||||
* @see Repeat
|
||||
* @see AbstractAnnotationAwareTransactionalTests
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Timed {
|
||||
|
||||
/**
|
||||
* The maximum amount of time (in milliseconds) that a test execution can
|
||||
* take without being marked as failed due to taking too long.
|
||||
*/
|
||||
long millis();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* Support classes for annotation-driven tests.
|
||||
*
|
||||
*/
|
||||
package org.springframework.test.annotation;
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* {@code ActiveProfiles} is a class-level annotation that is used to declare
|
||||
* which <em>active bean definition profiles</em> should be used when loading
|
||||
* an {@link org.springframework.context.ApplicationContext ApplicationContext}
|
||||
* for test classes.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 3.1
|
||||
* @see SmartContextLoader
|
||||
* @see MergedContextConfiguration
|
||||
* @see ContextConfiguration
|
||||
* @see org.springframework.context.ApplicationContext
|
||||
* @see org.springframework.context.annotation.Profile
|
||||
*/
|
||||
@Documented
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface ActiveProfiles {
|
||||
|
||||
/**
|
||||
* Alias for {@link #profiles}.
|
||||
*
|
||||
* <p>This attribute may <strong>not</strong> be used in conjunction
|
||||
* with {@link #profiles}, but it may be used <em>instead</em> of
|
||||
* {@link #profiles}.
|
||||
*/
|
||||
String[] value() default {};
|
||||
|
||||
/**
|
||||
* The bean definition profiles to activate.
|
||||
*
|
||||
* <p>This attribute may <strong>not</strong> be used in conjunction
|
||||
* with {@link #value}, but it may be used <em>instead</em> of
|
||||
* {@link #value}.
|
||||
*/
|
||||
String[] profiles() default {};
|
||||
|
||||
/**
|
||||
* Whether or not bean definition profiles from superclasses should be
|
||||
* <em>inherited</em>.
|
||||
*
|
||||
* <p>The default value is <code>true</code>, which means that an annotated
|
||||
* class will <em>inherit</em> bean definition profiles defined by an
|
||||
* annotated superclass. Specifically, the bean definition profiles for an
|
||||
* annotated class will be appended to the list of bean definition profiles
|
||||
* defined by an annotated superclass. Thus, subclasses have the option of
|
||||
* <em>extending</em> the list of bean definition profiles.
|
||||
*
|
||||
* <p>If <code>inheritProfiles</code> is set to <code>false</code>, the bean
|
||||
* definition profiles for the annotated class will <em>shadow</em> and
|
||||
* effectively replace any bean definition profiles defined by a superclass.
|
||||
*
|
||||
* <p>In the following example, the {@code ApplicationContext} for
|
||||
* {@code BaseTest} will be loaded using only the "base"
|
||||
* bean definition profile; beans defined in the "extended" profile
|
||||
* will therefore not be loaded. In contrast, the {@code ApplicationContext}
|
||||
* for {@code ExtendedTest} will be loaded using the "base"
|
||||
* <strong>and</strong> "extended" bean definition profiles.
|
||||
* <pre class="code">
|
||||
* @ActiveProfiles("base")
|
||||
* @ContextConfiguration
|
||||
* public class BaseTest {
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* @ActiveProfiles("extended")
|
||||
* @ContextConfiguration
|
||||
* public class ExtendedTest extends BaseTest {
|
||||
* // ...
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>Note: {@code @ActiveProfiles} can be used when loading an
|
||||
* {@code ApplicationContext} from path-based resource locations or
|
||||
* configuration classes.
|
||||
* @see ContextConfiguration#locations
|
||||
* @see ContextConfiguration#classes
|
||||
* @see ContextConfiguration#inheritLocations
|
||||
*/
|
||||
boolean inheritProfiles() default true;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Cache for Spring {@link ApplicationContext ApplicationContexts}
|
||||
* in a test environment.
|
||||
*
|
||||
* <p>Maintains a cache of {@link ApplicationContext contexts} keyed by
|
||||
* {@link MergedContextConfiguration} instances. This has significant performance
|
||||
* benefits if initializing the context would take time. While initializing a
|
||||
* Spring context itself is very quick, some beans in a context, such as a
|
||||
* {@link org.springframework.orm.hibernate3.LocalSessionFactoryBean LocalSessionFactoryBean}
|
||||
* for working with Hibernate, may take some time to initialize. Hence it often
|
||||
* makes sense to perform that initialization once.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
*/
|
||||
class ContextCache {
|
||||
|
||||
/**
|
||||
* Map of context keys to Spring ApplicationContext instances.
|
||||
*/
|
||||
private final Map<MergedContextConfiguration, ApplicationContext> contextMap = new ConcurrentHashMap<MergedContextConfiguration, ApplicationContext>();
|
||||
|
||||
private int hitCount;
|
||||
|
||||
private int missCount;
|
||||
|
||||
|
||||
/**
|
||||
* Clears all contexts from the cache.
|
||||
*/
|
||||
void clear() {
|
||||
this.contextMap.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears hit and miss count statistics for the cache (i.e., resets counters
|
||||
* to zero).
|
||||
*/
|
||||
void clearStatistics() {
|
||||
this.hitCount = 0;
|
||||
this.missCount = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether there is a cached context for the given key.
|
||||
* @param key the context key (never <code>null</code>)
|
||||
*/
|
||||
boolean contains(MergedContextConfiguration key) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
return this.contextMap.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a cached ApplicationContext for the given key.
|
||||
* <p>The {@link #getHitCount() hit} and {@link #getMissCount() miss}
|
||||
* counts will be updated accordingly.
|
||||
* @param key the context key (never <code>null</code>)
|
||||
* @return the corresponding ApplicationContext instance,
|
||||
* or <code>null</code> if not found in the cache.
|
||||
* @see #remove
|
||||
*/
|
||||
ApplicationContext get(MergedContextConfiguration key) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
ApplicationContext context = this.contextMap.get(key);
|
||||
if (context == null) {
|
||||
incrementMissCount();
|
||||
}
|
||||
else {
|
||||
incrementHitCount();
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the hit count by one. A <em>hit</em> is an access to the
|
||||
* cache, which returned a non-null context for a queried key.
|
||||
*/
|
||||
private void incrementHitCount() {
|
||||
this.hitCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the miss count by one. A <em>miss</em> is an access to the
|
||||
* cache, which returned a <code>null</code> context for a queried key.
|
||||
*/
|
||||
private void incrementMissCount() {
|
||||
this.missCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the overall hit count for this cache. A <em>hit</em> is an access
|
||||
* to the cache, which returned a non-null context for a queried key.
|
||||
*/
|
||||
int getHitCount() {
|
||||
return this.hitCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the overall miss count for this cache. A <em>miss</em> is an
|
||||
* access to the cache, which returned a <code>null</code> context for a
|
||||
* queried key.
|
||||
*/
|
||||
int getMissCount() {
|
||||
return this.missCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicitly add an ApplicationContext instance to the cache under the given key.
|
||||
* @param key the context key (never <code>null</code>)
|
||||
* @param context the ApplicationContext instance (never <code>null</code>)
|
||||
*/
|
||||
void put(MergedContextConfiguration key, ApplicationContext context) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
Assert.notNull(context, "ApplicationContext must not be null");
|
||||
this.contextMap.put(key, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the context with the given key.
|
||||
* @param key the context key (never <code>null</code>)
|
||||
* @return the corresponding ApplicationContext instance,
|
||||
* or <code>null</code> if not found in the cache.
|
||||
* @see #setDirty
|
||||
*/
|
||||
ApplicationContext remove(MergedContextConfiguration key) {
|
||||
return this.contextMap.remove(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the context with the given key as dirty, effectively
|
||||
* {@link #remove removing} the context from the cache and explicitly
|
||||
* {@link ConfigurableApplicationContext#close() closing} it if
|
||||
* it is an instance of {@link ConfigurableApplicationContext}.
|
||||
* <p>Generally speaking, you would only call this method if you change the
|
||||
* state of a singleton bean, potentially affecting future interaction with
|
||||
* the context.
|
||||
* @param key the context key (never <code>null</code>)
|
||||
* @see #remove
|
||||
*/
|
||||
void setDirty(MergedContextConfiguration key) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
ApplicationContext context = remove(key);
|
||||
if (context instanceof ConfigurableApplicationContext) {
|
||||
((ConfigurableApplicationContext) context).close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the number of contexts currently stored in the cache. If the
|
||||
* cache contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
|
||||
* <tt>Integer.MAX_VALUE</tt>.
|
||||
*/
|
||||
int size() {
|
||||
return this.contextMap.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a text string, which contains the {@link #size() size} as well
|
||||
* as the {@link #hitCount hit} and {@link #missCount miss} counts.
|
||||
*/
|
||||
public String toString() {
|
||||
return new ToStringCreator(this)//
|
||||
.append("size", size())//
|
||||
.append("hitCount", getHitCount())//
|
||||
.append("missCount", getMissCount())//
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* {@code ContextConfiguration} defines class-level metadata that is
|
||||
* used to determine how to load and configure an
|
||||
* {@link org.springframework.context.ApplicationContext ApplicationContext}
|
||||
* for test classes.
|
||||
*
|
||||
* <p>Prior to Spring 3.1, only path-based resource locations were supported.
|
||||
* As of Spring 3.1, {@link #loader context loaders} may choose to support
|
||||
* either path-based or class-based resources (but not both). Consequently
|
||||
* {@code @ContextConfiguration} can be used to declare either path-based
|
||||
* resource locations (via the {@link #locations} or {@link #value}
|
||||
* attribute) <i>or</i> configuration classes (via the {@link #classes}
|
||||
* attribute).
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 2.5
|
||||
* @see ContextLoader
|
||||
* @see SmartContextLoader
|
||||
* @see ContextConfigurationAttributes
|
||||
* @see MergedContextConfiguration
|
||||
* @see org.springframework.context.ApplicationContext
|
||||
* @see ActiveProfiles
|
||||
*/
|
||||
@Documented
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface ContextConfiguration {
|
||||
|
||||
/**
|
||||
* Alias for {@link #locations}.
|
||||
*
|
||||
* <p>This attribute may <strong>not</strong> be used in conjunction
|
||||
* with {@link #locations} or {@link #classes}, but it may be used
|
||||
* instead of {@link #locations}.
|
||||
* @since 3.0
|
||||
*/
|
||||
String[] value() default {};
|
||||
|
||||
/**
|
||||
* The resource locations to use for loading an
|
||||
* {@link org.springframework.context.ApplicationContext ApplicationContext}.
|
||||
*
|
||||
* <p>Check out the Javadoc for
|
||||
* {@link org.springframework.test.context.support.AbstractContextLoader#modifyLocations
|
||||
* AbstractContextLoader.modifyLocations()} for details on how a location
|
||||
* String will be interpreted at runtime, in particular in case of a relative
|
||||
* path. Also, check out the documentation on
|
||||
* {@link org.springframework.test.context.support.AbstractContextLoader#generateDefaultLocations
|
||||
* AbstractContextLoader.generateDefaultLocations()} for details on the default
|
||||
* locations that are going to be used if none are specified.
|
||||
*
|
||||
* <p>Note that the above-mentioned default rules only apply for a standard
|
||||
* {@link org.springframework.test.context.support.AbstractContextLoader
|
||||
* AbstractContextLoader} subclass such as
|
||||
* {@link org.springframework.test.context.support.GenericXmlContextLoader
|
||||
* GenericXmlContextLoader} which is the effective default implementation
|
||||
* used at runtime if <code>locations</code> are configured. See the
|
||||
* documentation for {@link #loader} for further details regarding default
|
||||
* loaders.
|
||||
*
|
||||
* <p>This attribute may <strong>not</strong> be used in conjunction with
|
||||
* {@link #value} or {@link #classes}, but it may be used instead of
|
||||
* {@link #value}.
|
||||
* @since 2.5
|
||||
*/
|
||||
String[] locations() default {};
|
||||
|
||||
/**
|
||||
* The {@link org.springframework.context.annotation.Configuration
|
||||
* configuration classes} to use for loading an
|
||||
* {@link org.springframework.context.ApplicationContext ApplicationContext}.
|
||||
*
|
||||
* <p>Check out the Javadoc for
|
||||
* {@link org.springframework.test.context.support.AnnotationConfigContextLoader#detectDefaultConfigurationClasses
|
||||
* AnnotationConfigContextLoader.detectDefaultConfigurationClasses()} for details
|
||||
* on how default configuration classes will be detected if none are specified.
|
||||
* See the documentation for {@link #loader} for further details regarding
|
||||
* default loaders.
|
||||
*
|
||||
* <p>This attribute may <strong>not</strong> be used in conjunction with
|
||||
* {@link #locations} or {@link #value}.
|
||||
* @since 3.1
|
||||
* @see org.springframework.context.annotation.Configuration
|
||||
* @see org.springframework.test.context.support.AnnotationConfigContextLoader
|
||||
*/
|
||||
Class<?>[] classes() default {};
|
||||
|
||||
/**
|
||||
* Whether or not {@link #locations resource locations} or
|
||||
* {@link #classes configuration classes} from superclasses should be
|
||||
* <em>inherited</em>.
|
||||
*
|
||||
* <p>The default value is <code>true</code>. This means that an annotated
|
||||
* class will <em>inherit</em> the resource locations or configuration
|
||||
* classes defined by annotated superclasses. Specifically, the resource
|
||||
* locations or configuration classes for an annotated class will be
|
||||
* appended to the list of resource locations or configuration classes
|
||||
* defined by annotated superclasses. Thus, subclasses have the option of
|
||||
* <em>extending</em> the list of resource locations or configuration
|
||||
* classes.
|
||||
*
|
||||
* <p>If <code>inheritLocations</code> is set to <code>false</code>, the
|
||||
* resource locations or configuration classes for the annotated class
|
||||
* will <em>shadow</em> and effectively replace any resource locations
|
||||
* or configuration classes defined by superclasses.
|
||||
*
|
||||
* <p>In the following example that uses path-based resource locations, the
|
||||
* {@link org.springframework.context.ApplicationContext ApplicationContext}
|
||||
* for {@code ExtendedTest} will be loaded from
|
||||
* "base-context.xml" <strong>and</strong>
|
||||
* "extended-context.xml", in that order. Beans defined in
|
||||
* "extended-context.xml" may therefore override those defined in
|
||||
* "base-context.xml".
|
||||
* <pre class="code">
|
||||
* @ContextConfiguration("base-context.xml")
|
||||
* public class BaseTest {
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* @ContextConfiguration("extended-context.xml")
|
||||
* public class ExtendedTest extends BaseTest {
|
||||
* // ...
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>Similarly, in the following example that uses configuration
|
||||
* classes, the
|
||||
* {@link org.springframework.context.ApplicationContext ApplicationContext}
|
||||
* for {@code ExtendedTest} will be loaded from the
|
||||
* {@code BaseConfig} <strong>and</strong> {@code ExtendedConfig}
|
||||
* configuration classes, in that order. Beans defined in
|
||||
* {@code ExtendedConfig} may therefore override those defined in
|
||||
* {@code BaseConfig}.
|
||||
* <pre class="code">
|
||||
* @ContextConfiguration(classes=BaseConfig.class)
|
||||
* public class BaseTest {
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* @ContextConfiguration(classes=ExtendedConfig.class)
|
||||
* public class ExtendedTest extends BaseTest {
|
||||
* // ...
|
||||
* }
|
||||
* </pre>
|
||||
* @since 2.5
|
||||
*/
|
||||
boolean inheritLocations() default true;
|
||||
|
||||
/**
|
||||
* The type of {@link ContextLoader} (or {@link SmartContextLoader}) to use
|
||||
* for loading an {@link org.springframework.context.ApplicationContext
|
||||
* ApplicationContext}.
|
||||
*
|
||||
* <p>If not specified, the loader will be inherited from the first superclass
|
||||
* that is annotated with {@code @ContextConfiguration} and specifies an
|
||||
* explicit loader. If no class in the hierarchy specifies an explicit
|
||||
* loader, a default loader will be used instead.
|
||||
*
|
||||
* <p>The default concrete implementation chosen at runtime will be
|
||||
* {@link org.springframework.test.context.support.DelegatingSmartContextLoader
|
||||
* DelegatingSmartContextLoader}. For further details on the default behavior
|
||||
* of various concrete {@code ContextLoaders}, check out the Javadoc for
|
||||
* {@link org.springframework.test.context.support.AbstractContextLoader
|
||||
* AbstractContextLoader},
|
||||
* {@link org.springframework.test.context.support.GenericXmlContextLoader
|
||||
* GenericXmlContextLoader}, and
|
||||
* {@link org.springframework.test.context.support.AnnotationConfigContextLoader
|
||||
* AnnotationConfigContextLoader}.
|
||||
* @since 2.5
|
||||
*/
|
||||
Class<? extends ContextLoader> loader() default ContextLoader.class;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* <code>ContextConfigurationAttributes</code> encapsulates the context
|
||||
* configuration attributes declared on a test class via
|
||||
* {@link ContextConfiguration @ContextConfiguration}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 3.1
|
||||
* @see ContextConfiguration
|
||||
* @see SmartContextLoader#processContextConfiguration(ContextConfigurationAttributes)
|
||||
* @see MergedContextConfiguration
|
||||
*/
|
||||
public class ContextConfigurationAttributes {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ContextConfigurationAttributes.class);
|
||||
|
||||
private final Class<?> declaringClass;
|
||||
|
||||
private String[] locations;
|
||||
|
||||
private Class<?>[] classes;
|
||||
|
||||
private final boolean inheritLocations;
|
||||
|
||||
private final Class<? extends ContextLoader> contextLoaderClass;
|
||||
|
||||
|
||||
/**
|
||||
* Resolve resource locations from the {@link ContextConfiguration#locations() locations}
|
||||
* and {@link ContextConfiguration#value() value} attributes of the supplied
|
||||
* {@link ContextConfiguration} annotation.
|
||||
*
|
||||
* @throws IllegalStateException if both the locations and value attributes have been declared
|
||||
*/
|
||||
private static String[] resolveLocations(Class<?> declaringClass, ContextConfiguration contextConfiguration) {
|
||||
Assert.notNull(declaringClass, "declaringClass must not be null");
|
||||
|
||||
String[] locations = contextConfiguration.locations();
|
||||
String[] valueLocations = contextConfiguration.value();
|
||||
|
||||
if (!ObjectUtils.isEmpty(valueLocations) && !ObjectUtils.isEmpty(locations)) {
|
||||
String msg = String.format("Test class [%s] has been configured with @ContextConfiguration's 'value' %s "
|
||||
+ "and 'locations' %s attributes. Only one declaration of resource "
|
||||
+ "locations is permitted per @ContextConfiguration annotation.", declaringClass.getName(),
|
||||
ObjectUtils.nullSafeToString(valueLocations), ObjectUtils.nullSafeToString(locations));
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
else if (!ObjectUtils.isEmpty(valueLocations)) {
|
||||
locations = valueLocations;
|
||||
}
|
||||
|
||||
return locations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@link ContextConfigurationAttributes} instance for the
|
||||
* supplied {@link ContextConfiguration @ContextConfiguration} annotation and
|
||||
* the {@link Class test class} that declared it.
|
||||
* @param declaringClass the test class that declared {@code @ContextConfiguration}
|
||||
* @param contextConfiguration the annotation from which to retrieve the attributes
|
||||
*/
|
||||
public ContextConfigurationAttributes(Class<?> declaringClass, ContextConfiguration contextConfiguration) {
|
||||
this(declaringClass, resolveLocations(declaringClass, contextConfiguration), contextConfiguration.classes(),
|
||||
contextConfiguration.inheritLocations(), contextConfiguration.loader());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@link ContextConfigurationAttributes} instance for the
|
||||
* {@link Class test class} that declared the
|
||||
* {@link ContextConfiguration @ContextConfiguration} annotation and its
|
||||
* corresponding attributes.
|
||||
*
|
||||
* @param declaringClass the test class that declared {@code @ContextConfiguration}
|
||||
* @param locations the resource locations declared via {@code @ContextConfiguration}
|
||||
* @param classes the configuration classes declared via {@code @ContextConfiguration}
|
||||
* @param inheritLocations the <code>inheritLocations</code> flag declared via {@code @ContextConfiguration}
|
||||
* @param contextLoaderClass the {@code ContextLoader} class declared via {@code @ContextConfiguration}
|
||||
* @throws IllegalArgumentException if the {@code declaringClass} or {@code contextLoaderClass} is
|
||||
* <code>null</code>, or if the {@code locations} and {@code classes} are both non-empty
|
||||
*/
|
||||
public ContextConfigurationAttributes(Class<?> declaringClass, String[] locations, Class<?>[] classes,
|
||||
boolean inheritLocations, Class<? extends ContextLoader> contextLoaderClass) {
|
||||
|
||||
Assert.notNull(declaringClass, "declaringClass must not be null");
|
||||
Assert.notNull(contextLoaderClass, "contextLoaderClass must not be null");
|
||||
|
||||
if (!ObjectUtils.isEmpty(locations) && !ObjectUtils.isEmpty(classes)) {
|
||||
String msg = String.format(
|
||||
"Test class [%s] has been configured with @ContextConfiguration's 'locations' (or 'value') %s "
|
||||
+ "and 'classes' %s attributes. Only one declaration of resources "
|
||||
+ "is permitted per @ContextConfiguration annotation.", declaringClass.getName(),
|
||||
ObjectUtils.nullSafeToString(locations), ObjectUtils.nullSafeToString(classes));
|
||||
logger.error(msg);
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
|
||||
this.declaringClass = declaringClass;
|
||||
this.locations = locations;
|
||||
this.classes = classes;
|
||||
this.inheritLocations = inheritLocations;
|
||||
this.contextLoaderClass = contextLoaderClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Class class} that declared the
|
||||
* {@link ContextConfiguration @ContextConfiguration} annotation.
|
||||
* @return the declaring class; never <code>null</code>
|
||||
*/
|
||||
public Class<?> getDeclaringClass() {
|
||||
return declaringClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resource locations that were declared via
|
||||
* {@link ContextConfiguration @ContextConfiguration}.
|
||||
* <p>Note: this is a mutable property. The returned value may therefore
|
||||
* represent a <em>processed</em> value that does not match the original value
|
||||
* declared via {@link ContextConfiguration @ContextConfiguration}.
|
||||
* @return the resource locations; potentially <code>null</code> or <em>empty</em>
|
||||
* @see ContextConfiguration#value
|
||||
* @see ContextConfiguration#locations
|
||||
* @see #setLocations()
|
||||
*/
|
||||
public String[] getLocations() {
|
||||
return locations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the <em>processed</em> resource locations, effectively overriding the
|
||||
* original value declared via {@link ContextConfiguration @ContextConfiguration}.
|
||||
* @see #getLocations()
|
||||
*/
|
||||
public void setLocations(String[] locations) {
|
||||
this.locations = locations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration classes that were declared via
|
||||
* {@link ContextConfiguration @ContextConfiguration}.
|
||||
* <p>Note: this is a mutable property. The returned value may therefore
|
||||
* represent a <em>processed</em> value that does not match the original value
|
||||
* declared via {@link ContextConfiguration @ContextConfiguration}.
|
||||
* @return the configuration classes; potentially <code>null</code> or <em>empty</em>
|
||||
* @see ContextConfiguration#classes
|
||||
* @see #setClasses()
|
||||
*/
|
||||
public Class<?>[] getClasses() {
|
||||
return classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the <em>processed</em> configuration classes, effectively overriding the
|
||||
* original value declared via {@link ContextConfiguration @ContextConfiguration}.
|
||||
* @see #getClasses()
|
||||
*/
|
||||
public void setClasses(Class<?>[] classes) {
|
||||
this.classes = classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this {@code ContextConfigurationAttributes} instance has
|
||||
* path-based resource locations.
|
||||
* @return <code>true</code> if the {@link #getLocations() locations} array is not empty
|
||||
* @see #hasResources()
|
||||
* @see #hasClasses()
|
||||
*/
|
||||
public boolean hasLocations() {
|
||||
return !ObjectUtils.isEmpty(getLocations());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this {@code ContextConfigurationAttributes} instance has
|
||||
* class-based resources.
|
||||
* @return <code>true</code> if the {@link #getClasses() classes} array is not empty
|
||||
* @see #hasResources()
|
||||
* @see #hasLocations()
|
||||
*/
|
||||
public boolean hasClasses() {
|
||||
return !ObjectUtils.isEmpty(getClasses());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this {@code ContextConfigurationAttributes} instance has
|
||||
* either path-based resource locations or class-based resources.
|
||||
* @return <code>true</code> if either the {@link #getLocations() locations}
|
||||
* or the {@link #getClasses() classes} array is not empty
|
||||
* @see #hasLocations()
|
||||
* @see #hasClasses()
|
||||
*/
|
||||
public boolean hasResources() {
|
||||
return hasLocations() || hasClasses();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the <code>inheritLocations</code> flag that was declared via
|
||||
* {@link ContextConfiguration @ContextConfiguration}.
|
||||
* @return the <code>inheritLocations</code> flag
|
||||
* @see ContextConfiguration#inheritLocations
|
||||
*/
|
||||
public boolean isInheritLocations() {
|
||||
return inheritLocations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the <code>ContextLoader</code> class that was declared via
|
||||
* {@link ContextConfiguration @ContextConfiguration}.
|
||||
* @return the <code>ContextLoader</code> class
|
||||
* @see ContextConfiguration#loader
|
||||
*/
|
||||
public Class<? extends ContextLoader> getContextLoaderClass() {
|
||||
return contextLoaderClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a String representation of the context configuration attributes
|
||||
* and declaring class.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this)//
|
||||
.append("declaringClass", declaringClass.getName())//
|
||||
.append("locations", ObjectUtils.nullSafeToString(locations))//
|
||||
.append("classes", ObjectUtils.nullSafeToString(classes))//
|
||||
.append("inheritLocations", inheritLocations)//
|
||||
.append("contextLoaderClass", contextLoaderClass.getName())//
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
/**
|
||||
* Strategy interface for loading an {@link ApplicationContext application context}
|
||||
* for an integration test managed by the Spring TestContext Framework.
|
||||
*
|
||||
* <p><b>Note</b>: as of Spring 3.1, implement {@link SmartContextLoader} instead
|
||||
* of this interface in order to provide support for configuration classes and
|
||||
* active bean definition profiles.
|
||||
*
|
||||
* <p>Clients of a ContextLoader should call
|
||||
* {@link #processLocations(Class,String...) processLocations()} prior to
|
||||
* calling {@link #loadContext(String...) loadContext()} in case the
|
||||
* ContextLoader provides custom support for modifying or generating locations.
|
||||
* The results of {@link #processLocations(Class,String...) processLocations()}
|
||||
* should then be supplied to {@link #loadContext(String...) loadContext()}.
|
||||
*
|
||||
* <p>Concrete implementations must provide a <code>public</code> no-args
|
||||
* constructor.
|
||||
*
|
||||
* <p>Spring provides the following out-of-the-box implementations:
|
||||
* <ul>
|
||||
* <li>{@link org.springframework.test.context.support.GenericXmlContextLoader GenericXmlContextLoader}</li>
|
||||
* <li>{@link org.springframework.test.context.support.GenericPropertiesContextLoader GenericPropertiesContextLoader}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see SmartContextLoader
|
||||
* @see org.springframework.test.context.support.AnnotationConfigContextLoader AnnotationConfigContextLoader
|
||||
*/
|
||||
public interface ContextLoader {
|
||||
|
||||
/**
|
||||
* Processes application context resource locations for a specified class.
|
||||
* <p>Concrete implementations may choose to modify the supplied locations,
|
||||
* generate new locations, or simply return the supplied locations unchanged.
|
||||
* @param clazz the class with which the locations are associated: used to
|
||||
* determine how to process the supplied locations
|
||||
* @param locations the unmodified locations to use for loading the
|
||||
* application context (can be <code>null</code> or empty)
|
||||
* @return an array of application context resource locations
|
||||
*/
|
||||
String[] processLocations(Class<?> clazz, String... locations);
|
||||
|
||||
/**
|
||||
* Loads a new {@link ApplicationContext context} based on the supplied
|
||||
* <code>locations</code>, configures the context, and finally returns
|
||||
* the context in fully <em>refreshed</em> state.
|
||||
* <p>Configuration locations are generally considered to be classpath
|
||||
* resources by default.
|
||||
* <p>Concrete implementations should register annotation configuration
|
||||
* processors with bean factories of {@link ApplicationContext application
|
||||
* contexts} loaded by this ContextLoader. Beans will therefore automatically
|
||||
* be candidates for annotation-based dependency injection using
|
||||
* {@link org.springframework.beans.factory.annotation.Autowired @Autowired},
|
||||
* {@link javax.annotation.Resource @Resource}, and
|
||||
* {@link javax.inject.Inject @Inject}.
|
||||
* <p>Any ApplicationContext loaded by a ContextLoader <strong>must</strong>
|
||||
* register a JVM shutdown hook for itself. Unless the context gets closed
|
||||
* early, all context instances will be automatically closed on JVM
|
||||
* shutdown. This allows for freeing external resources held by beans within
|
||||
* the context, e.g. temporary files.
|
||||
* @param locations the resource locations to use to load the application context
|
||||
* @return a new application context
|
||||
* @throws Exception if context loading failed
|
||||
*/
|
||||
ApplicationContext loadContext(String... locations) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
import static org.springframework.beans.BeanUtils.instantiateClass;
|
||||
import static org.springframework.core.annotation.AnnotationUtils.findAnnotationDeclaringClass;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Utility methods for working with {@link ContextLoader ContextLoaders} and
|
||||
* {@link SmartContextLoader SmartContextLoaders} and resolving resource locations,
|
||||
* configuration classes, and active bean definition profiles.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 3.1
|
||||
* @see ContextLoader
|
||||
* @see SmartContextLoader
|
||||
* @see ContextConfiguration
|
||||
* @see ContextConfigurationAttributes
|
||||
* @see ActiveProfiles
|
||||
* @see MergedContextConfiguration
|
||||
*/
|
||||
abstract class ContextLoaderUtils {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ContextLoaderUtils.class);
|
||||
|
||||
private static final String DEFAULT_CONTEXT_LOADER_CLASS_NAME = "org.springframework.test.context.support.DelegatingSmartContextLoader";
|
||||
|
||||
|
||||
private ContextLoaderUtils() {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the {@link ContextLoader} {@link Class class} to use for the
|
||||
* supplied {@link Class testClass} and then instantiate and return that
|
||||
* {@code ContextLoader}.
|
||||
* <p>If the supplied <code>defaultContextLoaderClassName</code> is
|
||||
* <code>null</code> or <em>empty</em>, the <em>standard</em>
|
||||
* default context loader class name {@value #DEFAULT_CONTEXT_LOADER_CLASS_NAME}
|
||||
* will be used. For details on the class resolution process, see
|
||||
* {@link #resolveContextLoaderClass()}.
|
||||
* @param testClass the test class for which the {@code ContextLoader}
|
||||
* should be resolved (must not be <code>null</code>)
|
||||
* @param defaultContextLoaderClassName the name of the default
|
||||
* {@code ContextLoader} class to use (may be <code>null</code>)
|
||||
* @return the resolved {@code ContextLoader} for the supplied
|
||||
* <code>testClass</code> (never <code>null</code>)
|
||||
* @see #resolveContextLoaderClass()
|
||||
*/
|
||||
static ContextLoader resolveContextLoader(Class<?> testClass, String defaultContextLoaderClassName) {
|
||||
Assert.notNull(testClass, "Test class must not be null");
|
||||
|
||||
if (!StringUtils.hasText(defaultContextLoaderClassName)) {
|
||||
defaultContextLoaderClassName = DEFAULT_CONTEXT_LOADER_CLASS_NAME;
|
||||
}
|
||||
|
||||
Class<? extends ContextLoader> contextLoaderClass = resolveContextLoaderClass(testClass,
|
||||
defaultContextLoaderClassName);
|
||||
|
||||
return instantiateClass(contextLoaderClass, ContextLoader.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the {@link ContextLoader} {@link Class} to use for the supplied
|
||||
* {@link Class testClass}.
|
||||
* <ol>
|
||||
* <li>If the {@link ContextConfiguration#loader() loader} attribute of
|
||||
* {@link ContextConfiguration @ContextConfiguration} is configured
|
||||
* with an explicit class, that class will be returned.</li>
|
||||
* <li>If a <code>loader</code> class is not specified, the class hierarchy
|
||||
* will be traversed to find a parent class annotated with
|
||||
* {@code @ContextConfiguration}; go to step #1.</li>
|
||||
* <li>If no explicit <code>loader</code> class is found after traversing
|
||||
* the class hierarchy, an attempt will be made to load and return the class
|
||||
* with the supplied <code>defaultContextLoaderClassName</code>.</li>
|
||||
* </ol>
|
||||
* @param testClass the class for which to resolve the {@code ContextLoader}
|
||||
* class; must not be <code>null</code>
|
||||
* @param defaultContextLoaderClassName the name of the default
|
||||
* {@code ContextLoader} class to use; must not be <code>null</code> or empty
|
||||
* @return the {@code ContextLoader} class to use for the supplied test class
|
||||
* @throws IllegalArgumentException if {@code @ContextConfiguration} is not
|
||||
* <em>present</em> on the supplied test class
|
||||
* @throws IllegalStateException if the default {@code ContextLoader} class
|
||||
* could not be loaded
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static Class<? extends ContextLoader> resolveContextLoaderClass(Class<?> testClass,
|
||||
String defaultContextLoaderClassName) {
|
||||
Assert.notNull(testClass, "Class must not be null");
|
||||
Assert.hasText(defaultContextLoaderClassName, "Default ContextLoader class name must not be null or empty");
|
||||
|
||||
Class<ContextConfiguration> annotationType = ContextConfiguration.class;
|
||||
Class<?> declaringClass = findAnnotationDeclaringClass(annotationType, testClass);
|
||||
Assert.notNull(declaringClass, String.format(
|
||||
"Could not find an 'annotation declaring class' for annotation type [%s] and test class [%s]",
|
||||
annotationType, testClass));
|
||||
|
||||
while (declaringClass != null) {
|
||||
ContextConfiguration contextConfiguration = declaringClass.getAnnotation(annotationType);
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format(
|
||||
"Processing ContextLoader for @ContextConfiguration [%s] and declaring class [%s]",
|
||||
contextConfiguration, declaringClass));
|
||||
}
|
||||
|
||||
Class<? extends ContextLoader> contextLoaderClass = contextConfiguration.loader();
|
||||
if (!ContextLoader.class.equals(contextLoaderClass)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format(
|
||||
"Found explicit ContextLoader class [%s] for @ContextConfiguration [%s] and declaring class [%s]",
|
||||
contextLoaderClass, contextConfiguration, declaringClass));
|
||||
}
|
||||
return contextLoaderClass;
|
||||
}
|
||||
|
||||
declaringClass = findAnnotationDeclaringClass(annotationType, declaringClass.getSuperclass());
|
||||
}
|
||||
|
||||
try {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Using default ContextLoader class [%s] for test class [%s]",
|
||||
defaultContextLoaderClassName, testClass));
|
||||
}
|
||||
return (Class<? extends ContextLoader>) ContextLoaderUtils.class.getClassLoader().loadClass(
|
||||
defaultContextLoaderClassName);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalStateException("Could not load default ContextLoader class ["
|
||||
+ defaultContextLoaderClassName + "]. Specify @ContextConfiguration's 'loader' "
|
||||
+ "attribute or make the default loader class available.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the list of {@link ContextConfigurationAttributes configuration
|
||||
* attributes} for the supplied {@link Class class} and its superclasses.
|
||||
* <p>Note that the {@link ContextConfiguration#inheritLocations
|
||||
* inheritLocations} flag of {@link ContextConfiguration
|
||||
* @ContextConfiguration} will be taken into consideration.
|
||||
* Specifically, if the <code>inheritLocations</code> flag is set to
|
||||
* <code>true</code>, configuration attributes defined in the annotated
|
||||
* class will be appended to the configuration attributes defined in
|
||||
* superclasses.
|
||||
* @param clazz the class for which to resolve the configuration attributes (must
|
||||
* not be <code>null</code>)
|
||||
* @return the list of configuration attributes for the specified class,
|
||||
* including configuration attributes from superclasses if appropriate
|
||||
* (never <code>null</code>)
|
||||
* @throws IllegalArgumentException if the supplied class is <code>null</code> or
|
||||
* if {@code @ContextConfiguration} is not <em>present</em> on the supplied class
|
||||
*/
|
||||
static List<ContextConfigurationAttributes> resolveContextConfigurationAttributes(Class<?> clazz) {
|
||||
Assert.notNull(clazz, "Class must not be null");
|
||||
|
||||
final List<ContextConfigurationAttributes> attributesList = new ArrayList<ContextConfigurationAttributes>();
|
||||
|
||||
Class<ContextConfiguration> annotationType = ContextConfiguration.class;
|
||||
Class<?> declaringClass = findAnnotationDeclaringClass(annotationType, clazz);
|
||||
Assert.notNull(declaringClass, String.format(
|
||||
"Could not find an 'annotation declaring class' for annotation type [%s] and class [%s]", annotationType,
|
||||
clazz));
|
||||
|
||||
while (declaringClass != null) {
|
||||
ContextConfiguration contextConfiguration = declaringClass.getAnnotation(annotationType);
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Retrieved @ContextConfiguration [%s] for declaring class [%s].",
|
||||
contextConfiguration, declaringClass));
|
||||
}
|
||||
|
||||
ContextConfigurationAttributes attributes = new ContextConfigurationAttributes(declaringClass,
|
||||
contextConfiguration);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Resolved context configuration attributes: " + attributes);
|
||||
}
|
||||
|
||||
attributesList.add(0, attributes);
|
||||
|
||||
declaringClass = contextConfiguration.inheritLocations() ? findAnnotationDeclaringClass(annotationType,
|
||||
declaringClass.getSuperclass()) : null;
|
||||
}
|
||||
|
||||
return attributesList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve <em>active bean definition profiles</em> for the supplied {@link Class}.
|
||||
* <p>Note that the {@link ActiveProfiles#inheritProfiles inheritProfiles}
|
||||
* flag of {@link ActiveProfiles @ActiveProfiles} will be taken into
|
||||
* consideration. Specifically, if the <code>inheritProfiles</code> flag is
|
||||
* set to <code>true</code>, profiles defined in the annotated class will be
|
||||
* merged with those defined in superclasses.
|
||||
* @param clazz the class for which to resolve the active profiles (must
|
||||
* not be <code>null</code>)
|
||||
* @return the set of active profiles for the specified class, including
|
||||
* active profiles from superclasses if appropriate (never <code>null</code>)
|
||||
* @see org.springframework.test.context.ActiveProfiles
|
||||
* @see org.springframework.context.annotation.Profile
|
||||
*/
|
||||
static String[] resolveActiveProfiles(Class<?> clazz) {
|
||||
Assert.notNull(clazz, "Class must not be null");
|
||||
|
||||
Class<ActiveProfiles> annotationType = ActiveProfiles.class;
|
||||
Class<?> declaringClass = findAnnotationDeclaringClass(annotationType, clazz);
|
||||
|
||||
if (declaringClass == null && logger.isDebugEnabled()) {
|
||||
logger.debug(String.format(
|
||||
"Could not find an 'annotation declaring class' for annotation type [%s] and class [%s]",
|
||||
annotationType, clazz));
|
||||
}
|
||||
|
||||
final Set<String> activeProfiles = new HashSet<String>();
|
||||
|
||||
while (declaringClass != null) {
|
||||
ActiveProfiles annotation = declaringClass.getAnnotation(annotationType);
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Retrieved @ActiveProfiles [%s] for declaring class [%s].", annotation,
|
||||
declaringClass));
|
||||
}
|
||||
|
||||
String[] profiles = annotation.profiles();
|
||||
String[] valueProfiles = annotation.value();
|
||||
|
||||
if (!ObjectUtils.isEmpty(valueProfiles) && !ObjectUtils.isEmpty(profiles)) {
|
||||
String msg = String.format("Test class [%s] has been configured with @ActiveProfiles' 'value' [%s] "
|
||||
+ "and 'profiles' [%s] attributes. Only one declaration of active bean "
|
||||
+ "definition profiles is permitted per @ActiveProfiles annotation.", declaringClass,
|
||||
ObjectUtils.nullSafeToString(valueProfiles), ObjectUtils.nullSafeToString(profiles));
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
else if (!ObjectUtils.isEmpty(valueProfiles)) {
|
||||
profiles = valueProfiles;
|
||||
}
|
||||
|
||||
for (String profile : profiles) {
|
||||
if (StringUtils.hasText(profile)) {
|
||||
activeProfiles.add(profile.trim());
|
||||
}
|
||||
}
|
||||
|
||||
declaringClass = annotation.inheritProfiles() ? findAnnotationDeclaringClass(annotationType,
|
||||
declaringClass.getSuperclass()) : null;
|
||||
}
|
||||
|
||||
return StringUtils.toStringArray(activeProfiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the {@link MergedContextConfiguration merged context configuration}
|
||||
* for the supplied {@link Class testClass} and
|
||||
* <code>defaultContextLoaderClassName</code>.
|
||||
* @param testClass the test class for which the {@code MergedContextConfiguration}
|
||||
* should be built (must not be <code>null</code>)
|
||||
* @param defaultContextLoaderClassName the name of the default
|
||||
* {@code ContextLoader} class to use (may be <code>null</code>)
|
||||
* @return the merged context configuration
|
||||
* @see #resolveContextLoader()
|
||||
* @see #resolveContextConfigurationAttributes()
|
||||
* @see SmartContextLoader#processContextConfiguration()
|
||||
* @see ContextLoader#processLocations()
|
||||
* @see #resolveActiveProfiles()
|
||||
* @see MergedContextConfiguration
|
||||
*/
|
||||
static MergedContextConfiguration buildMergedContextConfiguration(Class<?> testClass,
|
||||
String defaultContextLoaderClassName) {
|
||||
|
||||
final ContextLoader contextLoader = resolveContextLoader(testClass, defaultContextLoaderClassName);
|
||||
final List<ContextConfigurationAttributes> configAttributesList = resolveContextConfigurationAttributes(testClass);
|
||||
final List<String> locationsList = new ArrayList<String>();
|
||||
final List<Class<?>> classesList = new ArrayList<Class<?>>();
|
||||
|
||||
for (ContextConfigurationAttributes configAttributes : configAttributesList) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format(
|
||||
"Processing locations and classes for context configuration attributes [%s]", configAttributes));
|
||||
}
|
||||
|
||||
if (contextLoader instanceof SmartContextLoader) {
|
||||
SmartContextLoader smartContextLoader = (SmartContextLoader) contextLoader;
|
||||
smartContextLoader.processContextConfiguration(configAttributes);
|
||||
locationsList.addAll(Arrays.asList(configAttributes.getLocations()));
|
||||
classesList.addAll(Arrays.asList(configAttributes.getClasses()));
|
||||
}
|
||||
else {
|
||||
String[] processedLocations = contextLoader.processLocations(configAttributes.getDeclaringClass(),
|
||||
configAttributes.getLocations());
|
||||
locationsList.addAll(Arrays.asList(processedLocations));
|
||||
// Legacy ContextLoaders don't know how to process classes
|
||||
}
|
||||
}
|
||||
|
||||
String[] locations = StringUtils.toStringArray(locationsList);
|
||||
Class<?>[] classes = ClassUtils.toClassArray(classesList);
|
||||
String[] activeProfiles = resolveActiveProfiles(testClass);
|
||||
|
||||
return new MergedContextConfiguration(testClass, locations, classes, activeProfiles, contextLoader);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@code MergedContextConfiguration} encapsulates the <em>merged</em>
|
||||
* context configuration declared on a test class and all of its superclasses
|
||||
* via {@link ContextConfiguration @ContextConfiguration} and
|
||||
* {@link ActiveProfiles @ActiveProfiles}.
|
||||
*
|
||||
* <p>Merged resource locations, configuration classes, and active profiles
|
||||
* represent all declared values in the test class hierarchy taking into
|
||||
* consideration the semantics of the
|
||||
* {@link ContextConfiguration#inheritLocations inheritLocations} and
|
||||
* {@link ActiveProfiles#inheritProfiles inheritProfiles} flags in
|
||||
* {@code @ContextConfiguration} and {@code @ActiveProfiles}, respectively.
|
||||
*
|
||||
* <p>A {@link SmartContextLoader} uses {@code MergedContextConfiguration}
|
||||
* to load an {@link org.springframework.context.ApplicationContext ApplicationContext}.
|
||||
*
|
||||
* <p>{@code MergedContextConfiguration} is also used by the {@link TestContext}
|
||||
* as the context cache key for caching an
|
||||
* {@link org.springframework.context.ApplicationContext ApplicationContext}
|
||||
* that was loaded using properties of this {@code MergedContextConfiguration}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 3.1
|
||||
* @see ContextConfiguration
|
||||
* @see ActiveProfiles
|
||||
* @see ContextConfigurationAttributes
|
||||
* @see SmartContextLoader#loadContext(MergedContextConfiguration)
|
||||
*/
|
||||
public class MergedContextConfiguration implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -3290560718464957422L;
|
||||
|
||||
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
|
||||
|
||||
private final Class<?> testClass;
|
||||
private final String[] locations;
|
||||
private final Class<?>[] classes;
|
||||
private final String[] activeProfiles;
|
||||
private final ContextLoader contextLoader;
|
||||
|
||||
|
||||
private static String[] processLocations(String[] locations) {
|
||||
return locations == null ? EMPTY_STRING_ARRAY : locations;
|
||||
}
|
||||
|
||||
private static Class<?>[] processClasses(Class<?>[] classes) {
|
||||
return classes == null ? EMPTY_CLASS_ARRAY : classes;
|
||||
}
|
||||
|
||||
private static String[] processActiveProfiles(String[] activeProfiles) {
|
||||
if (activeProfiles == null) {
|
||||
return EMPTY_STRING_ARRAY;
|
||||
}
|
||||
|
||||
// Active profiles must be unique and sorted in order to support proper
|
||||
// cache key generation. Specifically, profile sets {foo,bar} and
|
||||
// {bar,foo} must both result in the same array (e.g., [bar,foo]).
|
||||
SortedSet<String> sortedProfilesSet = new TreeSet<String>(Arrays.asList(activeProfiles));
|
||||
return StringUtils.toStringArray(sortedProfilesSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a null-safe {@link String} representation of the supplied
|
||||
* {@link ContextLoader} based solely on the fully qualified name of the
|
||||
* loader or "null" if the supplied loaded is <code>null</code>.
|
||||
*/
|
||||
private static String nullSafeToString(ContextLoader contextLoader) {
|
||||
return contextLoader == null ? "null" : contextLoader.getClass().getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code MergedContextConfiguration} instance for the
|
||||
* supplied {@link Class test class}, resource locations, configuration
|
||||
* classes, active profiles, and {@link ContextLoader}.
|
||||
* <p>If a <code>null</code> value is supplied for <code>locations</code>,
|
||||
* <code>classes</code>, or <code>activeProfiles</code> an empty array will
|
||||
* be stored instead. Furthermore, active profiles will be sorted, and duplicate
|
||||
* profiles will be removed.
|
||||
* @param testClass the test class for which the configuration was merged
|
||||
* @param locations the merged resource locations
|
||||
* @param classes the merged configuration classes
|
||||
* @param activeProfiles the merged active bean definition profiles
|
||||
* @param contextLoader the resolved <code>ContextLoader</code>
|
||||
*/
|
||||
public MergedContextConfiguration(Class<?> testClass, String[] locations, Class<?>[] classes,
|
||||
String[] activeProfiles, ContextLoader contextLoader) {
|
||||
this.testClass = testClass;
|
||||
this.locations = processLocations(locations);
|
||||
this.classes = processClasses(classes);
|
||||
this.activeProfiles = processActiveProfiles(activeProfiles);
|
||||
this.contextLoader = contextLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Class test class} associated with this
|
||||
* {@code MergedContextConfiguration}.
|
||||
*/
|
||||
public Class<?> getTestClass() {
|
||||
return testClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the merged resource locations for the
|
||||
* {@link #getTestClass() test class}.
|
||||
*/
|
||||
public String[] getLocations() {
|
||||
return locations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the merged configuration classes for the
|
||||
* {@link #getTestClass() test class}.
|
||||
*/
|
||||
public Class<?>[] getClasses() {
|
||||
return classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the merged active bean definition profiles for the
|
||||
* {@link #getTestClass() test class}.
|
||||
*/
|
||||
public String[] getActiveProfiles() {
|
||||
return activeProfiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resolved {@link ContextLoader} for the
|
||||
* {@link #getTestClass() test class}.
|
||||
*/
|
||||
public ContextLoader getContextLoader() {
|
||||
return contextLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a unique hash code for all properties of this
|
||||
* {@code MergedContextConfiguration} excluding the
|
||||
* {@link #getTestClass() test class}.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + Arrays.hashCode(locations);
|
||||
result = prime * result + Arrays.hashCode(classes);
|
||||
result = prime * result + Arrays.hashCode(activeProfiles);
|
||||
result = prime * result + nullSafeToString(contextLoader).hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the supplied object is equal to this {@code MergedContextConfiguration}
|
||||
* instance by comparing both object's {@link #getLocations() locations},
|
||||
* {@link #getClasses() configuration classes}, {@link #getActiveProfiles()
|
||||
* active profiles}, and the fully qualified names of their
|
||||
* {@link #getContextLoader() ContextLoaders}.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof MergedContextConfiguration)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final MergedContextConfiguration that = (MergedContextConfiguration) obj;
|
||||
|
||||
if (!Arrays.equals(this.locations, that.locations)) {
|
||||
return false;
|
||||
}
|
||||
if (!Arrays.equals(this.classes, that.classes)) {
|
||||
return false;
|
||||
}
|
||||
if (!Arrays.equals(this.activeProfiles, that.activeProfiles)) {
|
||||
return false;
|
||||
}
|
||||
if (!nullSafeToString(this.contextLoader).equals(nullSafeToString(that.contextLoader))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a String representation of the {@link #getTestClass() test class},
|
||||
* {@link #getLocations() locations}, {@link #getClasses() configuration classes},
|
||||
* {@link #getActiveProfiles() active profiles}, and the name of the
|
||||
* {@link #getContextLoader() ContextLoader}.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this)//
|
||||
.append("testClass", testClass)//
|
||||
.append("locations", ObjectUtils.nullSafeToString(locations))//
|
||||
.append("classes", ObjectUtils.nullSafeToString(classes))//
|
||||
.append("activeProfiles", ObjectUtils.nullSafeToString(activeProfiles))//
|
||||
.append("contextLoader", nullSafeToString(contextLoader))//
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
/**
|
||||
* Strategy interface for loading an {@link ApplicationContext application context}
|
||||
* for an integration test managed by the Spring TestContext Framework.
|
||||
*
|
||||
* <p>The {@code SmartContextLoader} SPI supersedes the {@link ContextLoader} SPI
|
||||
* introduced in Spring 2.5: a {@code SmartContextLoader} can choose to process
|
||||
* either resource locations or configuration classes. Furthermore, a
|
||||
* {@code SmartContextLoader} can set active bean definition profiles in the
|
||||
* context that it loads (see {@link MergedContextConfiguration#getActiveProfiles()}
|
||||
* and {@link #loadContext(MergedContextConfiguration)}).
|
||||
*
|
||||
* <p>Clients of a {@code SmartContextLoader} should call
|
||||
* {@link #processContextConfiguration(ContextConfigurationAttributes)
|
||||
* processContextConfiguration()} prior to calling
|
||||
* {@link #loadContext(MergedContextConfiguration) loadContext()}. This gives a
|
||||
* {@code SmartContextLoader} the opportunity to provide custom support for
|
||||
* modifying resource locations or detecting default resource locations or
|
||||
* default configuration classes. The results of
|
||||
* {@link #processContextConfiguration(ContextConfigurationAttributes)
|
||||
* processContextConfiguration()} should be merged for all classes in the
|
||||
* hierarchy of the root test class and then supplied to
|
||||
* {@link #loadContext(MergedContextConfiguration) loadContext()}.
|
||||
*
|
||||
* <p>Even though {@code SmartContextLoader} extends {@code ContextLoader},
|
||||
* clients should favor {@code SmartContextLoader}-specific methods over those
|
||||
* defined in {@code ContextLoader}, particularly because a
|
||||
* {@code SmartContextLoader} may choose not to support methods defined in
|
||||
* the {@code ContextLoader} SPI.
|
||||
*
|
||||
* <p>Concrete implementations must provide a <code>public</code> no-args constructor.
|
||||
*
|
||||
* <p>Spring provides the following out-of-the-box implementations:
|
||||
* <ul>
|
||||
* <li>{@link org.springframework.test.context.support.DelegatingSmartContextLoader DelegatingSmartContextLoader}</li>
|
||||
* <li>{@link org.springframework.test.context.support.AnnotationConfigContextLoader AnnotationConfigContextLoader}</li>
|
||||
* <li>{@link org.springframework.test.context.support.GenericXmlContextLoader GenericXmlContextLoader}</li>
|
||||
* <li>{@link org.springframework.test.context.support.GenericPropertiesContextLoader GenericPropertiesContextLoader}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 3.1
|
||||
* @see ContextConfiguration
|
||||
* @see ActiveProfiles
|
||||
* @see ContextConfigurationAttributes
|
||||
* @see MergedContextConfiguration
|
||||
*/
|
||||
public interface SmartContextLoader extends ContextLoader {
|
||||
|
||||
/**
|
||||
* Processes the {@link ContextConfigurationAttributes} for a given test class.
|
||||
* <p>Concrete implementations may choose to <em>modify</em> the <code>locations</code>
|
||||
* or <code>classes</code> in the supplied {@link ContextConfigurationAttributes},
|
||||
* <em>generate</em> default configuration locations, or <em>detect</em>
|
||||
* default configuration classes if the supplied values are <code>null</code>
|
||||
* or empty.
|
||||
* <p><b>Note</b>: in contrast to a standard {@code ContextLoader}, a
|
||||
* {@code SmartContextLoader} <b>must</b> <em>preemptively</em> verify that
|
||||
* a generated or detected default actually exists before setting the corresponding
|
||||
* <code>locations</code> or <code>classes</code> property in the supplied
|
||||
* {@link ContextConfigurationAttributes}. Consequently, leaving the
|
||||
* <code>locations</code> or <code>classes</code> property empty signals that
|
||||
* this {@code SmartContextLoader} was not able to generate or detect defaults.
|
||||
* @param configAttributes the context configuration attributes to process
|
||||
*/
|
||||
void processContextConfiguration(ContextConfigurationAttributes configAttributes);
|
||||
|
||||
/**
|
||||
* Loads a new {@link ApplicationContext context} based on the supplied
|
||||
* {@link MergedContextConfiguration merged context configuration},
|
||||
* configures the context, and finally returns the context in a fully
|
||||
* <em>refreshed</em> state.
|
||||
* <p>Concrete implementations should register annotation configuration
|
||||
* processors with bean factories of
|
||||
* {@link ApplicationContext application contexts} loaded by this
|
||||
* {@code SmartContextLoader}. Beans will therefore automatically be
|
||||
* candidates for annotation-based dependency injection using
|
||||
* {@link org.springframework.beans.factory.annotation.Autowired @Autowired},
|
||||
* {@link javax.annotation.Resource @Resource}, and
|
||||
* {@link javax.inject.Inject @Inject}. In addition, concrete implementations
|
||||
* should set the active bean definition profiles in the context's
|
||||
* {@link org.springframework.core.env.Environment Environment}.
|
||||
* <p>Any <code>ApplicationContext</code> loaded by a
|
||||
* {@code SmartContextLoader} <strong>must</strong> register a JVM
|
||||
* shutdown hook for itself. Unless the context gets closed early, all context
|
||||
* instances will be automatically closed on JVM shutdown. This allows for
|
||||
* freeing of external resources held by beans within the context (e.g.,
|
||||
* temporary files).
|
||||
* @param mergedConfig the merged context configuration to use to load the
|
||||
* application context
|
||||
* @return a new application context
|
||||
* @throws Exception if context loading failed
|
||||
* @see #processContextConfiguration(ContextConfigurationAttributes)
|
||||
* @see org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors()
|
||||
* @see org.springframework.test.context.MergedContextConfiguration#getActiveProfiles()
|
||||
* @see org.springframework.context.ConfigurableApplicationContext#getEnvironment()
|
||||
*/
|
||||
ApplicationContext loadContext(MergedContextConfiguration mergedConfig) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.AttributeAccessorSupport;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* <code>TestContext</code> encapsulates the context in which a test is executed,
|
||||
* agnostic of the actual testing framework in use.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
*/
|
||||
public class TestContext extends AttributeAccessorSupport {
|
||||
|
||||
private static final long serialVersionUID = -5827157174866681233L;
|
||||
|
||||
private static final Log logger = LogFactory.getLog(TestContext.class);
|
||||
|
||||
private final ContextCache contextCache;
|
||||
|
||||
private final MergedContextConfiguration mergedContextConfiguration;
|
||||
|
||||
private final Class<?> testClass;
|
||||
|
||||
private Object testInstance;
|
||||
|
||||
private Method testMethod;
|
||||
|
||||
private Throwable testException;
|
||||
|
||||
|
||||
/**
|
||||
* Delegates to {@link #TestContext(Class, ContextCache, String)} with a
|
||||
* value of <code>null</code> for the default {@code ContextLoader} class name.
|
||||
*/
|
||||
TestContext(Class<?> testClass, ContextCache contextCache) {
|
||||
this(testClass, contextCache, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new test context for the supplied {@link Class test class}
|
||||
* and {@link ContextCache context cache} and parse the corresponding
|
||||
* {@link ContextConfiguration @ContextConfiguration} annotation, if
|
||||
* present.
|
||||
* <p>If the supplied class name for the default {@code ContextLoader}
|
||||
* is <code>null</code> or <em>empty</em> and no concrete {@code ContextLoader}
|
||||
* class is explicitly supplied via the {@code @ContextConfiguration}
|
||||
* annotation, a
|
||||
* {@link org.springframework.test.context.support.DelegatingSmartContextLoader
|
||||
* DelegatingSmartContextLoader} will be used instead.
|
||||
* @param testClass the test class for which the test context should be
|
||||
* constructed (must not be <code>null</code>)
|
||||
* @param contextCache the context cache from which the constructed test
|
||||
* context should retrieve application contexts (must not be
|
||||
* <code>null</code>)
|
||||
* @param defaultContextLoaderClassName the name of the default
|
||||
* {@code ContextLoader} class to use (may be <code>null</code>)
|
||||
*/
|
||||
TestContext(Class<?> testClass, ContextCache contextCache, String defaultContextLoaderClassName) {
|
||||
Assert.notNull(testClass, "Test class must not be null");
|
||||
Assert.notNull(contextCache, "ContextCache must not be null");
|
||||
|
||||
MergedContextConfiguration mergedContextConfiguration;
|
||||
ContextConfiguration contextConfiguration = testClass.getAnnotation(ContextConfiguration.class);
|
||||
|
||||
if (contextConfiguration == null) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(String.format("@ContextConfiguration not found for class [%s]", testClass));
|
||||
}
|
||||
mergedContextConfiguration = new MergedContextConfiguration(testClass, null, null, null, null);
|
||||
}
|
||||
else {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Retrieved @ContextConfiguration [%s] for class [%s]", contextConfiguration,
|
||||
testClass));
|
||||
}
|
||||
mergedContextConfiguration = ContextLoaderUtils.buildMergedContextConfiguration(testClass,
|
||||
defaultContextLoaderClassName);
|
||||
}
|
||||
|
||||
this.contextCache = contextCache;
|
||||
this.mergedContextConfiguration = mergedContextConfiguration;
|
||||
this.testClass = testClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an <code>ApplicationContext</code> for this test context using the
|
||||
* configured {@code ContextLoader} and merged context configuration. Supports
|
||||
* both the {@link SmartContextLoader} and {@link ContextLoader} SPIs.
|
||||
* @throws Exception if an error occurs while loading the application context
|
||||
*/
|
||||
private ApplicationContext loadApplicationContext() throws Exception {
|
||||
ContextLoader contextLoader = mergedContextConfiguration.getContextLoader();
|
||||
Assert.notNull(contextLoader, "Can not load an ApplicationContext with a NULL 'contextLoader'. "
|
||||
+ "Consider annotating your test class with @ContextConfiguration.");
|
||||
|
||||
ApplicationContext applicationContext;
|
||||
|
||||
if (contextLoader instanceof SmartContextLoader) {
|
||||
SmartContextLoader smartContextLoader = (SmartContextLoader) contextLoader;
|
||||
applicationContext = smartContextLoader.loadContext(mergedContextConfiguration);
|
||||
}
|
||||
else {
|
||||
String[] locations = mergedContextConfiguration.getLocations();
|
||||
Assert.notNull(locations, "Can not load an ApplicationContext with a NULL 'locations' array. "
|
||||
+ "Consider annotating your test class with @ContextConfiguration.");
|
||||
applicationContext = contextLoader.loadContext(locations);
|
||||
}
|
||||
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link ApplicationContext application context} for this test
|
||||
* context, possibly cached.
|
||||
* @return the application context
|
||||
* @throws IllegalStateException if an error occurs while retrieving the
|
||||
* application context
|
||||
*/
|
||||
public ApplicationContext getApplicationContext() {
|
||||
synchronized (contextCache) {
|
||||
ApplicationContext context = contextCache.get(mergedContextConfiguration);
|
||||
if (context == null) {
|
||||
try {
|
||||
context = loadApplicationContext();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format(
|
||||
"Storing ApplicationContext for test class [%s] in cache under key [%s].", testClass,
|
||||
mergedContextConfiguration));
|
||||
}
|
||||
contextCache.put(mergedContextConfiguration, context);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Failed to load ApplicationContext", ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format(
|
||||
"Retrieved ApplicationContext for test class [%s] from cache with key [%s].", testClass,
|
||||
mergedContextConfiguration));
|
||||
}
|
||||
}
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Class test class} for this test context.
|
||||
* @return the test class (never <code>null</code>)
|
||||
*/
|
||||
public final Class<?> getTestClass() {
|
||||
return testClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current {@link Object test instance} for this test context.
|
||||
* <p>Note: this is a mutable property.
|
||||
* @return the current test instance (may be <code>null</code>)
|
||||
* @see #updateState(Object,Method,Throwable)
|
||||
*/
|
||||
public final Object getTestInstance() {
|
||||
return testInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current {@link Method test method} for this test context.
|
||||
* <p>Note: this is a mutable property.
|
||||
* @return the current test method (may be <code>null</code>)
|
||||
* @see #updateState(Object, Method, Throwable)
|
||||
*/
|
||||
public final Method getTestMethod() {
|
||||
return testMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Throwable exception} that was thrown during execution of
|
||||
* the {@link #getTestMethod() test method}.
|
||||
* <p>Note: this is a mutable property.
|
||||
* @return the exception that was thrown, or <code>null</code> if no
|
||||
* exception was thrown
|
||||
* @see #updateState(Object, Method, Throwable)
|
||||
*/
|
||||
public final Throwable getTestException() {
|
||||
return testException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method to signal that the {@link ApplicationContext application
|
||||
* context} associated with this test context is <em>dirty</em> and should
|
||||
* be reloaded. Do this if a test has modified the context (for example, by
|
||||
* replacing a bean definition).
|
||||
*/
|
||||
public void markApplicationContextDirty() {
|
||||
synchronized (contextCache) {
|
||||
contextCache.setDirty(mergedContextConfiguration);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update this test context to reflect the state of the currently executing
|
||||
* test.
|
||||
* @param testInstance the current test instance (may be <code>null</code>)
|
||||
* @param testMethod the current test method (may be <code>null</code>)
|
||||
* @param testException the exception that was thrown in the test method, or
|
||||
* <code>null</code> if no exception was thrown
|
||||
*/
|
||||
void updateState(Object testInstance, Method testMethod, Throwable testException) {
|
||||
this.testInstance = testInstance;
|
||||
this.testMethod = testMethod;
|
||||
this.testException = testException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a String representation of this test context's state.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this)//
|
||||
.append("testClass", testClass)//
|
||||
.append("testInstance", testInstance)//
|
||||
.append("testMethod", testMethod)//
|
||||
.append("testException", testException)//
|
||||
.append("mergedContextConfiguration", mergedContextConfiguration)//
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,463 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* <code>TestContextManager</code> is the main entry point into the
|
||||
* <em>Spring TestContext Framework</em>, which provides support for loading and
|
||||
* accessing {@link ApplicationContext application contexts}, dependency
|
||||
* injection of test instances,
|
||||
* {@link org.springframework.transaction.annotation.Transactional
|
||||
* transactional} execution of test methods, etc.
|
||||
* </p>
|
||||
* <p>
|
||||
* Specifically, a <code>TestContextManager</code> is responsible for managing a
|
||||
* single {@link TestContext} and signaling events to all registered
|
||||
* {@link TestExecutionListener TestExecutionListeners} at well defined test
|
||||
* execution points:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>{@link #beforeTestClass() before test class execution}: prior to any
|
||||
* <em>before class methods</em> of a particular testing framework (e.g., JUnit
|
||||
* 4's {@link org.junit.BeforeClass @BeforeClass})</li>
|
||||
* <li>{@link #prepareTestInstance(Object) test instance preparation}:
|
||||
* immediately following instantiation of the test instance</li>
|
||||
* <li>{@link #beforeTestMethod(Object,Method) before test method execution}:
|
||||
* prior to any <em>before methods</em> of a particular testing framework (e.g.,
|
||||
* JUnit 4's {@link org.junit.Before @Before})</li>
|
||||
* <li>{@link #afterTestMethod(Object,Method,Throwable) after test method
|
||||
* execution}: after any <em>after methods</em> of a particular testing
|
||||
* framework (e.g., JUnit 4's {@link org.junit.After @After})</li>
|
||||
* <li>{@link #afterTestClass() after test class execution}: after any
|
||||
* <em>after class methods</em> of a particular testing framework (e.g., JUnit
|
||||
* 4's {@link org.junit.AfterClass @AfterClass})</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see TestContext
|
||||
* @see TestExecutionListeners
|
||||
* @see ContextConfiguration
|
||||
* @see org.springframework.test.context.transaction.TransactionConfiguration
|
||||
*/
|
||||
public class TestContextManager {
|
||||
|
||||
private static final String[] DEFAULT_TEST_EXECUTION_LISTENER_CLASS_NAMES = new String[] {
|
||||
"org.springframework.test.context.support.DependencyInjectionTestExecutionListener",
|
||||
"org.springframework.test.context.support.DirtiesContextTestExecutionListener",
|
||||
"org.springframework.test.context.transaction.TransactionalTestExecutionListener" };
|
||||
|
||||
private static final Log logger = LogFactory.getLog(TestContextManager.class);
|
||||
|
||||
/**
|
||||
* Cache of Spring application contexts. This needs to be static, as tests
|
||||
* may be destroyed and recreated between running individual test methods,
|
||||
* for example with JUnit.
|
||||
*/
|
||||
static final ContextCache contextCache = new ContextCache();
|
||||
|
||||
private final TestContext testContext;
|
||||
|
||||
private final List<TestExecutionListener> testExecutionListeners = new ArrayList<TestExecutionListener>();
|
||||
|
||||
|
||||
/**
|
||||
* Delegates to {@link #TestContextManager(Class, String)} with a value of
|
||||
* <code>null</code> for the default <code>ContextLoader</code> class name.
|
||||
*/
|
||||
public TestContextManager(Class<?> testClass) {
|
||||
this(testClass, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>TestContextManager</code> for the specified {@link Class test class}
|
||||
* and automatically {@link #registerTestExecutionListeners registers} the
|
||||
* {@link TestExecutionListener TestExecutionListeners} configured for the test class
|
||||
* via the {@link TestExecutionListeners @TestExecutionListeners} annotation.
|
||||
* @param testClass the test class to be managed
|
||||
* @param defaultContextLoaderClassName the name of the default
|
||||
* <code>ContextLoader</code> class to use (may be <code>null</code>)
|
||||
* @see #registerTestExecutionListeners(TestExecutionListener...)
|
||||
* @see #retrieveTestExecutionListeners(Class)
|
||||
*/
|
||||
public TestContextManager(Class<?> testClass, String defaultContextLoaderClassName) {
|
||||
this.testContext = new TestContext(testClass, contextCache, defaultContextLoaderClassName);
|
||||
registerTestExecutionListeners(retrieveTestExecutionListeners(testClass));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link TestContext} managed by this
|
||||
* <code>TestContextManager</code>.
|
||||
*/
|
||||
protected final TestContext getTestContext() {
|
||||
return this.testContext;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register the supplied {@link TestExecutionListener TestExecutionListeners}
|
||||
* by appending them to the set of listeners used by this <code>TestContextManager</code>.
|
||||
*/
|
||||
public void registerTestExecutionListeners(TestExecutionListener... testExecutionListeners) {
|
||||
for (TestExecutionListener listener : testExecutionListeners) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Registering TestExecutionListener: " + listener);
|
||||
}
|
||||
this.testExecutionListeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current {@link TestExecutionListener TestExecutionListeners}
|
||||
* registered for this <code>TestContextManager</code>.
|
||||
* <p>Allows for modifications, e.g. adding a listener to the beginning of the list.
|
||||
* However, make sure to keep the list stable while actually executing tests.
|
||||
*/
|
||||
public final List<TestExecutionListener> getTestExecutionListeners() {
|
||||
return this.testExecutionListeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a copy of the {@link TestExecutionListener TestExecutionListeners}
|
||||
* registered for this <code>TestContextManager</code> in reverse order.
|
||||
*/
|
||||
private List<TestExecutionListener> getReversedTestExecutionListeners() {
|
||||
List<TestExecutionListener> listenersReversed =
|
||||
new ArrayList<TestExecutionListener>(getTestExecutionListeners());
|
||||
Collections.reverse(listenersReversed);
|
||||
return listenersReversed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an array of newly instantiated {@link TestExecutionListener TestExecutionListeners}
|
||||
* for the specified {@link Class class}. If {@link TestExecutionListeners @TestExecutionListeners}
|
||||
* is not <em>present</em> on the supplied class, the default listeners will be returned.
|
||||
* <p>Note that the {@link TestExecutionListeners#inheritListeners() inheritListeners} flag of
|
||||
* {@link TestExecutionListeners @TestExecutionListeners} will be taken into consideration.
|
||||
* Specifically, if the <code>inheritListeners</code> flag is set to <code>true</code>, listeners
|
||||
* defined in the annotated class will be appended to the listeners defined in superclasses.
|
||||
* @param clazz the test class for which the listeners should be retrieved
|
||||
* @return an array of TestExecutionListeners for the specified class
|
||||
*/
|
||||
private TestExecutionListener[] retrieveTestExecutionListeners(Class<?> clazz) {
|
||||
Assert.notNull(clazz, "Class must not be null");
|
||||
Class<TestExecutionListeners> annotationType = TestExecutionListeners.class;
|
||||
List<Class<? extends TestExecutionListener>> classesList = new ArrayList<Class<? extends TestExecutionListener>>();
|
||||
Class<?> declaringClass = AnnotationUtils.findAnnotationDeclaringClass(annotationType, clazz);
|
||||
boolean defaultListeners = false;
|
||||
|
||||
// Use defaults?
|
||||
if (declaringClass == null) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("@TestExecutionListeners is not present for class [" + clazz + "]: using defaults.");
|
||||
}
|
||||
classesList.addAll(getDefaultTestExecutionListenerClasses());
|
||||
defaultListeners = true;
|
||||
}
|
||||
else {
|
||||
// Traverse the class hierarchy...
|
||||
while (declaringClass != null) {
|
||||
TestExecutionListeners testExecutionListeners = declaringClass.getAnnotation(annotationType);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Retrieved @TestExecutionListeners [" + testExecutionListeners
|
||||
+ "] for declaring class [" + declaringClass + "].");
|
||||
}
|
||||
|
||||
Class<? extends TestExecutionListener>[] valueListenerClasses = testExecutionListeners.value();
|
||||
Class<? extends TestExecutionListener>[] listenerClasses = testExecutionListeners.listeners();
|
||||
if (!ObjectUtils.isEmpty(valueListenerClasses) && !ObjectUtils.isEmpty(listenerClasses)) {
|
||||
String msg = String.format(
|
||||
"Test class [%s] has been configured with @TestExecutionListeners' 'value' [%s] " +
|
||||
"and 'listeners' [%s] attributes. Use one or the other, but not both.",
|
||||
declaringClass, ObjectUtils.nullSafeToString(valueListenerClasses),
|
||||
ObjectUtils.nullSafeToString(listenerClasses));
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
else if (!ObjectUtils.isEmpty(valueListenerClasses)) {
|
||||
listenerClasses = valueListenerClasses;
|
||||
}
|
||||
|
||||
if (listenerClasses != null) {
|
||||
classesList.addAll(0, Arrays.<Class<? extends TestExecutionListener>> asList(listenerClasses));
|
||||
}
|
||||
declaringClass = (testExecutionListeners.inheritListeners() ?
|
||||
AnnotationUtils.findAnnotationDeclaringClass(annotationType, declaringClass.getSuperclass()) : null);
|
||||
}
|
||||
}
|
||||
|
||||
List<TestExecutionListener> listeners = new ArrayList<TestExecutionListener>(classesList.size());
|
||||
for (Class<? extends TestExecutionListener> listenerClass : classesList) {
|
||||
try {
|
||||
listeners.add(BeanUtils.instantiateClass(listenerClass));
|
||||
}
|
||||
catch (NoClassDefFoundError err) {
|
||||
if (defaultListeners) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not instantiate default TestExecutionListener class ["
|
||||
+ listenerClass.getName()
|
||||
+ "]. Specify custom listener classes or make the default listener classes available.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
return listeners.toArray(new TestExecutionListener[listeners.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the default {@link TestExecutionListener} classes.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Set<Class<? extends TestExecutionListener>> getDefaultTestExecutionListenerClasses() {
|
||||
Set<Class<? extends TestExecutionListener>> defaultListenerClasses =
|
||||
new LinkedHashSet<Class<? extends TestExecutionListener>>();
|
||||
for (String className : DEFAULT_TEST_EXECUTION_LISTENER_CLASS_NAMES) {
|
||||
try {
|
||||
defaultListenerClasses.add(
|
||||
(Class<? extends TestExecutionListener>) getClass().getClassLoader().loadClass(className));
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not load default TestExecutionListener class [" + className
|
||||
+ "]. Specify custom listener classes or make the default listener classes available.");
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultListenerClasses;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Hook for pre-processing a test class <em>before</em> execution of any
|
||||
* tests within the class. Should be called prior to any framework-specific
|
||||
* <em>before class methods</em> (e.g., methods annotated with JUnit's
|
||||
* {@link org.junit.BeforeClass @BeforeClass}).
|
||||
* <p>An attempt will be made to give each registered
|
||||
* {@link TestExecutionListener} a chance to pre-process the test class
|
||||
* execution. If a listener throws an exception, however, the remaining
|
||||
* registered listeners will <strong>not</strong> be called.
|
||||
* @throws Exception if a registered TestExecutionListener throws an
|
||||
* exception
|
||||
* @see #getTestExecutionListeners()
|
||||
*/
|
||||
public void beforeTestClass() throws Exception {
|
||||
final Class<?> testClass = getTestContext().getTestClass();
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("beforeTestClass(): class [" + testClass + "]");
|
||||
}
|
||||
getTestContext().updateState(null, null, null);
|
||||
|
||||
for (TestExecutionListener testExecutionListener : getTestExecutionListeners()) {
|
||||
try {
|
||||
testExecutionListener.beforeTestClass(getTestContext());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener
|
||||
+ "] to process 'before class' callback for test class [" + testClass + "]", ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for preparing a test instance prior to execution of any individual
|
||||
* test methods, for example for injecting dependencies, etc. Should be
|
||||
* called immediately after instantiation of the test instance.
|
||||
* <p>The managed {@link TestContext} will be updated with the supplied
|
||||
* <code>testInstance</code>.
|
||||
* <p>An attempt will be made to give each registered
|
||||
* {@link TestExecutionListener} a chance to prepare the test instance. If a
|
||||
* listener throws an exception, however, the remaining registered listeners
|
||||
* will <strong>not</strong> be called.
|
||||
* @param testInstance the test instance to prepare (never <code>null</code>)
|
||||
* @throws Exception if a registered TestExecutionListener throws an exception
|
||||
* @see #getTestExecutionListeners()
|
||||
*/
|
||||
public void prepareTestInstance(Object testInstance) throws Exception {
|
||||
Assert.notNull(testInstance, "testInstance must not be null");
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("prepareTestInstance(): instance [" + testInstance + "]");
|
||||
}
|
||||
getTestContext().updateState(testInstance, null, null);
|
||||
|
||||
for (TestExecutionListener testExecutionListener : getTestExecutionListeners()) {
|
||||
try {
|
||||
testExecutionListener.prepareTestInstance(getTestContext());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.error("Caught exception while allowing TestExecutionListener [" + testExecutionListener
|
||||
+ "] to prepare test instance [" + testInstance + "]", ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for pre-processing a test <em>before</em> execution of the supplied
|
||||
* {@link Method test method}, for example for setting up test fixtures,
|
||||
* starting a transaction, etc. Should be called prior to any
|
||||
* framework-specific <em>before methods</em> (e.g., methods annotated with
|
||||
* JUnit's {@link org.junit.Before @Before}).
|
||||
* <p>The managed {@link TestContext} will be updated with the supplied
|
||||
* <code>testInstance</code> and <code>testMethod</code>.
|
||||
* <p>An attempt will be made to give each registered
|
||||
* {@link TestExecutionListener} a chance to pre-process the test method
|
||||
* execution. If a listener throws an exception, however, the remaining
|
||||
* registered listeners will <strong>not</strong> be called.
|
||||
* @param testInstance the current test instance (never <code>null</code>)
|
||||
* @param testMethod the test method which is about to be executed on the
|
||||
* test instance
|
||||
* @throws Exception if a registered TestExecutionListener throws an exception
|
||||
* @see #getTestExecutionListeners()
|
||||
*/
|
||||
public void beforeTestMethod(Object testInstance, Method testMethod) throws Exception {
|
||||
Assert.notNull(testInstance, "Test instance must not be null");
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("beforeTestMethod(): instance [" + testInstance + "], method [" + testMethod + "]");
|
||||
}
|
||||
getTestContext().updateState(testInstance, testMethod, null);
|
||||
|
||||
for (TestExecutionListener testExecutionListener : getTestExecutionListeners()) {
|
||||
try {
|
||||
testExecutionListener.beforeTestMethod(getTestContext());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener
|
||||
+ "] to process 'before' execution of test method [" + testMethod + "] for test instance ["
|
||||
+ testInstance + "]", ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for post-processing a test <em>after</em> execution of the supplied
|
||||
* {@link Method test method}, for example for tearing down test fixtures,
|
||||
* ending a transaction, etc. Should be called after any framework-specific
|
||||
* <em>after methods</em> (e.g., methods annotated with JUnit's
|
||||
* {@link org.junit.After @After}).
|
||||
* <p>The managed {@link TestContext} will be updated with the supplied
|
||||
* <code>testInstance</code>, <code>testMethod</code>, and
|
||||
* <code>exception</code>.
|
||||
* <p>Each registered {@link TestExecutionListener} will be given a chance to
|
||||
* post-process the test method execution. If a listener throws an
|
||||
* exception, the remaining registered listeners will still be called, but
|
||||
* the first exception thrown will be tracked and rethrown after all
|
||||
* listeners have executed. Note that registered listeners will be executed
|
||||
* in the opposite order in which they were registered.
|
||||
* @param testInstance the current test instance (never <code>null</code>)
|
||||
* @param testMethod the test method which has just been executed on the
|
||||
* test instance
|
||||
* @param exception the exception that was thrown during execution of the
|
||||
* test method or by a TestExecutionListener, or <code>null</code> if none
|
||||
* was thrown
|
||||
* @throws Exception if a registered TestExecutionListener throws an exception
|
||||
* @see #getTestExecutionListeners()
|
||||
*/
|
||||
public void afterTestMethod(Object testInstance, Method testMethod, Throwable exception) throws Exception {
|
||||
Assert.notNull(testInstance, "testInstance must not be null");
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("afterTestMethod(): instance [" + testInstance + "], method [" + testMethod + "], exception ["
|
||||
+ exception + "]");
|
||||
}
|
||||
getTestContext().updateState(testInstance, testMethod, exception);
|
||||
|
||||
Exception afterTestMethodException = null;
|
||||
// Traverse the TestExecutionListeners in reverse order to ensure proper
|
||||
// "wrapper"-style execution of listeners.
|
||||
for (TestExecutionListener testExecutionListener : getReversedTestExecutionListeners()) {
|
||||
try {
|
||||
testExecutionListener.afterTestMethod(getTestContext());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener
|
||||
+ "] to process 'after' execution for test: method [" + testMethod + "], instance ["
|
||||
+ testInstance + "], exception [" + exception + "]", ex);
|
||||
if (afterTestMethodException == null) {
|
||||
afterTestMethodException = ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (afterTestMethodException != null) {
|
||||
throw afterTestMethodException;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for post-processing a test class <em>after</em> execution of all
|
||||
* tests within the class. Should be called after any framework-specific
|
||||
* <em>after class methods</em> (e.g., methods annotated with JUnit's
|
||||
* {@link org.junit.AfterClass @AfterClass}).
|
||||
* <p>Each registered {@link TestExecutionListener} will be given a chance to
|
||||
* post-process the test class. If a listener throws an exception, the
|
||||
* remaining registered listeners will still be called, but the first
|
||||
* exception thrown will be tracked and rethrown after all listeners have
|
||||
* executed. Note that registered listeners will be executed in the opposite
|
||||
* order in which they were registered.
|
||||
* @throws Exception if a registered TestExecutionListener throws an exception
|
||||
* @see #getTestExecutionListeners()
|
||||
*/
|
||||
public void afterTestClass() throws Exception {
|
||||
final Class<?> testClass = getTestContext().getTestClass();
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("afterTestClass(): class [" + testClass + "]");
|
||||
}
|
||||
getTestContext().updateState(null, null, null);
|
||||
|
||||
Exception afterTestClassException = null;
|
||||
// Traverse the TestExecutionListeners in reverse order to ensure proper
|
||||
// "wrapper"-style execution of listeners.
|
||||
for (TestExecutionListener testExecutionListener : getReversedTestExecutionListeners()) {
|
||||
try {
|
||||
testExecutionListener.afterTestClass(getTestContext());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener
|
||||
+ "] to process 'after class' callback for test class [" + testClass + "]", ex);
|
||||
if (afterTestClassException == null) {
|
||||
afterTestClassException = ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (afterTestClassException != null) {
|
||||
throw afterTestClassException;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* <code>TestExecutionListener</code> defines a <em>listener</em> API for
|
||||
* reacting to test execution events published by the {@link TestContextManager}
|
||||
* with which the listener is registered.
|
||||
* </p>
|
||||
* <p>
|
||||
* Concrete implementations must provide a <code>public</code> no-args
|
||||
* constructor, so that listeners can be instantiated transparently by tools and
|
||||
* configuration mechanisms.
|
||||
* </p>
|
||||
* <p>
|
||||
* Spring provides the following out-of-the-box implementations:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>
|
||||
* {@link org.springframework.test.context.support.DependencyInjectionTestExecutionListener
|
||||
* DependencyInjectionTestExecutionListener}</li>
|
||||
* <li>
|
||||
* {@link org.springframework.test.context.support.DirtiesContextTestExecutionListener
|
||||
* DirtiesContextTestExecutionListener}</li>
|
||||
* <li>
|
||||
* {@link org.springframework.test.context.transaction.TransactionalTestExecutionListener
|
||||
* TransactionalTestExecutionListener}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
*/
|
||||
public interface TestExecutionListener {
|
||||
|
||||
/**
|
||||
* Pre-processes a test class <em>before</em> execution of all tests within
|
||||
* the class.
|
||||
* <p>
|
||||
* This method should be called immediately before framework-specific
|
||||
* <em>before class</em> lifecycle callbacks.
|
||||
* <p>
|
||||
* If a given testing framework (e.g., JUnit 3.8) does not support
|
||||
* <em>before class</em> lifecycle callbacks, this method will not be called
|
||||
* for that framework.
|
||||
*
|
||||
* @param testContext the test context for the test; never <code>null</code>
|
||||
* @throws Exception allows any exception to propagate
|
||||
*/
|
||||
void beforeTestClass(TestContext testContext) throws Exception;
|
||||
|
||||
/**
|
||||
* Prepares the {@link Object test instance} of the supplied
|
||||
* {@link TestContext test context}, for example by injecting dependencies.
|
||||
* <p>
|
||||
* This method should be called immediately after instantiation of the test
|
||||
* instance but prior to any framework-specific lifecycle callbacks.
|
||||
*
|
||||
* @param testContext the test context for the test; never <code>null</code>
|
||||
* @throws Exception allows any exception to propagate
|
||||
*/
|
||||
void prepareTestInstance(TestContext testContext) throws Exception;
|
||||
|
||||
/**
|
||||
* Pre-processes a test <em>before</em> execution of the
|
||||
* {@link java.lang.reflect.Method test method} in the supplied
|
||||
* {@link TestContext test context}, for example by setting up test
|
||||
* fixtures.
|
||||
* <p>
|
||||
* This method should be called immediately prior to framework-specific
|
||||
* <em>before</em> lifecycle callbacks.
|
||||
*
|
||||
* @param testContext the test context in which the test method will be
|
||||
* executed; never <code>null</code>
|
||||
* @throws Exception allows any exception to propagate
|
||||
*/
|
||||
void beforeTestMethod(TestContext testContext) throws Exception;
|
||||
|
||||
/**
|
||||
* Post-processes a test <em>after</em> execution of the
|
||||
* {@link java.lang.reflect.Method test method} in the supplied
|
||||
* {@link TestContext test context}, for example by tearing down test
|
||||
* fixtures.
|
||||
* <p>
|
||||
* This method should be called immediately after framework-specific
|
||||
* <em>after</em> lifecycle callbacks.
|
||||
*
|
||||
* @param testContext the test context in which the test method was
|
||||
* executed; never <code>null</code>
|
||||
* @throws Exception allows any exception to propagate
|
||||
*/
|
||||
void afterTestMethod(TestContext testContext) throws Exception;
|
||||
|
||||
/**
|
||||
* Post-processes a test class <em>after</em> execution of all tests within
|
||||
* the class.
|
||||
* <p>
|
||||
* This method should be called immediately after framework-specific
|
||||
* <em>after class</em> lifecycle callbacks.
|
||||
* <p>
|
||||
* If a given testing framework (e.g., JUnit 3.8) does not support
|
||||
* <em>after class</em> lifecycle callbacks, this method will not be called
|
||||
* for that framework.
|
||||
*
|
||||
* @param testContext the test context for the test; never <code>null</code>
|
||||
* @throws Exception allows any exception to propagate
|
||||
*/
|
||||
void afterTestClass(TestContext testContext) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* <code>TestExecutionListeners</code> defines class-level metadata for
|
||||
* configuring which {@link TestExecutionListener TestExecutionListeners} should
|
||||
* be registered with a {@link TestContextManager}. Typically,
|
||||
* <code>@TestExecutionListeners</code> will be used in conjunction with
|
||||
* {@link ContextConfiguration @ContextConfiguration}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 2.5
|
||||
* @see TestExecutionListener
|
||||
* @see TestContextManager
|
||||
* @see ContextConfiguration
|
||||
*/
|
||||
@Documented
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface TestExecutionListeners {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The {@link TestExecutionListener TestExecutionListeners} to register with
|
||||
* a {@link TestContextManager}.
|
||||
* </p>
|
||||
*
|
||||
* @see org.springframework.test.context.support.DependencyInjectionTestExecutionListener
|
||||
* @see org.springframework.test.context.support.DirtiesContextTestExecutionListener
|
||||
* @see org.springframework.test.context.transaction.TransactionalTestExecutionListener
|
||||
*/
|
||||
Class<? extends TestExecutionListener>[] listeners() default {};
|
||||
|
||||
/**
|
||||
* Alias for {@link #listeners() listeners}.
|
||||
*/
|
||||
Class<? extends TestExecutionListener>[] value() default {};
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Whether or not {@link #value() TestExecutionListeners} from superclasses
|
||||
* should be <em>inherited</em>.
|
||||
* </p>
|
||||
* <p>
|
||||
* The default value is <code>true</code>, which means that an annotated
|
||||
* class will <em>inherit</em> the listeners defined by an annotated
|
||||
* superclass. Specifically, the listeners for an annotated class will be
|
||||
* appended to the list of listeners defined by an annotated superclass.
|
||||
* Thus, subclasses have the option of <em>extending</em> the list of
|
||||
* listeners. In the following example, <code>AbstractBaseTest</code> will
|
||||
* be configured with <code>DependencyInjectionTestExecutionListener</code>
|
||||
* and <code>DirtiesContextTestExecutionListener</code>; whereas,
|
||||
* <code>TransactionalTest</code> will be configured with
|
||||
* <code>DependencyInjectionTestExecutionListener</code>,
|
||||
* <code>DirtiesContextTestExecutionListener</code>, <strong>and</strong>
|
||||
* <code>TransactionalTestExecutionListener</code>, in that order.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
|
||||
* DirtiesContextTestExecutionListener.class })
|
||||
* public abstract class AbstractBaseTest {
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* @TestExecutionListeners(TransactionalTestExecutionListener.class)
|
||||
* public class TransactionalTest extends AbstractBaseTest {
|
||||
* // ...
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* If <code>inheritListeners</code> is set to <code>false</code>, the
|
||||
* listeners for the annotated class will <em>shadow</em> and effectively
|
||||
* replace any listeners defined by a superclass.
|
||||
* </p>
|
||||
*/
|
||||
boolean inheritListeners() default true;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,399 @@
|
||||
/*
|
||||
* 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.junit38;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.test.annotation.ExpectedException;
|
||||
import org.springframework.test.annotation.IfProfileValue;
|
||||
import org.springframework.test.annotation.ProfileValueSource;
|
||||
import org.springframework.test.annotation.ProfileValueUtils;
|
||||
import org.springframework.test.annotation.Repeat;
|
||||
import org.springframework.test.annotation.Timed;
|
||||
import org.springframework.test.context.TestContextManager;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
||||
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Abstract base {@link TestCase} which integrates the <i>Spring TestContext
|
||||
* Framework</i> and explicit {@link ApplicationContext} testing support in a
|
||||
* <strong>JUnit 3.8</strong> environment.
|
||||
* </p>
|
||||
* <p>
|
||||
* Concrete subclasses:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>Typically declare a class-level
|
||||
* {@link org.springframework.test.context.ContextConfiguration
|
||||
* @ContextConfiguration} annotation to configure the
|
||||
* {@link ApplicationContext application context}
|
||||
* {@link org.springframework.test.context.ContextConfiguration#locations()
|
||||
* resource locations}. <i>If your test does not need to load an application
|
||||
* context, you may choose to omit the
|
||||
* {@link org.springframework.test.context.ContextConfiguration
|
||||
* @ContextConfiguration} declaration and configure the appropriate
|
||||
* {@link org.springframework.test.context.TestExecutionListener
|
||||
* TestExecutionListeners} manually.</i></li>
|
||||
* <li>Must declare public constructors which match the signatures of
|
||||
* {@link #AbstractJUnit38SpringContextTests()
|
||||
* AbstractJUnit38SpringContextTests()} and
|
||||
* {@link #AbstractJUnit38SpringContextTests(String)
|
||||
* AbstractJUnit38SpringContextTests(String)} and delegate to
|
||||
* <code>super();</code> and <code>super(name);</code> respectively.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* The following list constitutes all annotations currently supported directly
|
||||
* by <code>AbstractJUnit38SpringContextTests</code>. <i>(Note that additional
|
||||
* annotations may be supported by various
|
||||
* {@link org.springframework.test.context.TestExecutionListener
|
||||
* TestExecutionListeners})</i>
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>{@link org.springframework.test.annotation.DirtiesContext
|
||||
* @DirtiesContext} (via the configured
|
||||
* {@link DirtiesContextTestExecutionListener}; only supported on methods for
|
||||
* JUnit 3.8)</li>
|
||||
* <li>
|
||||
* {@link org.springframework.test.annotation.ProfileValueSourceConfiguration
|
||||
* @ProfileValueSourceConfiguration}</li>
|
||||
* <li>{@link IfProfileValue @IfProfileValue}</li>
|
||||
* <li>{@link ExpectedException @ExpectedException}</li>
|
||||
* <li>{@link Timed @Timed}</li>
|
||||
* <li>{@link Repeat @Repeat}</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* JUnit 3.8 does not support <i>before class</i> or <i>after class</i>
|
||||
* lifecycle callbacks. The following
|
||||
* {@link org.springframework.test.context.TestExecutionListener
|
||||
* TestExecutionListener} methods are therefore unsupported in a JUnit 3.8
|
||||
* environment:
|
||||
* <ul>
|
||||
* <li>
|
||||
* {@link org.springframework.test.context.TestExecutionListener#beforeTestClass(org.springframework.test.context.TestContext)
|
||||
* beforeTestClass()}</li>
|
||||
* <li>
|
||||
* {@link org.springframework.test.context.TestExecutionListener#afterTestClass(org.springframework.test.context.TestContext)
|
||||
* afterTestClass()}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see org.springframework.test.context.TestContext
|
||||
* @see org.springframework.test.context.TestContextManager
|
||||
* @see org.springframework.test.context.TestExecutionListeners
|
||||
* @see AbstractTransactionalJUnit38SpringContextTests
|
||||
* @see org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests
|
||||
* @see org.springframework.test.context.testng.AbstractTestNGSpringContextTests
|
||||
* @deprecated as of Spring 3.1, in favor of using
|
||||
* {@link org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests AbstractJUnit4SpringContextTests}
|
||||
*/
|
||||
@Deprecated
|
||||
@TestExecutionListeners( { DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class })
|
||||
public abstract class AbstractJUnit38SpringContextTests extends TestCase implements ApplicationContextAware {
|
||||
|
||||
private static int disabledTestCount = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Return the number of tests disabled in this environment.
|
||||
*/
|
||||
public static int getDisabledTestCount() {
|
||||
return disabledTestCount;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Logger available to subclasses.
|
||||
*/
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
/**
|
||||
* The {@link ApplicationContext} that was injected into this test instance
|
||||
* via {@link #setApplicationContext(ApplicationContext)}.
|
||||
*/
|
||||
protected ApplicationContext applicationContext;
|
||||
|
||||
/**
|
||||
* {@link ProfileValueSource} available to subclasses but primarily intended
|
||||
* for internal use to provide support for {@link IfProfileValue
|
||||
* @IfProfileValue}.
|
||||
*/
|
||||
protected final ProfileValueSource profileValueSource;
|
||||
|
||||
private final TestContextManager testContextManager;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new AbstractJUnit38SpringContextTests instance; initializes
|
||||
* the internal {@link TestContextManager} for the current test; and
|
||||
* retrieves the configured (or default) {@link ProfileValueSource}.
|
||||
*/
|
||||
public AbstractJUnit38SpringContextTests() {
|
||||
super();
|
||||
this.testContextManager = new TestContextManager(getClass());
|
||||
this.profileValueSource = ProfileValueUtils.retrieveProfileValueSource(getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new AbstractJUnit38SpringContextTests instance with the
|
||||
* supplied <code>name</code>; initializes the internal
|
||||
* {@link TestContextManager} for the current test; and retrieves the
|
||||
* configured (or default) {@link ProfileValueSource}.
|
||||
*
|
||||
* @param name the name of the current test to execute
|
||||
*/
|
||||
public AbstractJUnit38SpringContextTests(String name) {
|
||||
super(name);
|
||||
this.testContextManager = new TestContextManager(getClass());
|
||||
this.profileValueSource = ProfileValueUtils.retrieveProfileValueSource(getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link ApplicationContext} to be used by this test instance,
|
||||
* provided via {@link ApplicationContextAware} semantics.
|
||||
*/
|
||||
public final void setApplicationContext(final ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the <em>Spring TestContext Framework</em> test sequence.
|
||||
* <p>
|
||||
* In addition to standard {@link TestCase#runBare()} semantics, this
|
||||
* implementation performs the following:
|
||||
* <ul>
|
||||
* <li>Calls {@link TestContextManager#prepareTestInstance(Object)
|
||||
* prepareTestInstance()},
|
||||
* {@link TestContextManager#beforeTestMethod(Object,Method)
|
||||
* beforeTestMethod()}, and
|
||||
* {@link TestContextManager#afterTestMethod(Object,Method,Throwable)
|
||||
* afterTestMethod()} on this test's {@link TestContextManager} at the
|
||||
* appropriate test execution points.</li>
|
||||
* <li>Provides support for {@link IfProfileValue @IfProfileValue}.</li>
|
||||
* <li>Provides support for {@link Repeat @Repeat}.</li>
|
||||
* <li>Provides support for {@link Timed @Timed}.</li>
|
||||
* <li>Provides support for {@link ExpectedException
|
||||
* @ExpectedException}.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @see ProfileValueUtils#isTestEnabledInThisEnvironment
|
||||
*/
|
||||
@Override
|
||||
public void runBare() throws Throwable {
|
||||
this.testContextManager.prepareTestInstance(this);
|
||||
final Method testMethod = getTestMethod();
|
||||
|
||||
if (!ProfileValueUtils.isTestEnabledInThisEnvironment(this.profileValueSource, testMethod, getClass())) {
|
||||
recordDisabled(testMethod);
|
||||
return;
|
||||
}
|
||||
|
||||
runTestTimed(new TestExecutionCallback() {
|
||||
|
||||
public void run() throws Throwable {
|
||||
runManaged(testMethod);
|
||||
}
|
||||
}, testMethod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current test method.
|
||||
*/
|
||||
private Method getTestMethod() {
|
||||
assertNotNull("TestCase.getName() cannot be null", getName());
|
||||
Method testMethod = null;
|
||||
try {
|
||||
testMethod = getClass().getMethod(getName(), (Class[]) null);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
fail("Method \"" + getName() + "\" not found");
|
||||
}
|
||||
if (!Modifier.isPublic(testMethod.getModifiers())) {
|
||||
fail("Method \"" + getName() + "\" should be public");
|
||||
}
|
||||
return testMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a <em>timed</em> test via the supplied {@link TestExecutionCallback}
|
||||
* , providing support for the {@link Timed @Timed} annotation.
|
||||
*
|
||||
* @param tec the test execution callback to run
|
||||
* @param testMethod the actual test method: used to retrieve the
|
||||
* <code>timeout</code>
|
||||
* @throws Throwable if any exception is thrown
|
||||
* @see Timed
|
||||
* @see #runTest
|
||||
*/
|
||||
private void runTestTimed(TestExecutionCallback tec, Method testMethod) throws Throwable {
|
||||
Timed timed = testMethod.getAnnotation(Timed.class);
|
||||
if (timed == null) {
|
||||
runTest(tec, testMethod);
|
||||
}
|
||||
else {
|
||||
long startTime = System.currentTimeMillis();
|
||||
try {
|
||||
runTest(tec, testMethod);
|
||||
}
|
||||
finally {
|
||||
long elapsed = System.currentTimeMillis() - startTime;
|
||||
if (elapsed > timed.millis()) {
|
||||
fail("Took " + elapsed + " ms; limit was " + timed.millis());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a test via the supplied {@link TestExecutionCallback}, providing
|
||||
* support for the {@link ExpectedException @ExpectedException} and
|
||||
* {@link Repeat @Repeat} annotations.
|
||||
*
|
||||
* @param tec the test execution callback to run
|
||||
* @param testMethod the actual test method: used to retrieve the
|
||||
* {@link ExpectedException @ExpectedException} and {@link Repeat
|
||||
* @Repeat} annotations
|
||||
* @throws Throwable if any exception is thrown
|
||||
* @see ExpectedException
|
||||
* @see Repeat
|
||||
*/
|
||||
private void runTest(TestExecutionCallback tec, Method testMethod) throws Throwable {
|
||||
ExpectedException expectedExceptionAnnotation = testMethod.getAnnotation(ExpectedException.class);
|
||||
boolean exceptionIsExpected = (expectedExceptionAnnotation != null && expectedExceptionAnnotation.value() != null);
|
||||
Class<? extends Throwable> expectedException = (exceptionIsExpected ? expectedExceptionAnnotation.value()
|
||||
: null);
|
||||
|
||||
Repeat repeat = testMethod.getAnnotation(Repeat.class);
|
||||
int runs = ((repeat != null) && (repeat.value() > 1)) ? repeat.value() : 1;
|
||||
|
||||
for (int i = 0; i < runs; i++) {
|
||||
try {
|
||||
if (runs > 1 && this.logger.isInfoEnabled()) {
|
||||
this.logger.info("Repetition " + (i + 1) + " of test " + testMethod.getName());
|
||||
}
|
||||
tec.run();
|
||||
if (exceptionIsExpected) {
|
||||
fail("Expected exception: " + expectedException.getName());
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
if (!exceptionIsExpected) {
|
||||
throw ex;
|
||||
}
|
||||
if (!expectedException.isAssignableFrom(ex.getClass())) {
|
||||
// Wrap the unexpected throwable with an explicit message.
|
||||
AssertionFailedError assertionError = new AssertionFailedError("Unexpected exception, expected <"
|
||||
+ expectedException.getName() + "> but was <" + ex.getClass().getName() + ">");
|
||||
assertionError.initCause(ex);
|
||||
throw assertionError;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link TestContextManager#beforeTestMethod(Object,Method)} and
|
||||
* {@link TestContextManager#afterTestMethod(Object,Method,Throwable)} at
|
||||
* the appropriate test execution points.
|
||||
*
|
||||
* @param testMethod the test method to run
|
||||
* @throws Throwable if any exception is thrown
|
||||
* @see #runBare()
|
||||
* @see TestCase#runTest()
|
||||
*/
|
||||
private void runManaged(Method testMethod) throws Throwable {
|
||||
Throwable exception = null;
|
||||
boolean reachedTest = false;
|
||||
|
||||
try {
|
||||
this.testContextManager.beforeTestMethod(this, testMethod);
|
||||
setUp();
|
||||
reachedTest = true;
|
||||
runTest();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
exception = ex;
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
if (reachedTest) {
|
||||
tearDown();
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
if (exception == null) {
|
||||
exception = ex;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
this.testContextManager.afterTestMethod(this, testMethod, exception);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
if (exception == null) {
|
||||
exception = ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exception != null) {
|
||||
if (exception.getCause() instanceof AssertionError) {
|
||||
exception = exception.getCause();
|
||||
}
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the supplied test method as <em>disabled</em> in the current
|
||||
* environment by incrementing the total number of disabled tests and
|
||||
* logging a debug message.
|
||||
*
|
||||
* @param testMethod the test method that is disabled.
|
||||
* @see #getDisabledTestCount()
|
||||
*/
|
||||
protected void recordDisabled(Method testMethod) {
|
||||
disabledTestCount++;
|
||||
if (this.logger.isInfoEnabled()) {
|
||||
this.logger.info("**** " + getClass().getName() + "." + getName() + "() is disabled in this environment. "
|
||||
+ "Total disabled tests = " + getDisabledTestCount());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Private inner class that defines a callback analogous to {@link Runnable}
|
||||
* , just declaring Throwable.
|
||||
*/
|
||||
private static interface TestExecutionCallback {
|
||||
|
||||
void run() throws Throwable;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.junit38;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.EncodedResource;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
|
||||
import org.springframework.test.jdbc.SimpleJdbcTestUtils;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Abstract {@link Transactional transactional} extension of
|
||||
* {@link AbstractJUnit38SpringContextTests} which adds convenience
|
||||
* functionality for JDBC access. Expects a {@link javax.sql.DataSource} bean
|
||||
* and a {@link PlatformTransactionManager} bean to be defined in the Spring
|
||||
* {@link ApplicationContext application context}.
|
||||
* </p>
|
||||
* <p>
|
||||
* This class exposes a {@link SimpleJdbcTemplate} and provides an easy way to
|
||||
* {@link #countRowsInTable(String) count the number of rows in a table} ,
|
||||
* {@link #deleteFromTables(String...) delete from the database} , and
|
||||
* {@link #executeSqlScript(String, boolean) execute SQL scripts} within a
|
||||
* transaction.
|
||||
* </p>
|
||||
* <p>
|
||||
* Concrete subclasses must fulfill the same requirements outlined in
|
||||
* {@link AbstractJUnit38SpringContextTests}.
|
||||
* </p>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see AbstractJUnit38SpringContextTests
|
||||
* @see org.springframework.test.context.ContextConfiguration
|
||||
* @see org.springframework.test.context.TestExecutionListeners
|
||||
* @see org.springframework.test.context.transaction.TransactionalTestExecutionListener
|
||||
* @see org.springframework.test.context.transaction.TransactionConfiguration
|
||||
* @see org.springframework.transaction.annotation.Transactional
|
||||
* @see org.springframework.test.annotation.NotTransactional
|
||||
* @see org.springframework.test.annotation.Rollback
|
||||
* @see org.springframework.test.context.transaction.BeforeTransaction
|
||||
* @see org.springframework.test.context.transaction.AfterTransaction
|
||||
* @see org.springframework.test.jdbc.SimpleJdbcTestUtils
|
||||
* @see org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests
|
||||
* @see org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests
|
||||
* @deprecated as of Spring 3.1, in favor of using
|
||||
* {@link org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests AbstractTransactionalJUnit4SpringContextTests}
|
||||
*/
|
||||
@Deprecated
|
||||
@TestExecutionListeners(TransactionalTestExecutionListener.class)
|
||||
@Transactional
|
||||
public abstract class AbstractTransactionalJUnit38SpringContextTests extends AbstractJUnit38SpringContextTests {
|
||||
|
||||
/**
|
||||
* The SimpleJdbcTemplate that this base class manages, available to subclasses.
|
||||
*/
|
||||
protected SimpleJdbcTemplate simpleJdbcTemplate;
|
||||
|
||||
private String sqlScriptEncoding;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new AbstractTransactionalJUnit38SpringContextTests instance.
|
||||
*/
|
||||
public AbstractTransactionalJUnit38SpringContextTests() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new AbstractTransactionalJUnit38SpringContextTests instance
|
||||
* with the supplied <code>name</code>.
|
||||
* @param name the name of the current test to execute
|
||||
*/
|
||||
public AbstractTransactionalJUnit38SpringContextTests(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the DataSource, typically provided via Dependency Injection.
|
||||
* @param dataSource The DataSource to inject
|
||||
*/
|
||||
@Autowired
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the encoding for SQL scripts, if different from the platform encoding.
|
||||
* @see #executeSqlScript
|
||||
*/
|
||||
public void setSqlScriptEncoding(String sqlScriptEncoding) {
|
||||
this.sqlScriptEncoding = sqlScriptEncoding;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Count the rows in the given table.
|
||||
* @param tableName table name to count rows in
|
||||
* @return the number of rows in the table
|
||||
*/
|
||||
protected int countRowsInTable(String tableName) {
|
||||
return SimpleJdbcTestUtils.countRowsInTable(this.simpleJdbcTemplate, tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for deleting all rows from the specified tables.
|
||||
* Use with caution outside of a transaction!
|
||||
* @param names the names of the tables from which to delete
|
||||
* @return the total number of rows deleted from all specified tables
|
||||
*/
|
||||
protected int deleteFromTables(String... names) {
|
||||
return SimpleJdbcTestUtils.deleteFromTables(this.simpleJdbcTemplate, names);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given SQL script. Use with caution outside of a transaction!
|
||||
* <p>The script will normally be loaded by classpath. There should be one statement
|
||||
* per line. Any semicolons will be removed. <b>Do not use this method to execute
|
||||
* DDL if you expect rollback.</b>
|
||||
* @param sqlResourcePath the Spring resource path for the SQL script
|
||||
* @param continueOnError whether or not to continue without throwing an
|
||||
* exception in the event of an error
|
||||
* @throws DataAccessException if there is an error executing a statement
|
||||
* and continueOnError was <code>false</code>
|
||||
*/
|
||||
protected void executeSqlScript(String sqlResourcePath, boolean continueOnError)
|
||||
throws DataAccessException {
|
||||
|
||||
Resource resource = this.applicationContext.getResource(sqlResourcePath);
|
||||
SimpleJdbcTestUtils.executeSqlScript(
|
||||
this.simpleJdbcTemplate, new EncodedResource(resource, this.sqlScriptEncoding), continueOnError);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* <p>Support classes for ApplicationContext-based and transactional
|
||||
* tests run with JUnit 3.8 and the <em>Spring TestContext Framework</em>.</p>
|
||||
*/
|
||||
|
||||
package org.springframework.test.context.junit38;
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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.junit4;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.test.context.TestContextManager;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
||||
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Abstract base test class which integrates the <em>Spring TestContext
|
||||
* Framework</em> with explicit {@link ApplicationContext} testing support in a
|
||||
* <strong>JUnit 4.5+</strong> environment.
|
||||
* </p>
|
||||
* <p>
|
||||
* Concrete subclasses should typically declare a class-level
|
||||
* {@link ContextConfiguration @ContextConfiguration} annotation to
|
||||
* configure the {@link ApplicationContext application context}
|
||||
* {@link ContextConfiguration#locations() resource locations}.
|
||||
* <em>If your test does not need to load an application context, you may choose
|
||||
* to omit the {@link ContextConfiguration @ContextConfiguration} declaration
|
||||
* and to configure the appropriate
|
||||
* {@link org.springframework.test.context.TestExecutionListener TestExecutionListeners}
|
||||
* manually.</em>
|
||||
* </p>
|
||||
* <p>
|
||||
* Note: this class serves only as a convenience for extension. If you do not
|
||||
* wish for your test classes to be tied to a Spring-specific class hierarchy,
|
||||
* you may configure your own custom test classes by using
|
||||
* {@link SpringJUnit4ClassRunner}, {@link ContextConfiguration
|
||||
* @ContextConfiguration}, {@link TestExecutionListeners
|
||||
* @TestExecutionListeners}, etc.
|
||||
* </p>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 2.5
|
||||
* @see ContextConfiguration
|
||||
* @see TestContext
|
||||
* @see TestContextManager
|
||||
* @see AbstractTransactionalJUnit4SpringContextTests
|
||||
* @see org.springframework.test.context.junit38.AbstractJUnit38SpringContextTests
|
||||
* @see org.springframework.test.context.testng.AbstractTestNGSpringContextTests
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@TestExecutionListeners( { DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class })
|
||||
public abstract class AbstractJUnit4SpringContextTests implements ApplicationContextAware {
|
||||
|
||||
/**
|
||||
* Logger available to subclasses.
|
||||
*/
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
/**
|
||||
* The {@link ApplicationContext} that was injected into this test instance
|
||||
* via {@link #setApplicationContext(ApplicationContext)}.
|
||||
*/
|
||||
protected ApplicationContext applicationContext;
|
||||
|
||||
|
||||
/**
|
||||
* Set the {@link ApplicationContext} to be used by this test instance,
|
||||
* provided via {@link ApplicationContextAware} semantics.
|
||||
*/
|
||||
public final void setApplicationContext(final ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.junit4;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.EncodedResource;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
|
||||
import org.springframework.test.jdbc.SimpleJdbcTestUtils;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Abstract {@link Transactional transactional} extension of
|
||||
* {@link AbstractJUnit4SpringContextTests} which adds convenience functionality
|
||||
* for JDBC access. Expects a {@link DataSource} bean and a
|
||||
* {@link PlatformTransactionManager} bean to be defined in the Spring
|
||||
* {@link ApplicationContext application context}.
|
||||
* </p>
|
||||
* <p>
|
||||
* This class exposes a {@link SimpleJdbcTemplate} and provides an easy way to
|
||||
* {@link #countRowsInTable(String) count the number of rows in a table} ,
|
||||
* {@link #deleteFromTables(String...) delete from the database} , and
|
||||
* {@link #executeSqlScript(String, boolean) execute SQL scripts} within a
|
||||
* transaction.
|
||||
* </p>
|
||||
* <p>
|
||||
* Concrete subclasses must fulfill the same requirements outlined in
|
||||
* {@link AbstractJUnit4SpringContextTests}.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note: this class serves only as a convenience for extension. If you do not
|
||||
* wish for your test classes to be tied to a Spring-specific class hierarchy,
|
||||
* you may configure your own custom test classes by using
|
||||
* {@link SpringJUnit4ClassRunner}, {@link ContextConfiguration
|
||||
* @ContextConfiguration}, {@link TestExecutionListeners
|
||||
* @TestExecutionListeners}, {@link Transactional @Transactional},
|
||||
* etc.
|
||||
* </p>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see AbstractJUnit4SpringContextTests
|
||||
* @see org.springframework.test.context.ContextConfiguration
|
||||
* @see org.springframework.test.context.TestExecutionListeners
|
||||
* @see org.springframework.test.context.transaction.TransactionalTestExecutionListener
|
||||
* @see org.springframework.test.context.transaction.TransactionConfiguration
|
||||
* @see org.springframework.transaction.annotation.Transactional
|
||||
* @see org.springframework.test.annotation.NotTransactional
|
||||
* @see org.springframework.test.annotation.Rollback
|
||||
* @see org.springframework.test.context.transaction.BeforeTransaction
|
||||
* @see org.springframework.test.context.transaction.AfterTransaction
|
||||
* @see org.springframework.test.jdbc.SimpleJdbcTestUtils
|
||||
* @see org.springframework.test.context.junit38.AbstractTransactionalJUnit38SpringContextTests
|
||||
* @see org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
@TestExecutionListeners(TransactionalTestExecutionListener.class)
|
||||
@Transactional
|
||||
public abstract class AbstractTransactionalJUnit4SpringContextTests extends AbstractJUnit4SpringContextTests {
|
||||
|
||||
/**
|
||||
* The SimpleJdbcTemplate that this base class manages, available to subclasses.
|
||||
*/
|
||||
protected SimpleJdbcTemplate simpleJdbcTemplate;
|
||||
|
||||
private String sqlScriptEncoding;
|
||||
|
||||
|
||||
/**
|
||||
* Set the DataSource, typically provided via Dependency Injection.
|
||||
*/
|
||||
@Autowired
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the encoding for SQL scripts, if different from the platform encoding.
|
||||
* @see #executeSqlScript
|
||||
*/
|
||||
public void setSqlScriptEncoding(String sqlScriptEncoding) {
|
||||
this.sqlScriptEncoding = sqlScriptEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the rows in the given table.
|
||||
* @param tableName table name to count rows in
|
||||
* @return the number of rows in the table
|
||||
*/
|
||||
protected int countRowsInTable(String tableName) {
|
||||
return SimpleJdbcTestUtils.countRowsInTable(this.simpleJdbcTemplate, tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for deleting all rows from the specified tables. Use
|
||||
* with caution outside of a transaction!
|
||||
* @param names the names of the tables from which to delete
|
||||
* @return the total number of rows deleted from all specified tables
|
||||
*/
|
||||
protected int deleteFromTables(String... names) {
|
||||
return SimpleJdbcTestUtils.deleteFromTables(this.simpleJdbcTemplate, names);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given SQL script. Use with caution outside of a transaction!
|
||||
* <p>The script will normally be loaded by classpath. There should be one
|
||||
* statement per line. Any semicolons will be removed. <b>Do not use this
|
||||
* method to execute DDL if you expect rollback.</b>
|
||||
* @param sqlResourcePath the Spring resource path for the SQL script
|
||||
* @param continueOnError whether or not to continue without throwing an
|
||||
* exception in the event of an error
|
||||
* @throws DataAccessException if there is an error executing a statement
|
||||
* and continueOnError was <code>false</code>
|
||||
*/
|
||||
protected void executeSqlScript(String sqlResourcePath, boolean continueOnError) throws DataAccessException {
|
||||
Resource resource = this.applicationContext.getResource(sqlResourcePath);
|
||||
SimpleJdbcTestUtils.executeSqlScript(this.simpleJdbcTemplate, new EncodedResource(resource,
|
||||
this.sqlScriptEncoding), continueOnError);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,474 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.junit4;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.internal.AssumptionViolatedException;
|
||||
import org.junit.internal.runners.model.EachTestNotifier;
|
||||
import org.junit.internal.runners.model.ReflectiveCallable;
|
||||
import org.junit.internal.runners.statements.ExpectException;
|
||||
import org.junit.internal.runners.statements.Fail;
|
||||
import org.junit.internal.runners.statements.FailOnTimeout;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runner.notification.RunNotifier;
|
||||
import org.junit.runners.BlockJUnit4ClassRunner;
|
||||
import org.junit.runners.model.FrameworkMethod;
|
||||
import org.junit.runners.model.InitializationError;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.springframework.test.annotation.ExpectedException;
|
||||
import org.springframework.test.annotation.ProfileValueUtils;
|
||||
import org.springframework.test.annotation.Repeat;
|
||||
import org.springframework.test.annotation.Timed;
|
||||
import org.springframework.test.context.TestContextManager;
|
||||
import org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks;
|
||||
import org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks;
|
||||
import org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks;
|
||||
import org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks;
|
||||
import org.springframework.test.context.junit4.statements.SpringFailOnTimeout;
|
||||
import org.springframework.test.context.junit4.statements.SpringRepeat;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* <code>SpringJUnit4ClassRunner</code> is a custom extension of
|
||||
* {@link BlockJUnit4ClassRunner} which provides functionality of the
|
||||
* <em>Spring TestContext Framework</em> to standard JUnit 4.5+ tests by means
|
||||
* of the {@link TestContextManager} and associated support classes and
|
||||
* annotations.
|
||||
* </p>
|
||||
* <p>
|
||||
* The following list constitutes all annotations currently supported directly
|
||||
* by <code>SpringJUnit4ClassRunner</code>.
|
||||
* <em>(Note that additional annotations may be supported by various
|
||||
* {@link org.springframework.test.context.TestExecutionListener
|
||||
* TestExecutionListeners})</em>
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>{@link Test#expected() @Test(expected=...)}</li>
|
||||
* <li>{@link ExpectedException @ExpectedException}</li>
|
||||
* <li>{@link Test#timeout() @Test(timeout=...)}</li>
|
||||
* <li>{@link Timed @Timed}</li>
|
||||
* <li>{@link Repeat @Repeat}</li>
|
||||
* <li>{@link Ignore @Ignore}</li>
|
||||
* <li>
|
||||
* {@link org.springframework.test.annotation.ProfileValueSourceConfiguration
|
||||
* @ProfileValueSourceConfiguration}</li>
|
||||
* <li>{@link org.springframework.test.annotation.IfProfileValue
|
||||
* @IfProfileValue}</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* <b>NOTE:</b> As of Spring 3.0, <code>SpringJUnit4ClassRunner</code> requires
|
||||
* JUnit 4.5+.
|
||||
* </p>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see TestContextManager
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(SpringJUnit4ClassRunner.class);
|
||||
|
||||
private final TestContextManager testContextManager;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new <code>SpringJUnit4ClassRunner</code> and initializes a
|
||||
* {@link TestContextManager} to provide Spring testing functionality to
|
||||
* standard JUnit tests.
|
||||
* @param clazz the test class to be run
|
||||
* @see #createTestContextManager(Class)
|
||||
*/
|
||||
public SpringJUnit4ClassRunner(Class<?> clazz) throws InitializationError {
|
||||
super(clazz);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("SpringJUnit4ClassRunner constructor called with [" + clazz + "].");
|
||||
}
|
||||
this.testContextManager = createTestContextManager(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link TestContextManager} for the supplied test class and
|
||||
* the configured <em>default <code>ContextLoader</code> class name</em>.
|
||||
* Can be overridden by subclasses.
|
||||
* @param clazz the test class to be managed
|
||||
* @see #getDefaultContextLoaderClassName(Class)
|
||||
*/
|
||||
protected TestContextManager createTestContextManager(Class<?> clazz) {
|
||||
return new TestContextManager(clazz, getDefaultContextLoaderClassName(clazz));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link TestContextManager} associated with this runner.
|
||||
*/
|
||||
protected final TestContextManager getTestContextManager() {
|
||||
return this.testContextManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the default <code>ContextLoader</code> class to use for
|
||||
* the supplied test class. The named class will be used if the test class
|
||||
* does not explicitly declare a <code>ContextLoader</code> class via the
|
||||
* <code>@ContextConfiguration</code> annotation.
|
||||
* <p>The default implementation returns <code>null</code>, thus implying use
|
||||
* of the <em>standard</em> default <code>ContextLoader</code> class name.
|
||||
* Can be overridden by subclasses.
|
||||
* @param clazz the test class
|
||||
* @return <code>null</code>
|
||||
*/
|
||||
protected String getDefaultContextLoaderClassName(Class<?> clazz) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a description suitable for an ignored test class if the test is
|
||||
* disabled via <code>@IfProfileValue</code> at the class-level, and
|
||||
* otherwise delegates to the parent implementation.
|
||||
* @see ProfileValueUtils#isTestEnabledInThisEnvironment(Class)
|
||||
*/
|
||||
@Override
|
||||
public Description getDescription() {
|
||||
if (!ProfileValueUtils.isTestEnabledInThisEnvironment(getTestClass().getJavaClass())) {
|
||||
return Description.createSuiteDescription(getTestClass().getJavaClass());
|
||||
}
|
||||
return super.getDescription();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the test is enabled in the first place. This prevents
|
||||
* classes with a non-matching <code>@IfProfileValue</code> annotation
|
||||
* from running altogether, even skipping the execution of
|
||||
* <code>prepareTestInstance()</code> <code>TestExecutionListener</code>
|
||||
* methods.
|
||||
* @see ProfileValueUtils#isTestEnabledInThisEnvironment(Class)
|
||||
* @see org.springframework.test.annotation.IfProfileValue
|
||||
* @see org.springframework.test.context.TestExecutionListener
|
||||
*/
|
||||
@Override
|
||||
public void run(RunNotifier notifier) {
|
||||
if (!ProfileValueUtils.isTestEnabledInThisEnvironment(getTestClass().getJavaClass())) {
|
||||
notifier.fireTestIgnored(getDescription());
|
||||
return;
|
||||
}
|
||||
super.run(notifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the {@link Statement} returned by the parent implementation with a
|
||||
* {@link RunBeforeTestClassCallbacks} statement, thus preserving the
|
||||
* default functionality but adding support for the Spring TestContext
|
||||
* Framework.
|
||||
* @see RunBeforeTestClassCallbacks
|
||||
*/
|
||||
@Override
|
||||
protected Statement withBeforeClasses(Statement statement) {
|
||||
Statement junitBeforeClasses = super.withBeforeClasses(statement);
|
||||
return new RunBeforeTestClassCallbacks(junitBeforeClasses, getTestContextManager());
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the {@link Statement} returned by the parent implementation with a
|
||||
* {@link RunAfterTestClassCallbacks} statement, thus preserving the default
|
||||
* functionality but adding support for the Spring TestContext Framework.
|
||||
* @see RunAfterTestClassCallbacks
|
||||
*/
|
||||
@Override
|
||||
protected Statement withAfterClasses(Statement statement) {
|
||||
Statement junitAfterClasses = super.withAfterClasses(statement);
|
||||
return new RunAfterTestClassCallbacks(junitAfterClasses, getTestContextManager());
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to the parent implementation for creating the test instance and
|
||||
* then allows the {@link #getTestContextManager() TestContextManager} to
|
||||
* prepare the test instance before returning it.
|
||||
* @see TestContextManager#prepareTestInstance(Object)
|
||||
*/
|
||||
@Override
|
||||
protected Object createTest() throws Exception {
|
||||
Object testInstance = super.createTest();
|
||||
getTestContextManager().prepareTestInstance(testInstance);
|
||||
return testInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the same logic as
|
||||
* {@link BlockJUnit4ClassRunner#runChild(FrameworkMethod, RunNotifier)},
|
||||
* except that tests are determined to be <em>ignored</em> by
|
||||
* {@link #isTestMethodIgnored(FrameworkMethod)}.
|
||||
*/
|
||||
@Override
|
||||
protected void runChild(FrameworkMethod frameworkMethod, RunNotifier notifier) {
|
||||
EachTestNotifier eachNotifier = springMakeNotifier(frameworkMethod, notifier);
|
||||
if (isTestMethodIgnored(frameworkMethod)) {
|
||||
eachNotifier.fireTestIgnored();
|
||||
return;
|
||||
}
|
||||
|
||||
eachNotifier.fireTestStarted();
|
||||
try {
|
||||
methodBlock(frameworkMethod).evaluate();
|
||||
}
|
||||
catch (AssumptionViolatedException e) {
|
||||
eachNotifier.addFailedAssumption(e);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
eachNotifier.addFailure(e);
|
||||
}
|
||||
finally {
|
||||
eachNotifier.fireTestFinished();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>springMakeNotifier()</code> is an exact copy of
|
||||
* {@link BlockJUnit4ClassRunner BlockJUnit4ClassRunner's}
|
||||
* <code>makeNotifier()</code> method, but we have decided to prefix it with
|
||||
* "spring" and keep it <code>private</code> in order to avoid the
|
||||
* compatibility clashes that were introduced in JUnit between versions 4.5,
|
||||
* 4.6, and 4.7.
|
||||
*/
|
||||
private EachTestNotifier springMakeNotifier(FrameworkMethod method, RunNotifier notifier) {
|
||||
Description description = describeChild(method);
|
||||
return new EachTestNotifier(notifier, description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Augments the default JUnit behavior
|
||||
* {@link #withPotentialRepeat(FrameworkMethod, Object, Statement) with
|
||||
* potential repeats} of the entire execution chain.
|
||||
* <p>Furthermore, support for timeouts has been moved down the execution chain
|
||||
* in order to include execution of {@link org.junit.Before @Before}
|
||||
* and {@link org.junit.After @After} methods within the timed
|
||||
* execution. Note that this differs from the default JUnit behavior of
|
||||
* executing <code>@Before</code> and <code>@After</code> methods
|
||||
* in the main thread while executing the actual test method in a separate
|
||||
* thread. Thus, the end effect is that <code>@Before</code> and
|
||||
* <code>@After</code> methods will be executed in the same thread as
|
||||
* the test method. As a consequence, JUnit-specified timeouts will work
|
||||
* fine in combination with Spring transactions. Note that JUnit-specific
|
||||
* timeouts still differ from Spring-specific timeouts in that the former
|
||||
* execute in a separate thread while the latter simply execute in the main
|
||||
* thread (like regular tests).
|
||||
* @see #possiblyExpectingExceptions(FrameworkMethod, Object, Statement)
|
||||
* @see #withBefores(FrameworkMethod, Object, Statement)
|
||||
* @see #withAfters(FrameworkMethod, Object, Statement)
|
||||
* @see #withPotentialTimeout(FrameworkMethod, Object, Statement)
|
||||
* @see #withPotentialRepeat(FrameworkMethod, Object, Statement)
|
||||
*/
|
||||
@Override
|
||||
protected Statement methodBlock(FrameworkMethod frameworkMethod) {
|
||||
Object testInstance;
|
||||
try {
|
||||
testInstance = new ReflectiveCallable() {
|
||||
|
||||
@Override
|
||||
protected Object runReflectiveCall() throws Throwable {
|
||||
return createTest();
|
||||
}
|
||||
}.run();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
return new Fail(ex);
|
||||
}
|
||||
|
||||
Statement statement = methodInvoker(frameworkMethod, testInstance);
|
||||
statement = possiblyExpectingExceptions(frameworkMethod, testInstance, statement);
|
||||
statement = withBefores(frameworkMethod, testInstance, statement);
|
||||
statement = withAfters(frameworkMethod, testInstance, statement);
|
||||
statement = withRulesReflectively(frameworkMethod, testInstance, statement);
|
||||
statement = withPotentialRepeat(frameworkMethod, testInstance, statement);
|
||||
statement = withPotentialTimeout(frameworkMethod, testInstance, statement);
|
||||
|
||||
return statement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes JUnit 4.7's private <code>withRules()</code> method using
|
||||
* reflection. This is necessary for backwards compatibility with the JUnit
|
||||
* 4.5 and 4.6 implementations of {@link BlockJUnit4ClassRunner}.
|
||||
*/
|
||||
private Statement withRulesReflectively(FrameworkMethod frameworkMethod, Object testInstance, Statement statement) {
|
||||
Method withRulesMethod = ReflectionUtils.findMethod(getClass(), "withRules", FrameworkMethod.class,
|
||||
Object.class, Statement.class);
|
||||
if (withRulesMethod != null) {
|
||||
// Original JUnit 4.7 code:
|
||||
// statement = withRules(frameworkMethod, testInstance, statement);
|
||||
ReflectionUtils.makeAccessible(withRulesMethod);
|
||||
statement = (Statement) ReflectionUtils.invokeMethod(withRulesMethod, this, frameworkMethod, testInstance,
|
||||
statement);
|
||||
}
|
||||
return statement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if {@link Ignore @Ignore} is present for
|
||||
* the supplied {@link FrameworkMethod test method} or if the test method is
|
||||
* disabled via <code>@IfProfileValue</code>.
|
||||
* @see ProfileValueUtils#isTestEnabledInThisEnvironment(Method, Class)
|
||||
*/
|
||||
protected boolean isTestMethodIgnored(FrameworkMethod frameworkMethod) {
|
||||
Method method = frameworkMethod.getMethod();
|
||||
return (method.isAnnotationPresent(Ignore.class) || !ProfileValueUtils.isTestEnabledInThisEnvironment(method,
|
||||
getTestClass().getJavaClass()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the same logic as
|
||||
* {@link BlockJUnit4ClassRunner#possiblyExpectingExceptions(FrameworkMethod, Object, Statement)}
|
||||
* except that the <em>expected exception</em> is retrieved using
|
||||
* {@link #getExpectedException(FrameworkMethod)}.
|
||||
*/
|
||||
@Override
|
||||
protected Statement possiblyExpectingExceptions(FrameworkMethod frameworkMethod, Object testInstance, Statement next) {
|
||||
Class<? extends Throwable> expectedException = getExpectedException(frameworkMethod);
|
||||
return expectedException != null ? new ExpectException(next, expectedException) : next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the <code>exception</code> that the supplied {@link FrameworkMethod
|
||||
* test method} is expected to throw.
|
||||
* <p>Supports both Spring's {@link ExpectedException @ExpectedException(...)}
|
||||
* and JUnit's {@link Test#expected() @Test(expected=...)} annotations, but
|
||||
* not both simultaneously.
|
||||
* @return the expected exception, or <code>null</code> if none was specified
|
||||
*/
|
||||
protected Class<? extends Throwable> getExpectedException(FrameworkMethod frameworkMethod) {
|
||||
Test testAnnotation = frameworkMethod.getAnnotation(Test.class);
|
||||
Class<? extends Throwable> junitExpectedException = (testAnnotation != null
|
||||
&& testAnnotation.expected() != Test.None.class ? testAnnotation.expected() : null);
|
||||
|
||||
ExpectedException expectedExAnn = frameworkMethod.getAnnotation(ExpectedException.class);
|
||||
Class<? extends Throwable> springExpectedException = (expectedExAnn != null ? expectedExAnn.value() : null);
|
||||
|
||||
if (springExpectedException != null && junitExpectedException != null) {
|
||||
String msg = "Test method [" + frameworkMethod.getMethod()
|
||||
+ "] has been configured with Spring's @ExpectedException(" + springExpectedException.getName()
|
||||
+ ".class) and JUnit's @Test(expected=" + junitExpectedException.getName()
|
||||
+ ".class) annotations. "
|
||||
+ "Only one declaration of an 'expected exception' is permitted per test method.";
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
|
||||
return springExpectedException != null ? springExpectedException : junitExpectedException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supports both Spring's {@link Timed @Timed} and JUnit's
|
||||
* {@link Test#timeout() @Test(timeout=...)} annotations, but not both
|
||||
* simultaneously. Returns either a {@link SpringFailOnTimeout}, a
|
||||
* {@link FailOnTimeout}, or the unmodified, supplied {@link Statement} as
|
||||
* appropriate.
|
||||
* @see #getSpringTimeout(FrameworkMethod)
|
||||
* @see #getJUnitTimeout(FrameworkMethod)
|
||||
*/
|
||||
@Override
|
||||
protected Statement withPotentialTimeout(FrameworkMethod frameworkMethod, Object testInstance, Statement next) {
|
||||
Statement statement = null;
|
||||
long springTimeout = getSpringTimeout(frameworkMethod);
|
||||
long junitTimeout = getJUnitTimeout(frameworkMethod);
|
||||
if (springTimeout > 0 && junitTimeout > 0) {
|
||||
String msg = "Test method [" + frameworkMethod.getMethod()
|
||||
+ "] has been configured with Spring's @Timed(millis=" + springTimeout
|
||||
+ ") and JUnit's @Test(timeout=" + junitTimeout
|
||||
+ ") annotations. Only one declaration of a 'timeout' is permitted per test method.";
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
else if (springTimeout > 0) {
|
||||
statement = new SpringFailOnTimeout(next, springTimeout);
|
||||
}
|
||||
else if (junitTimeout > 0) {
|
||||
statement = new FailOnTimeout(next, junitTimeout);
|
||||
}
|
||||
else {
|
||||
statement = next;
|
||||
}
|
||||
|
||||
return statement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the configured JUnit <code>timeout</code> from the {@link Test
|
||||
* @Test} annotation on the supplied {@link FrameworkMethod test method}.
|
||||
* @return the timeout, or <code>0</code> if none was specified.
|
||||
*/
|
||||
protected long getJUnitTimeout(FrameworkMethod frameworkMethod) {
|
||||
Test testAnnotation = frameworkMethod.getAnnotation(Test.class);
|
||||
return (testAnnotation != null && testAnnotation.timeout() > 0 ? testAnnotation.timeout() : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the configured Spring-specific <code>timeout</code> from the
|
||||
* {@link Timed @Timed} annotation on the supplied
|
||||
* {@link FrameworkMethod test method}.
|
||||
* @return the timeout, or <code>0</code> if none was specified.
|
||||
*/
|
||||
protected long getSpringTimeout(FrameworkMethod frameworkMethod) {
|
||||
Timed timedAnnotation = frameworkMethod.getAnnotation(Timed.class);
|
||||
return (timedAnnotation != null && timedAnnotation.millis() > 0 ? timedAnnotation.millis() : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the {@link Statement} returned by the parent implementation with a
|
||||
* {@link RunBeforeTestMethodCallbacks} statement, thus preserving the
|
||||
* default functionality but adding support for the Spring TestContext
|
||||
* Framework.
|
||||
* @see RunBeforeTestMethodCallbacks
|
||||
*/
|
||||
@Override
|
||||
protected Statement withBefores(FrameworkMethod frameworkMethod, Object testInstance, Statement statement) {
|
||||
Statement junitBefores = super.withBefores(frameworkMethod, testInstance, statement);
|
||||
return new RunBeforeTestMethodCallbacks(junitBefores, testInstance, frameworkMethod.getMethod(),
|
||||
getTestContextManager());
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the {@link Statement} returned by the parent implementation with a
|
||||
* {@link RunAfterTestMethodCallbacks} statement, thus preserving the
|
||||
* default functionality but adding support for the Spring TestContext
|
||||
* Framework.
|
||||
* @see RunAfterTestMethodCallbacks
|
||||
*/
|
||||
@Override
|
||||
protected Statement withAfters(FrameworkMethod frameworkMethod, Object testInstance, Statement statement) {
|
||||
Statement junitAfters = super.withAfters(frameworkMethod, testInstance, statement);
|
||||
return new RunAfterTestMethodCallbacks(junitAfters, testInstance, frameworkMethod.getMethod(),
|
||||
getTestContextManager());
|
||||
}
|
||||
|
||||
/**
|
||||
* Supports Spring's {@link Repeat @Repeat} annotation by returning a
|
||||
* {@link SpringRepeat} statement initialized with the configured repeat
|
||||
* count or <code>1</code> if no repeat count is configured.
|
||||
* @see SpringRepeat
|
||||
*/
|
||||
protected Statement withPotentialRepeat(FrameworkMethod frameworkMethod, Object testInstance, Statement next) {
|
||||
Repeat repeatAnnotation = frameworkMethod.getAnnotation(Repeat.class);
|
||||
int repeat = (repeatAnnotation != null ? repeatAnnotation.value() : 1);
|
||||
return new SpringRepeat(next, frameworkMethod.getMethod(), repeat);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* <p>Support classes for ApplicationContext-based and transactional
|
||||
* tests run with JUnit 4.5+ and the <em>Spring TestContext Framework</em>.</p>
|
||||
*/
|
||||
|
||||
package org.springframework.test.context.junit4;
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.junit4.statements;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.internal.runners.model.MultipleFailureException;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.springframework.test.context.TestContextManager;
|
||||
|
||||
/**
|
||||
* <code>RunAfterTestClassCallbacks</code> is a custom JUnit 4.5+
|
||||
* {@link Statement} which allows the <em>Spring TestContext Framework</em> to
|
||||
* be plugged into the JUnit execution chain by calling
|
||||
* {@link TestContextManager#afterTestClass() afterTestClass()} on the supplied
|
||||
* {@link TestContextManager}.
|
||||
*
|
||||
* @see #evaluate()
|
||||
* @see RunBeforeTestMethodCallbacks
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class RunAfterTestClassCallbacks extends Statement {
|
||||
|
||||
private final Statement next;
|
||||
|
||||
private final TestContextManager testContextManager;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new <code>RunAfterTestClassCallbacks</code> statement.
|
||||
*
|
||||
* @param next the next <code>Statement</code> in the execution chain
|
||||
* @param testContextManager the TestContextManager upon which to call
|
||||
* <code>afterTestClass()</code>
|
||||
*/
|
||||
public RunAfterTestClassCallbacks(Statement next, TestContextManager testContextManager) {
|
||||
this.next = next;
|
||||
this.testContextManager = testContextManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the next {@link Statement} in the execution chain (typically an
|
||||
* instance of {@link org.junit.internal.runners.statements.RunAfters
|
||||
* RunAfters}), catching any exceptions thrown, and then calls
|
||||
* {@link TestContextManager#afterTestClass()}. If the call to
|
||||
* <code>afterTestClass()</code> throws an exception, it will also be
|
||||
* tracked. Multiple exceptions will be combined into a
|
||||
* {@link MultipleFailureException}.
|
||||
*/
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
List<Throwable> errors = new ArrayList<Throwable>();
|
||||
try {
|
||||
this.next.evaluate();
|
||||
}
|
||||
catch (Throwable e) {
|
||||
errors.add(e);
|
||||
}
|
||||
|
||||
try {
|
||||
this.testContextManager.afterTestClass();
|
||||
}
|
||||
catch (Exception e) {
|
||||
errors.add(e);
|
||||
}
|
||||
|
||||
if (errors.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (errors.size() == 1) {
|
||||
throw errors.get(0);
|
||||
}
|
||||
throw new MultipleFailureException(errors);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.junit4.statements;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.internal.runners.model.MultipleFailureException;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.springframework.test.context.TestContextManager;
|
||||
|
||||
/**
|
||||
* <code>RunAfterTestMethodCallbacks</code> is a custom JUnit 4.5+
|
||||
* {@link Statement} which allows the <em>Spring TestContext Framework</em> to
|
||||
* be plugged into the JUnit execution chain by calling
|
||||
* {@link TestContextManager#afterTestMethod(Object, Method) afterTestMethod()}
|
||||
* on the supplied {@link TestContextManager}.
|
||||
*
|
||||
* @see #evaluate()
|
||||
* @see RunBeforeTestMethodCallbacks
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class RunAfterTestMethodCallbacks extends Statement {
|
||||
|
||||
private final Statement next;
|
||||
|
||||
private final Object testInstance;
|
||||
|
||||
private final Method testMethod;
|
||||
|
||||
private final TestContextManager testContextManager;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new <code>RunAfterTestMethodCallbacks</code> statement.
|
||||
*
|
||||
* @param next the next <code>Statement</code> in the execution chain
|
||||
* @param testInstance the current test instance (never <code>null</code>)
|
||||
* @param testMethod the test method which has just been executed on the
|
||||
* test instance
|
||||
* @param testContextManager the TestContextManager upon which to call
|
||||
* <code>afterTestMethod()</code>
|
||||
*/
|
||||
public RunAfterTestMethodCallbacks(Statement next, Object testInstance, Method testMethod,
|
||||
TestContextManager testContextManager) {
|
||||
this.next = next;
|
||||
this.testInstance = testInstance;
|
||||
this.testMethod = testMethod;
|
||||
this.testContextManager = testContextManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the next {@link Statement} in the execution chain (typically an
|
||||
* instance of {@link org.junit.internal.runners.statements.RunAfters
|
||||
* RunAfters}), catching any exceptions thrown, and then calls
|
||||
* {@link TestContextManager#afterTestMethod(Object, Method)} with the first
|
||||
* caught exception (if any). If the call to <code>afterTestMethod()</code>
|
||||
* throws an exception, it will also be tracked. Multiple exceptions will be
|
||||
* combined into a {@link MultipleFailureException}.
|
||||
*/
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
Throwable testException = null;
|
||||
List<Throwable> errors = new ArrayList<Throwable>();
|
||||
try {
|
||||
this.next.evaluate();
|
||||
}
|
||||
catch (Throwable e) {
|
||||
testException = e;
|
||||
errors.add(e);
|
||||
}
|
||||
|
||||
try {
|
||||
this.testContextManager.afterTestMethod(this.testInstance, this.testMethod, testException);
|
||||
}
|
||||
catch (Exception e) {
|
||||
errors.add(e);
|
||||
}
|
||||
|
||||
if (errors.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (errors.size() == 1) {
|
||||
throw errors.get(0);
|
||||
}
|
||||
throw new MultipleFailureException(errors);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.junit4.statements;
|
||||
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.springframework.test.context.TestContextManager;
|
||||
|
||||
/**
|
||||
* <code>RunBeforeTestClassCallbacks</code> is a custom JUnit 4.5+
|
||||
* {@link Statement} which allows the <em>Spring TestContext Framework</em> to
|
||||
* be plugged into the JUnit execution chain by calling
|
||||
* {@link TestContextManager#beforeTestClass() beforeTestClass()} on the
|
||||
* supplied {@link TestContextManager}.
|
||||
*
|
||||
* @see #evaluate()
|
||||
* @see RunAfterTestMethodCallbacks
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
public class RunBeforeTestClassCallbacks extends Statement {
|
||||
|
||||
private final Statement next;
|
||||
|
||||
private final TestContextManager testContextManager;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new <code>RunBeforeTestClassCallbacks</code> statement.
|
||||
*
|
||||
* @param next the next <code>Statement</code> in the execution chain
|
||||
* @param testContextManager the TestContextManager upon which to call
|
||||
* <code>beforeTestClass()</code>
|
||||
*/
|
||||
public RunBeforeTestClassCallbacks(Statement next, TestContextManager testContextManager) {
|
||||
this.next = next;
|
||||
this.testContextManager = testContextManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link TestContextManager#beforeTestClass()} and then invokes the
|
||||
* next {@link Statement} in the execution chain (typically an instance of
|
||||
* {@link org.junit.internal.runners.statements.RunBefores RunBefores}).
|
||||
*/
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
this.testContextManager.beforeTestClass();
|
||||
this.next.evaluate();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.junit4.statements;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.springframework.test.context.TestContextManager;
|
||||
|
||||
/**
|
||||
* <code>RunBeforeTestMethodCallbacks</code> is a custom JUnit 4.5+
|
||||
* {@link Statement} which allows the <em>Spring TestContext Framework</em> to
|
||||
* be plugged into the JUnit execution chain by calling
|
||||
* {@link TestContextManager#beforeTestMethod(Object, Method)
|
||||
* beforeTestMethod()} on the supplied {@link TestContextManager}.
|
||||
*
|
||||
* @see #evaluate()
|
||||
* @see RunAfterTestMethodCallbacks
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
public class RunBeforeTestMethodCallbacks extends Statement {
|
||||
|
||||
private final Statement next;
|
||||
|
||||
private final Object testInstance;
|
||||
|
||||
private final Method testMethod;
|
||||
|
||||
private final TestContextManager testContextManager;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new <code>RunBeforeTestMethodCallbacks</code> statement.
|
||||
*
|
||||
* @param next the next <code>Statement</code> in the execution chain
|
||||
* @param testInstance the current test instance (never <code>null</code>)
|
||||
* @param testMethod the test method which is about to be executed on the
|
||||
* test instance
|
||||
* @param testContextManager the TestContextManager upon which to call
|
||||
* <code>beforeTestMethod()</code>
|
||||
*/
|
||||
public RunBeforeTestMethodCallbacks(Statement next, Object testInstance, Method testMethod,
|
||||
TestContextManager testContextManager) {
|
||||
this.next = next;
|
||||
this.testInstance = testInstance;
|
||||
this.testMethod = testMethod;
|
||||
this.testContextManager = testContextManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link TestContextManager#beforeTestMethod(Object, Method)} and
|
||||
* then invokes the next {@link Statement} in the execution chain (typically
|
||||
* an instance of {@link org.junit.internal.runners.statements.RunBefores
|
||||
* RunBefores}).
|
||||
*/
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
this.testContextManager.beforeTestMethod(this.testInstance, this.testMethod);
|
||||
this.next.evaluate();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.junit4.statements;
|
||||
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.springframework.test.annotation.Timed;
|
||||
|
||||
/**
|
||||
* <code>SpringFailOnTimeout</code> is a custom JUnit 4.5+ {@link Statement}
|
||||
* which adds support for Spring's {@link Timed @Timed} annotation by throwing
|
||||
* an exception if the next statement in the execution chain takes more than the
|
||||
* specified number of milliseconds.
|
||||
*
|
||||
* @see #evaluate()
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
public class SpringFailOnTimeout extends Statement {
|
||||
|
||||
private final Statement next;
|
||||
|
||||
private final long timeout;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new <code>SpringFailOnTimeout</code> statement.
|
||||
*
|
||||
* @param next the next <code>Statement</code> in the execution chain
|
||||
* @param timeout the configured <code>timeout</code> for the current test
|
||||
* @see Timed#millis()
|
||||
*/
|
||||
public SpringFailOnTimeout(Statement next, long timeout) {
|
||||
this.next = next;
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the next {@link Statement statement} in the execution chain
|
||||
* (typically an instance of
|
||||
* {@link org.junit.internal.runners.statements.InvokeMethod InvokeMethod}
|
||||
* or {@link org.junit.internal.runners.statements.ExpectException
|
||||
* ExpectException}) and throws an exception if the next
|
||||
* <code>statement</code> takes more than the specified <code>timeout</code>
|
||||
* .
|
||||
*/
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
long startTime = System.currentTimeMillis();
|
||||
try {
|
||||
this.next.evaluate();
|
||||
}
|
||||
finally {
|
||||
long elapsed = System.currentTimeMillis() - startTime;
|
||||
if (elapsed > this.timeout) {
|
||||
throw new TimeoutException(String.format("Test took %s ms; limit was %s ms.", elapsed, this.timeout));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.junit4.statements;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.springframework.test.annotation.Repeat;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* <code>SpringRepeat</code> is a custom JUnit 4.5+ {@link Statement} which adds
|
||||
* support for Spring's {@link Repeat @Repeat} annotation by repeating the
|
||||
* test for the specified number of times.
|
||||
*
|
||||
* @see #evaluate()
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
public class SpringRepeat extends Statement {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(SpringRepeat.class);
|
||||
|
||||
private final Statement next;
|
||||
|
||||
private final Method testMethod;
|
||||
|
||||
private final int repeat;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new <code>SpringRepeat</code> statement.
|
||||
*
|
||||
* @param next the next <code>Statement</code> in the execution chain
|
||||
* @param testMethod the current test method
|
||||
* @param repeat the configured repeat count for the current test method
|
||||
* @see Repeat#value()
|
||||
*/
|
||||
public SpringRepeat(Statement next, Method testMethod, int repeat) {
|
||||
this.next = next;
|
||||
this.testMethod = testMethod;
|
||||
this.repeat = Math.max(1, repeat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the next {@link Statement statement} in the execution chain for
|
||||
* the specified repeat count.
|
||||
*/
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
for (int i = 0; i < this.repeat; i++) {
|
||||
if (this.repeat > 1 && logger.isInfoEnabled()) {
|
||||
logger.info(String.format("Repetition %d of test %s#%s()", (i + 1),
|
||||
ClassUtils.getShortName(this.testMethod.getDeclaringClass()), this.testMethod.getName()));
|
||||
}
|
||||
this.next.evaluate();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>JUnit 4.5 based <code>statements</code> used in the <em>Spring TestContext Framework</em>.</p>
|
||||
*
|
||||
*/
|
||||
package org.springframework.test.context.junit4.statements;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* <p>This package contains the <em>Spring TestContext Framework</em>
|
||||
* which provides annotation-driven unit and integration testing support
|
||||
* that is agnostic of the actual testing framework in use. The same
|
||||
* techniques and annotation-based configuration used in, for example, a
|
||||
* JUnit 3.8 environment can also be applied to tests written with JUnit
|
||||
* 4.5+, TestNG, etc.</p>
|
||||
*
|
||||
* <p>In addition to providing generic and extensible testing
|
||||
* infrastructure, the Spring TestContext Framework provides out-of-the-box
|
||||
* support for Spring-specific integration testing functionality such as
|
||||
* context management and caching, dependency injection of test fixtures,
|
||||
* and transactional test management with default rollback semantics.</p>
|
||||
*/
|
||||
|
||||
package org.springframework.test.context;
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.support.ResourcePatternUtils;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.test.context.SmartContextLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Abstract application context loader that provides a basis for all concrete
|
||||
* implementations of the {@link ContextLoader} SPI. Provides a
|
||||
* <em>Template Method</em> based approach for {@link #processLocations processing}
|
||||
* resource locations.
|
||||
*
|
||||
* <p>As of Spring 3.1, <code>AbstractContextLoader</code> also provides a basis
|
||||
* for all concrete implementations of the {@link SmartContextLoader} SPI. For
|
||||
* backwards compatibility with the {@code ContextLoader} SPI,
|
||||
* {@link #processContextConfiguration()} delegates to {@link #processLocations()}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see #generateDefaultLocations
|
||||
* @see #modifyLocations
|
||||
*/
|
||||
public abstract class AbstractContextLoader implements SmartContextLoader {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(AbstractContextLoader.class);
|
||||
|
||||
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
private static final String SLASH = "/";
|
||||
|
||||
|
||||
// --- SmartContextLoader -----------------------------------------------
|
||||
|
||||
/**
|
||||
* For backwards compatibility with the {@link ContextLoader} SPI, the
|
||||
* default implementation simply delegates to {@link #processLocations()},
|
||||
* passing it the {@link ContextConfigurationAttributes#getDeclaringClass()
|
||||
* declaring class} and {@link ContextConfigurationAttributes#getLocations()
|
||||
* resource locations} retrieved from the supplied
|
||||
* {@link ContextConfigurationAttributes configuration attributes}. The
|
||||
* processed locations are then
|
||||
* {@link ContextConfigurationAttributes#setLocations(String[]) set} in
|
||||
* the supplied configuration attributes.
|
||||
* <p>Can be overridden in subclasses — for example, to process
|
||||
* configuration classes instead of resource locations.
|
||||
* @since 3.1
|
||||
* @see #processLocations()
|
||||
*/
|
||||
public void processContextConfiguration(ContextConfigurationAttributes configAttributes) {
|
||||
String[] processedLocations = processLocations(configAttributes.getDeclaringClass(),
|
||||
configAttributes.getLocations());
|
||||
configAttributes.setLocations(processedLocations);
|
||||
}
|
||||
|
||||
// --- ContextLoader -------------------------------------------------------
|
||||
|
||||
/**
|
||||
* If the supplied <code>locations</code> are <code>null</code> or
|
||||
* <em>empty</em> and {@link #isGenerateDefaultLocations()} returns
|
||||
* <code>true</code>, default locations will be
|
||||
* {@link #generateDefaultLocations(Class) generated} for the specified
|
||||
* {@link Class class} and the configured
|
||||
* {@link #getResourceSuffix() resource suffix}; otherwise, the supplied
|
||||
* <code>locations</code> will be {@link #modifyLocations modified} if
|
||||
* necessary and returned.
|
||||
* @param clazz the class with which the locations are associated: to be
|
||||
* used when generating default locations
|
||||
* @param locations the unmodified locations to use for loading the
|
||||
* application context (can be <code>null</code> or empty)
|
||||
* @return a processed array of application context resource locations
|
||||
* @since 2.5
|
||||
* @see #isGenerateDefaultLocations()
|
||||
* @see #generateDefaultLocations()
|
||||
* @see #modifyLocations()
|
||||
* @see org.springframework.test.context.ContextLoader#processLocations()
|
||||
* @see #processContextConfiguration()
|
||||
*/
|
||||
public final String[] processLocations(Class<?> clazz, String... locations) {
|
||||
return (ObjectUtils.isEmpty(locations) && isGenerateDefaultLocations()) ? generateDefaultLocations(clazz)
|
||||
: modifyLocations(clazz, locations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the default classpath resource locations array based on the
|
||||
* supplied class.
|
||||
* <p>For example, if the supplied class is <code>com.example.MyTest</code>,
|
||||
* the generated locations will contain a single string with a value of
|
||||
* "classpath:/com/example/MyTest<code><suffix></code>",
|
||||
* where <code><suffix></code> is the value of the
|
||||
* {@link #getResourceSuffix() resource suffix} string.
|
||||
* <p>As of Spring 3.1, the implementation of this method adheres to the
|
||||
* contract defined in the {@link SmartContextLoader} SPI. Specifically,
|
||||
* this method will <em>preemptively</em> verify that the generated default
|
||||
* location actually exists. If it does not exist, this method will log a
|
||||
* warning and return an empty array.
|
||||
* <p>Subclasses can override this method to implement a different
|
||||
* <em>default location generation</em> strategy.
|
||||
* @param clazz the class for which the default locations are to be generated
|
||||
* @return an array of default application context resource locations
|
||||
* @since 2.5
|
||||
* @see #getResourceSuffix()
|
||||
*/
|
||||
protected String[] generateDefaultLocations(Class<?> clazz) {
|
||||
Assert.notNull(clazz, "Class must not be null");
|
||||
String suffix = getResourceSuffix();
|
||||
Assert.hasText(suffix, "Resource suffix must not be empty");
|
||||
String resourcePath = SLASH + ClassUtils.convertClassNameToResourcePath(clazz.getName()) + suffix;
|
||||
String prefixedResourcePath = ResourceUtils.CLASSPATH_URL_PREFIX + resourcePath;
|
||||
ClassPathResource classPathResource = new ClassPathResource(resourcePath, clazz);
|
||||
|
||||
if (classPathResource.exists()) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(String.format("Detected default resource location \"%s\" for test class [%s].",
|
||||
prefixedResourcePath, clazz.getName()));
|
||||
}
|
||||
return new String[] { prefixedResourcePath };
|
||||
}
|
||||
|
||||
// else
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(String.format("Could not detect default resource locations for test class [%s]: "
|
||||
+ "%s does not exist.", clazz.getName(), classPathResource));
|
||||
}
|
||||
return EMPTY_STRING_ARRAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a modified version of the supplied locations array and return it.
|
||||
* <p>A plain path — for example, "context.xml" — will
|
||||
* be treated as a classpath resource that is relative to the package in which
|
||||
* the specified class is defined. A path starting with a slash is treated
|
||||
* as an absolute classpath location, for example:
|
||||
* "/org/springframework/whatever/foo.xml". A path which
|
||||
* references a URL (e.g., a path prefixed with
|
||||
* {@link ResourceUtils#CLASSPATH_URL_PREFIX classpath:},
|
||||
* {@link ResourceUtils#FILE_URL_PREFIX file:}, <code>http:</code>,
|
||||
* etc.) will be added to the results unchanged.
|
||||
* <p>Subclasses can override this method to implement a different
|
||||
* <em>location modification</em> strategy.
|
||||
* @param clazz the class with which the locations are associated
|
||||
* @param locations the resource locations to be modified
|
||||
* @return an array of modified application context resource locations
|
||||
* @since 2.5
|
||||
*/
|
||||
protected String[] modifyLocations(Class<?> clazz, String... locations) {
|
||||
String[] modifiedLocations = new String[locations.length];
|
||||
for (int i = 0; i < locations.length; i++) {
|
||||
String path = locations[i];
|
||||
if (path.startsWith(SLASH)) {
|
||||
modifiedLocations[i] = ResourceUtils.CLASSPATH_URL_PREFIX + path;
|
||||
}
|
||||
else if (!ResourcePatternUtils.isUrl(path)) {
|
||||
modifiedLocations[i] = ResourceUtils.CLASSPATH_URL_PREFIX + SLASH
|
||||
+ StringUtils.cleanPath(ClassUtils.classPackageAsResourcePath(clazz) + SLASH + path);
|
||||
}
|
||||
else {
|
||||
modifiedLocations[i] = StringUtils.cleanPath(path);
|
||||
}
|
||||
}
|
||||
return modifiedLocations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not <em>default</em> resource locations should be
|
||||
* generated if the <code>locations</code> provided to
|
||||
* {@link #processLocations()} are <code>null</code> or empty.
|
||||
* <p>As of Spring 3.1, the semantics of this method have been overloaded
|
||||
* to include detection of either default resource locations or default
|
||||
* configuration classes. Consequently, this method can also be used to
|
||||
* determine whether or not <em>default</em> configuration classes should be
|
||||
* detected if the <code>classes</code> present in the
|
||||
* {@link ContextConfigurationAttributes configuration attributes} supplied
|
||||
* to {@link #processContextConfiguration()} are <code>null</code> or empty.
|
||||
* <p>Can be overridden by subclasses to change the default behavior.
|
||||
* @return always <code>true</code> by default
|
||||
* @since 2.5
|
||||
*/
|
||||
protected boolean isGenerateDefaultLocations() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the suffix to append to {@link ApplicationContext} resource
|
||||
* locations when generating default locations.
|
||||
* <p>Must be implemented by subclasses.
|
||||
* @return the resource suffix; should not be <code>null</code> or empty
|
||||
* @since 2.5
|
||||
* @see #generateDefaultLocations()
|
||||
*/
|
||||
protected abstract String getResourceSuffix();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReader;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Abstract, generic extension of {@link AbstractContextLoader} that loads a
|
||||
* {@link GenericApplicationContext}.
|
||||
*
|
||||
* <ul>
|
||||
* <li>If instances of concrete subclasses are invoked via the
|
||||
* {@link org.springframework.test.context.ContextLoader ContextLoader} SPI, the
|
||||
* context will be loaded from the <em>locations</em> provided to
|
||||
* {@link #loadContext(String...)}.</li>
|
||||
* <li>If instances of concrete subclasses are invoked via the
|
||||
* {@link org.springframework.test.context.SmartContextLoader SmartContextLoader}
|
||||
* SPI, the context will be loaded from the {@link MergedContextConfiguration}
|
||||
* provided to {@link #loadContext(MergedContextConfiguration)}. In such cases, a
|
||||
* <code>SmartContextLoader</code> will decide whether to load the context from
|
||||
* <em>locations</em> or
|
||||
* {@link org.springframework.context.annotation.Configuration configuration classes}.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Concrete subclasses must provide an appropriate implementation of
|
||||
* {@link #createBeanDefinitionReader createBeanDefinitionReader()},
|
||||
* potentially overriding {@link #loadBeanDefinitions loadBeanDefinitions()}
|
||||
* as well.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see #loadContext(MergedContextConfiguration)
|
||||
* @see #loadContext(String...)
|
||||
*/
|
||||
public abstract class AbstractGenericContextLoader extends AbstractContextLoader {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(AbstractGenericContextLoader.class);
|
||||
|
||||
|
||||
/**
|
||||
* Load a Spring ApplicationContext from the supplied {@link MergedContextConfiguration}.
|
||||
* <p>Implementation details:
|
||||
* <ul>
|
||||
* <li>Creates a {@link GenericApplicationContext} instance.</li>
|
||||
* <li>Sets the <em>active bean definition profiles</em> from the supplied
|
||||
* <code>MergedContextConfiguration</code> in the
|
||||
* {@link org.springframework.core.env.Environment Environment} of the context.</li>
|
||||
* <li>Calls {@link #prepareContext()} to allow for customizing the context
|
||||
* before bean definitions are loaded.</li>
|
||||
* <li>Calls {@link #customizeBeanFactory()} to allow for customizing the
|
||||
* context's <code>DefaultListableBeanFactory</code>.</li>
|
||||
* <li>Delegates to {@link #loadBeanDefinitions()} to populate the context
|
||||
* from the configuration locations or classes in the supplied
|
||||
* <code>MergedContextConfiguration</code>.</li>
|
||||
* <li>Delegates to {@link AnnotationConfigUtils} for
|
||||
* {@link AnnotationConfigUtils#registerAnnotationConfigProcessors registering}
|
||||
* annotation configuration processors.</li>
|
||||
* <li>Calls {@link #customizeContext()} to allow for customizing the context
|
||||
* before it is refreshed.</li>
|
||||
* <li>{@link ConfigurableApplicationContext#refresh Refreshes} the
|
||||
* context and registers a JVM shutdown hook for it.</li>
|
||||
* </ul>
|
||||
* @return a new application context
|
||||
* @see org.springframework.test.context.SmartContextLoader#loadContext(MergedContextConfiguration)
|
||||
* @see GenericApplicationContext
|
||||
* @since 3.1
|
||||
*/
|
||||
public final ConfigurableApplicationContext loadContext(MergedContextConfiguration mergedConfig) throws Exception {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Loading ApplicationContext for merged context configuration [%s].",
|
||||
mergedConfig));
|
||||
}
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
context.getEnvironment().setActiveProfiles(mergedConfig.getActiveProfiles());
|
||||
prepareContext(context);
|
||||
customizeBeanFactory(context.getDefaultListableBeanFactory());
|
||||
loadBeanDefinitions(context, mergedConfig);
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
||||
customizeContext(context);
|
||||
context.refresh();
|
||||
context.registerShutdownHook();
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a Spring ApplicationContext from the supplied <code>locations</code>.
|
||||
* <p>Implementation details:
|
||||
* <ul>
|
||||
* <li>Creates a {@link GenericApplicationContext} instance.</li>
|
||||
* <li>Calls {@link #prepareContext()} to allow for customizing the context
|
||||
* before bean definitions are loaded.</li>
|
||||
* <li>Calls {@link #customizeBeanFactory()} to allow for customizing the
|
||||
* context's <code>DefaultListableBeanFactory</code>.</li>
|
||||
* <li>Delegates to {@link #createBeanDefinitionReader()} to create a
|
||||
* {@link BeanDefinitionReader} which is then used to populate the context
|
||||
* from the specified config locations.</li>
|
||||
* <li>Delegates to {@link AnnotationConfigUtils} for
|
||||
* {@link AnnotationConfigUtils#registerAnnotationConfigProcessors registering}
|
||||
* annotation configuration processors.</li>
|
||||
* <li>Calls {@link #customizeContext()} to allow for customizing the context
|
||||
* before it is refreshed.</li>
|
||||
* <li>{@link ConfigurableApplicationContext#refresh Refreshes} the
|
||||
* context and registers a JVM shutdown hook for it.</li>
|
||||
* </ul>
|
||||
* <p><b>Note</b>: this method does not provide a means to set active bean definition
|
||||
* profiles for the loaded context. See {@link #loadContext(MergedContextConfiguration)}
|
||||
* for an alternative.
|
||||
* @return a new application context
|
||||
* @see org.springframework.test.context.ContextLoader#loadContext
|
||||
* @see GenericApplicationContext
|
||||
* @see #loadContext(MergedContextConfiguration)
|
||||
* @since 2.5
|
||||
*/
|
||||
public final ConfigurableApplicationContext loadContext(String... locations) throws Exception {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Loading ApplicationContext for locations [%s].",
|
||||
StringUtils.arrayToCommaDelimitedString(locations)));
|
||||
}
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
prepareContext(context);
|
||||
customizeBeanFactory(context.getDefaultListableBeanFactory());
|
||||
createBeanDefinitionReader(context).loadBeanDefinitions(locations);
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
||||
customizeContext(context);
|
||||
context.refresh();
|
||||
context.registerShutdownHook();
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the {@link GenericApplicationContext} created by this <code>ContextLoader</code>.
|
||||
* Called <i>before</i> bean definitions are read.
|
||||
* <p>The default implementation is empty. Can be overridden in subclasses to
|
||||
* customize <code>GenericApplicationContext</code>'s standard settings.
|
||||
* @param context the context that should be prepared
|
||||
* @see #loadContext(MergedContextConfiguration)
|
||||
* @see #loadContext(String...)
|
||||
* @see GenericApplicationContext#setAllowBeanDefinitionOverriding
|
||||
* @see GenericApplicationContext#setResourceLoader
|
||||
* @see GenericApplicationContext#setId
|
||||
* @since 2.5
|
||||
*/
|
||||
protected void prepareContext(GenericApplicationContext context) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize the internal bean factory of the ApplicationContext created by
|
||||
* this <code>ContextLoader</code>.
|
||||
* <p>The default implementation is empty but can be overridden in subclasses
|
||||
* to customize <code>DefaultListableBeanFactory</code>'s standard settings.
|
||||
* @param beanFactory the bean factory created by this <code>ContextLoader</code>
|
||||
* @see #loadContext(MergedContextConfiguration)
|
||||
* @see #loadContext(String...)
|
||||
* @see DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
|
||||
* @see DefaultListableBeanFactory#setAllowEagerClassLoading
|
||||
* @see DefaultListableBeanFactory#setAllowCircularReferences
|
||||
* @see DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping
|
||||
* @since 2.5
|
||||
*/
|
||||
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Load bean definitions into the supplied {@link GenericApplicationContext context}
|
||||
* from the configuration locations or classes in the supplied
|
||||
* <code>MergedContextConfiguration</code>.</li>
|
||||
* <p>The default implementation delegates to the {@link BeanDefinitionReader}
|
||||
* returned by {@link #createBeanDefinitionReader()} to
|
||||
* {@link BeanDefinitionReader#loadBeanDefinitions(String) load} the
|
||||
* bean definitions.
|
||||
* <p>Subclasses must provide an appropriate implementation of
|
||||
* {@link #createBeanDefinitionReader()}. Alternatively subclasses may
|
||||
* provide a <em>no-op</em> implementation of {@code createBeanDefinitionReader()}
|
||||
* and override this method to provide a custom strategy for loading or
|
||||
* registering bean definitions.
|
||||
* @param context the context into which the bean definitions should be loaded
|
||||
* @param mergedConfig the merged context configuration
|
||||
* @see #loadContext(MergedContextConfiguration)
|
||||
* @since 3.1
|
||||
*/
|
||||
protected void loadBeanDefinitions(GenericApplicationContext context, MergedContextConfiguration mergedConfig) {
|
||||
createBeanDefinitionReader(context).loadBeanDefinitions(mergedConfig.getLocations());
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for creating a new {@link BeanDefinitionReader} for loading
|
||||
* bean definitions into the supplied {@link GenericApplicationContext context}.
|
||||
* @param context the context for which the <code>BeanDefinitionReader</code>
|
||||
* should be created
|
||||
* @return a <code>BeanDefinitionReader</code> for the supplied context
|
||||
* @see #loadContext(String...)
|
||||
* @see #loadBeanDefinitions
|
||||
* @see BeanDefinitionReader
|
||||
* @since 2.5
|
||||
*/
|
||||
protected abstract BeanDefinitionReader createBeanDefinitionReader(GenericApplicationContext context);
|
||||
|
||||
/**
|
||||
* Customize the {@link GenericApplicationContext} created by this
|
||||
* <code>ContextLoader</code> <i>after</i> bean definitions have been
|
||||
* loaded into the context but <i>before</i> the context is refreshed.
|
||||
* <p>The default implementation is empty but can be overridden in subclasses
|
||||
* to customize the application context.
|
||||
* @param context the newly created application context
|
||||
* @see #loadContext(MergedContextConfiguration)
|
||||
* @see #loadContext(String...)
|
||||
* @since 2.5
|
||||
*/
|
||||
protected void customizeContext(GenericApplicationContext context) {
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user