Initial import of test module
This commit is contained in:
parent
af47a8b79b
commit
995a323a6f
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="org.springframework.test">
|
||||
<property file="${basedir}/../build.properties"/>
|
||||
<import file="${basedir}/../build-spring-framework/package-bundle.xml"/>
|
||||
<import file="${basedir}/../spring-build/standard/default.xml"/>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml-stylesheet type="text/xsl" href="http://ivyrep.jayasoft.org/ivy-doc.xsl"?>
|
||||
<ivy-module
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="http://incubator.apache.org/ivy/schemas/ivy.xsd"
|
||||
version="1.3">
|
||||
|
||||
<info organisation="org.springframework" module="${ant.project.name}">
|
||||
<license name="Apache 2.0" url="http://www.apache.org/licenses/LICENSE-2.0"/>
|
||||
</info>
|
||||
|
||||
<configurations>
|
||||
<include file="${spring.build.dir}/common/default-ivy-configurations.xml"/>
|
||||
</configurations>
|
||||
|
||||
<publications>
|
||||
<artifact name="${ant.project.name}"/>
|
||||
<artifact name="${ant.project.name}-sources" type="src" ext="jar"/>
|
||||
</publications>
|
||||
|
||||
<dependencies>
|
||||
<dependency org="javax.activation" name="com.springsource.javax.activation" rev="1.1.0" conf="provided->compile"/>
|
||||
<dependency org="javax.persistence" name="com.springsource.javax.persistence" rev="1.0.0" conf="provided->compile"/>
|
||||
<dependency org="javax.portlet" name="com.springsource.javax.portlet" rev="1.0.0" conf="provided->compile"/>
|
||||
<dependency org="javax.servlet" name="com.springsource.javax.servlet" rev="2.4.0" conf="provided->compile"/>
|
||||
<dependency org="javax.servlet" name="com.springsource.javax.servlet.jsp" rev="2.1.0" conf="provided->compile"/>
|
||||
<dependency org="org.junit" name="com.springsource.junit" rev="3.8.2" conf="optional->compile"/>
|
||||
<dependency org="org.junit" name="com.springsource.org.junit" rev="4.4.0" conf="optional->compile"/>
|
||||
<dependency org="org.apache.commons" name="com.springsource.org.apache.commons.logging" rev="1.1.1" conf="compile->compile"/>
|
||||
<dependency org="org.apache.taglibs" name="com.springsource.org.apache.taglibs.standard" rev="1.1.2" conf="optional->compile"/>
|
||||
<dependency org="org.aspectj" name="com.springsource.org.aspectj.weaver" rev="1.5.4" conf="optional->compile"/>
|
||||
<dependency org="org.testng" name="com.springsource.org.testng" rev="5.8.0" conf="optional->compile"/>
|
||||
<dependency org="org.springframework" name="org.springframework.beans" rev="latest.integration" conf="optional->compile"/>
|
||||
<dependency org="org.springframework" name="org.springframework.context" rev="latest.integration" conf="optional->compile"/>
|
||||
<dependency org="org.springframework" name="org.springframework.core" rev="latest.integration" conf="optional->compile"/>
|
||||
<dependency org="org.springframework" name="org.springframework.instrument.classloading" rev="latest.integration" conf="optional->compile"/>
|
||||
<dependency org="org.springframework" name="org.springframework.jdbc" rev="latest.integration" conf="optional->compile"/>
|
||||
<dependency org="org.springframework" name="org.springframework.orm" rev="latest.integration" conf="optional->compile"/>
|
||||
<dependency org="org.springframework" name="org.springframework.transaction" rev="latest.integration" conf="optional->compile"/>
|
||||
<dependency org="org.springframework" name="org.springframework.web" rev="latest.integration" conf="optional->compile"/>
|
||||
<dependency org="org.springframework" name="org.springframework.web.portlet" rev="latest.integration" conf="optional->compile"/>
|
||||
<dependency org="org.springframework" name="org.springframework.web.servlet" rev="latest.integration" conf="optional->compile"/>
|
||||
</dependencies>
|
||||
|
||||
</ivy-module>
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>org.springframework.parent</artifactId>
|
||||
<version>3.0-M1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>org.springframework.core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Framework: Core</name>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.log4j</groupId>
|
||||
<artifactId>com.springsource.org.apache.log4j</artifactId>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>com.springsource.org.apache.commons.collections</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>com.springsource.org.aspectj.weaver</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.objectweb.asm</groupId>
|
||||
<artifactId>com.springsource.org.objectweb.asm</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.objectweb.asm</groupId>
|
||||
<artifactId>com.springsource.org.objectweb.asm.commons</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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.Map;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
|
||||
import org.springframework.core.CollectionFactory;
|
||||
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 jndiObjects = CollectionFactory.createConcurrentMapIfPossible(16);
|
||||
|
||||
|
||||
/**
|
||||
* 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,349 @@
|
|||
/*
|
||||
* 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 boundObjects;
|
||||
|
||||
private final Hashtable environment = new Hashtable();
|
||||
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 boundObjects, Hashtable environment) {
|
||||
this.root = root;
|
||||
this.boundObjects = boundObjects;
|
||||
if (environment != null) {
|
||||
this.environment.putAll(environment);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Actual implementations of Context methods follow
|
||||
|
||||
public NamingEnumeration list(String root) throws NamingException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Listing name/class pairs under [" + root + "]");
|
||||
}
|
||||
return new NameClassPairEnumeration(this, root);
|
||||
}
|
||||
|
||||
public NamingEnumeration 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 (Iterator it = this.boundObjects.keySet().iterator(); it.hasNext();) {
|
||||
String boundName = (String) it.next();
|
||||
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 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 list(Name name) throws NamingException {
|
||||
throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]");
|
||||
}
|
||||
|
||||
public NamingEnumeration 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 implements NamingEnumeration {
|
||||
|
||||
private Iterator iterator;
|
||||
|
||||
private AbstractNamingEnumeration(SimpleNamingContext context, String proot) throws NamingException {
|
||||
if (!"".equals(proot) && !proot.endsWith("/")) {
|
||||
proot = proot + "/";
|
||||
}
|
||||
String root = context.root + proot;
|
||||
Map contents = new HashMap();
|
||||
Iterator it = context.boundObjects.keySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
String boundName = (String) it.next();
|
||||
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 Object createObject(String strippedName, Object obj);
|
||||
|
||||
public boolean hasMore() {
|
||||
return this.iterator.hasNext();
|
||||
}
|
||||
|
||||
public Object next() {
|
||||
return this.iterator.next();
|
||||
}
|
||||
|
||||
public boolean hasMoreElements() {
|
||||
return this.iterator.hasNext();
|
||||
}
|
||||
|
||||
public Object nextElement() {
|
||||
return this.iterator.next();
|
||||
}
|
||||
|
||||
public void close() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class NameClassPairEnumeration extends AbstractNamingEnumeration {
|
||||
|
||||
private NameClassPairEnumeration(SimpleNamingContext context, String root) throws NamingException {
|
||||
super(context, root);
|
||||
}
|
||||
|
||||
protected Object createObject(String strippedName, Object obj) {
|
||||
return new NameClassPair(strippedName, obj.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class BindingEnumeration extends AbstractNamingEnumeration {
|
||||
|
||||
private BindingEnumeration(SimpleNamingContext context, String root) throws NamingException {
|
||||
super(context, root);
|
||||
}
|
||||
|
||||
protected Object createObject(String strippedName, Object obj) {
|
||||
return new Binding(strippedName, obj);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
* 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.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 assigment to the activated variable.
|
||||
builder.activate();
|
||||
}
|
||||
return activated;
|
||||
}
|
||||
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final Hashtable boundObjects = new Hashtable();
|
||||
|
||||
|
||||
/**
|
||||
* 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 contexz 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() {
|
||||
public Context getInitialContext(Hashtable environment) {
|
||||
return new SimpleNamingContext("", boundObjects, environment);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
The simplest implementation of the JNDI SPI that could possibly work.
|
||||
|
||||
<p>Useful for setting up a simple JNDI environment for test suites
|
||||
or standalone applications. If e.g. JDBC DataSources get bound to the
|
||||
same JNDI names as within a J2EE container, both application code and
|
||||
configuration can me reused without changes.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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.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,72 @@
|
|||
/*
|
||||
* 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.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,85 @@
|
|||
/*
|
||||
* 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.mock.web;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
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 values = new LinkedList();
|
||||
|
||||
|
||||
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 getValues() {
|
||||
return Collections.unmodifiableList(this.values);
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return (!this.values.isEmpty() ? this.values.get(0) : 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 headers, String name) {
|
||||
Assert.notNull(name, "Header name must not be null");
|
||||
for (Iterator it = headers.keySet().iterator(); it.hasNext();) {
|
||||
String headerName = (String) it.next();
|
||||
if (headerName.equalsIgnoreCase(name)) {
|
||||
return (HeaderValueHolder) headers.get(headerName);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* 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.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,92 @@
|
|||
/*
|
||||
* Copyright 2002-2006 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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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-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.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 usefol 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,103 @@
|
|||
/*
|
||||
* 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.mock.web;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.Properties;
|
||||
|
||||
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 usefol 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 Properties initParameters = new Properties();
|
||||
|
||||
|
||||
/**
|
||||
* 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.setProperty(name, value);
|
||||
}
|
||||
|
||||
public String getInitParameter(String name) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
return this.initParameters.getProperty(name);
|
||||
}
|
||||
|
||||
public Enumeration getInitParameterNames() {
|
||||
return this.initParameters.keys();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,858 @@
|
|||
/*
|
||||
* 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.web;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
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.Hashtable;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
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.ServletInputStream;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.servlet.http.HttpServletRequest}
|
||||
* interface. Supports the Servlet 2.4 API level.
|
||||
*
|
||||
* <p>Used for testing the web framework; also useful for testing
|
||||
* application controllers.
|
||||
*
|
||||
* @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 boolean active = true;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// ServletRequest properties
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
private final Hashtable attributes = new Hashtable();
|
||||
|
||||
private String characterEncoding;
|
||||
|
||||
private byte[] content;
|
||||
|
||||
private String contentType;
|
||||
|
||||
private final Map parameters = new LinkedHashMap(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 Vector locales = new Vector();
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* The key is the lowercase header name; the value is a {@link HeaderValueHolder} object.
|
||||
*/
|
||||
private final Hashtable headers = new Hashtable();
|
||||
|
||||
private String method;
|
||||
|
||||
private String pathInfo;
|
||||
|
||||
private String contextPath = "";
|
||||
|
||||
private String queryString;
|
||||
|
||||
private String remoteUser;
|
||||
|
||||
private final Set userRoles = new HashSet();
|
||||
|
||||
private Principal userPrincipal;
|
||||
|
||||
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 getAttributeNames() {
|
||||
checkActive();
|
||||
return this.attributes.keys();
|
||||
}
|
||||
|
||||
public String getCharacterEncoding() {
|
||||
return this.characterEncoding;
|
||||
}
|
||||
|
||||
public void setCharacterEncoding(String characterEncoding) {
|
||||
this.characterEncoding = characterEncoding;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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(Map)}.
|
||||
*/
|
||||
public void setParameters(Map params) {
|
||||
Assert.notNull(params, "Parameter map must not be null");
|
||||
for (Iterator it = params.keySet().iterator(); it.hasNext();) {
|
||||
Object key = it.next();
|
||||
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 = (String[]) 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(Map)}.
|
||||
*/
|
||||
public void addParameters(Map params) {
|
||||
Assert.notNull(params, "Parameter map must not be null");
|
||||
for (Iterator it = params.keySet().iterator(); it.hasNext();) {
|
||||
Object key = it.next();
|
||||
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 = (String[]) this.parameters.get(name);
|
||||
return (arr != null && arr.length > 0 ? arr[0] : null);
|
||||
}
|
||||
|
||||
public Enumeration getParameterNames() {
|
||||
return Collections.enumeration(this.parameters.keySet());
|
||||
}
|
||||
|
||||
public String[] getParameterValues(String name) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
return (String[]) this.parameters.get(name);
|
||||
}
|
||||
|
||||
public Map 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 (Locale) this.locales.get(0);
|
||||
}
|
||||
|
||||
public Enumeration getLocales() {
|
||||
return this.locales.elements();
|
||||
}
|
||||
|
||||
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) {
|
||||
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 (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 String getHeader(String name) {
|
||||
HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
|
||||
return (header != null ? header.getValue().toString() : null);
|
||||
}
|
||||
|
||||
public Enumeration getHeaders(String name) {
|
||||
HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
|
||||
return Collections.enumeration(header != null ? header.getValues() : Collections.EMPTY_LIST);
|
||||
}
|
||||
|
||||
public Enumeration getHeaderNames() {
|
||||
return this.headers.keys();
|
||||
}
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated in favor of addUserRole
|
||||
* @see #addUserRole
|
||||
*/
|
||||
public void addRole(String role) {
|
||||
addUserRole(role);
|
||||
}
|
||||
|
||||
public void addUserRole(String role) {
|
||||
this.userRoles.add(role);
|
||||
}
|
||||
|
||||
public boolean isUserInRole(String role) {
|
||||
return this.userRoles.contains(role);
|
||||
}
|
||||
|
||||
public void setUserPrincipal(Principal userPrincipal) {
|
||||
this.userPrincipal = userPrincipal;
|
||||
}
|
||||
|
||||
public Principal getUserPrincipal() {
|
||||
return this.userPrincipal;
|
||||
}
|
||||
|
||||
public String getRequestedSessionId() {
|
||||
HttpSession session = getSession();
|
||||
return (session != null ? session.getId() : null);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,517 @@
|
|||
/*
|
||||
* 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.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.HashMap;
|
||||
import java.util.Iterator;
|
||||
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.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.servlet.http.HttpServletResponse}
|
||||
* interface. Supports the Servlet 2.4 API level.
|
||||
*
|
||||
* <p>Used for testing the web framework; also useful for testing
|
||||
* application controllers.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rod Johnson
|
||||
* @since 1.0.2
|
||||
*/
|
||||
public class MockHttpServletResponse implements HttpServletResponse {
|
||||
|
||||
public static final int DEFAULT_SERVER_PORT = 80;
|
||||
|
||||
private static final String CHARSET_PREFIX = "charset=";
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// ServletResponse properties
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
private boolean outputStreamAccessAllowed = true;
|
||||
|
||||
private boolean writerAccessAllowed = true;
|
||||
|
||||
private String characterEncoding = WebUtils.DEFAULT_CHARACTER_ENCODING;
|
||||
|
||||
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 cookies = new ArrayList();
|
||||
|
||||
/**
|
||||
* The key is the lowercase header name; the value is a {@link HeaderValueHolder} object.
|
||||
*/
|
||||
private final Map headers = new HashMap();
|
||||
|
||||
private int status = HttpServletResponse.SC_OK;
|
||||
|
||||
private String errorMessage;
|
||||
|
||||
private String redirectedUrl;
|
||||
|
||||
private String forwardedUrl;
|
||||
|
||||
private String includedUrl;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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());
|
||||
setCharacterEncoding(encoding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 (Cookie[]) this.cookies.toArray(new Cookie[this.cookies.size()]);
|
||||
}
|
||||
|
||||
public Cookie getCookie(String name) {
|
||||
Assert.notNull(name, "Cookie name must not be null");
|
||||
for (Iterator it = this.cookies.iterator(); it.hasNext();) {
|
||||
Cookie cookie = (Cookie) it.next();
|
||||
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.
|
||||
* @return the <code>Set</code> of header name <code>Strings</code>, or an empty <code>Set</code> if none
|
||||
*/
|
||||
public Set getHeaderNames() {
|
||||
return this.headers.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 getHeader(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 getHeaders(String name) {
|
||||
HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
|
||||
return (header != null ? header.getValues() : Collections.EMPTY_LIST);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, new Long(value));
|
||||
}
|
||||
|
||||
public void addDateHeader(String name, long value) {
|
||||
addHeaderValue(name, new Long(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, new Integer(value));
|
||||
}
|
||||
|
||||
public void addIntHeader(String name, int value) {
|
||||
addHeaderValue(name, new Integer(value));
|
||||
}
|
||||
|
||||
private void setHeaderValue(String name, Object value) {
|
||||
doAddHeaderValue(name, value, true);
|
||||
}
|
||||
|
||||
private void addHeaderValue(String name, Object value) {
|
||||
doAddHeaderValue(name, value, 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.includedUrl = includedUrl;
|
||||
}
|
||||
|
||||
public String getIncludedUrl() {
|
||||
return this.includedUrl;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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-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.mock.web;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
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.
|
||||
* Supports the Servlet 2.4 API level.
|
||||
*
|
||||
* <p>Used for testing the web framework; also useful for testing
|
||||
* application controllers.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rod Johnson
|
||||
* @author Mark Fisher
|
||||
* @since 1.0.2
|
||||
*/
|
||||
public class MockHttpSession implements HttpSession {
|
||||
|
||||
public static final String SESSION_COOKIE_NAME = "JSESSION";
|
||||
|
||||
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 Hashtable attributes = new Hashtable();
|
||||
|
||||
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 getAttributeNames() {
|
||||
return this.attributes.keys();
|
||||
}
|
||||
|
||||
public String[] getValueNames() {
|
||||
return (String[]) 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 it = this.attributes.entrySet().iterator(); it.hasNext();) {
|
||||
Map.Entry entry = (Map.Entry) it.next();
|
||||
String name = (String) 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 state = new HashMap();
|
||||
for (Iterator it = this.attributes.entrySet().iterator(); it.hasNext();) {
|
||||
Map.Entry entry = (Map.Entry) it.next();
|
||||
String name = (String) entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
it.remove();
|
||||
if (value instanceof Serializable) {
|
||||
state.put(name, 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
|
||||
*/
|
||||
public void deserializeState(Serializable state) {
|
||||
Assert.isTrue(state instanceof Map, "Serialized state needs to be of type [java.util.Map]");
|
||||
this.attributes.putAll((Map) state);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* 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.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,132 @@
|
|||
/*
|
||||
* Copyright 2002-2006 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,68 @@
|
|||
/*
|
||||
* 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.mock.web;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
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
|
||||
* @since 2.0
|
||||
* @see MockMultipartFile
|
||||
*/
|
||||
public class MockMultipartHttpServletRequest extends MockHttpServletRequest implements MultipartHttpServletRequest {
|
||||
|
||||
private final Map multipartFiles = new LinkedHashMap(4);
|
||||
|
||||
|
||||
/**
|
||||
* 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.put(file.getName(), file);
|
||||
}
|
||||
|
||||
public Iterator getFileNames() {
|
||||
return getFileMap().keySet().iterator();
|
||||
}
|
||||
|
||||
public MultipartFile getFile(String name) {
|
||||
return (MultipartFile) this.multipartFiles.get(name);
|
||||
}
|
||||
|
||||
public Map getFileMap() {
|
||||
return Collections.unmodifiableMap(this.multipartFiles);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,330 @@
|
|||
/*
|
||||
* 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.mock.web;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
|
||||
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
|
||||
*/
|
||||
public class MockPageContext extends PageContext {
|
||||
|
||||
private final ServletContext servletContext;
|
||||
|
||||
private final HttpServletRequest request;
|
||||
|
||||
private final HttpServletResponse response;
|
||||
|
||||
private final ServletConfig servletConfig;
|
||||
|
||||
private final Hashtable attributes = new Hashtable();
|
||||
|
||||
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 getAttributeNames() {
|
||||
return this.attributes.keys();
|
||||
}
|
||||
|
||||
public Enumeration 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 VariableResolver getVariableResolver() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public HttpSession getSession() {
|
||||
return this.request.getSession();
|
||||
}
|
||||
|
||||
public Object getPage() {
|
||||
throw new UnsupportedOperationException("getPage");
|
||||
}
|
||||
|
||||
public ServletRequest getRequest() {
|
||||
return this.request;
|
||||
}
|
||||
|
||||
public ServletResponse getResponse() {
|
||||
return this.response;
|
||||
}
|
||||
|
||||
public Exception getException() {
|
||||
throw new UnsupportedOperationException("getException");
|
||||
}
|
||||
|
||||
public ServletConfig getServletConfig() {
|
||||
return this.servletConfig;
|
||||
}
|
||||
|
||||
public ServletContext getServletContext() {
|
||||
return this.servletContext;
|
||||
}
|
||||
|
||||
public void forward(String url) throws ServletException, IOException {
|
||||
throw new UnsupportedOperationException("forward");
|
||||
}
|
||||
|
||||
public void include(String url) throws ServletException, IOException {
|
||||
throw new UnsupportedOperationException("include");
|
||||
}
|
||||
|
||||
public void include(String url, boolean flush) throws ServletException, IOException {
|
||||
throw new UnsupportedOperationException("include");
|
||||
}
|
||||
|
||||
public void handlePageException(Exception ex) throws ServletException, IOException {
|
||||
throw new UnsupportedOperationException("handlePageException");
|
||||
}
|
||||
|
||||
public void handlePageException(Throwable ex) throws ServletException, IOException {
|
||||
throw new UnsupportedOperationException("handlePageException");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* 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.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).setIncludedUrl(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,102 @@
|
|||
/*
|
||||
* Copyright 2002-2006 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.Enumeration;
|
||||
import java.util.Properties;
|
||||
|
||||
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 Properties initParameters = new Properties();
|
||||
|
||||
|
||||
/**
|
||||
* 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 servletName;
|
||||
}
|
||||
|
||||
public ServletContext getServletContext() {
|
||||
return servletContext;
|
||||
}
|
||||
|
||||
public void addInitParameter(String name, String value) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
this.initParameters.setProperty(name, value);
|
||||
}
|
||||
|
||||
public String getInitParameter(String name) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
return this.initParameters.getProperty(name);
|
||||
}
|
||||
|
||||
public Enumeration getInitParameterNames() {
|
||||
return this.initParameters.keys();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,356 @@
|
|||
/*
|
||||
* 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.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.Hashtable;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
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.ObjectUtils;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.servlet.ServletContext} interface.
|
||||
*
|
||||
* <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 final Map contexts = new HashMap();
|
||||
|
||||
private final Properties initParameters = new Properties();
|
||||
|
||||
private final Hashtable attributes = new Hashtable();
|
||||
|
||||
private String servletContextName = "MockServletContext";
|
||||
|
||||
|
||||
/**
|
||||
* 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 (ServletContext) this.contexts.get(contextPath);
|
||||
}
|
||||
|
||||
public int getMajorVersion() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
public int getMinorVersion() {
|
||||
return 5;
|
||||
}
|
||||
|
||||
public String getMimeType(String filePath) {
|
||||
return MimeTypeResolver.getMimeType(filePath);
|
||||
}
|
||||
|
||||
public Set 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 resourcePaths = new LinkedHashSet(fileList.length);
|
||||
for (int i = 0; i < fileList.length; i++) {
|
||||
String resultPath = actualPath + fileList[i];
|
||||
if (resource.createRelative(fileList[i]).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 getServlets() {
|
||||
return Collections.enumeration(Collections.EMPTY_SET);
|
||||
}
|
||||
|
||||
public Enumeration getServletNames() {
|
||||
return Collections.enumeration(Collections.EMPTY_SET);
|
||||
}
|
||||
|
||||
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.getProperty(name);
|
||||
}
|
||||
|
||||
public void addInitParameter(String name, String value) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
this.initParameters.setProperty(name, value);
|
||||
}
|
||||
|
||||
public Enumeration getInitParameterNames() {
|
||||
return this.initParameters.keys();
|
||||
}
|
||||
|
||||
public Object getAttribute(String name) {
|
||||
Assert.notNull(name, "Attribute name must not be null");
|
||||
return this.attributes.get(name);
|
||||
}
|
||||
|
||||
public Enumeration getAttributeNames() {
|
||||
return this.attributes.keys();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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,86 @@
|
|||
/*
|
||||
* 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.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 combo
|
||||
* (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,14 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
A comprehensive set of Servlet API 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>).
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* 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.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.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 MockPortletRequest implements ActionRequest {
|
||||
|
||||
private String characterEncoding;
|
||||
|
||||
private byte[] content;
|
||||
|
||||
private String contentType;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MockActionRequest with a default {@link MockPortalContext}
|
||||
* and a default {@link MockPortletContext}.
|
||||
* @see MockPortalContext
|
||||
* @see MockPortletContext
|
||||
*/
|
||||
public MockActionRequest() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
|
||||
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 characterEncoding;
|
||||
}
|
||||
|
||||
public void setContentType(String contentType) {
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public int getContentLength() {
|
||||
return (this.content != null ? content.length : -1);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
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.core.CollectionFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.ActionResponse} interface.
|
||||
*
|
||||
* @author John A. Lewis
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MockActionResponse extends MockPortletResponse implements ActionResponse {
|
||||
|
||||
private WindowState windowState;
|
||||
|
||||
private PortletMode portletMode;
|
||||
|
||||
private String redirectedUrl;
|
||||
|
||||
private final Map renderParameters = new LinkedHashMap(16);
|
||||
|
||||
|
||||
/**
|
||||
* 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");
|
||||
}
|
||||
if (!CollectionUtils.contains(getPortalContext().getSupportedWindowStates(), windowState)) {
|
||||
throw new WindowStateException("WindowState not supported", windowState);
|
||||
}
|
||||
this.windowState = windowState;
|
||||
}
|
||||
|
||||
public WindowState getWindowState() {
|
||||
return windowState;
|
||||
}
|
||||
|
||||
public void setPortletMode(PortletMode portletMode) throws PortletModeException {
|
||||
if (this.redirectedUrl != null) {
|
||||
throw new IllegalStateException("Cannot set PortletMode after sendRedirect has been called");
|
||||
}
|
||||
if (!CollectionUtils.contains(getPortalContext().getSupportedPortletModes(), portletMode)) {
|
||||
throw new PortletModeException("PortletMode not supported", portletMode);
|
||||
}
|
||||
this.portletMode = portletMode;
|
||||
}
|
||||
|
||||
public PortletMode getPortletMode() {
|
||||
return portletMode;
|
||||
}
|
||||
|
||||
public void sendRedirect(String url) throws IOException {
|
||||
if (this.windowState != null || this.portletMode != null || !this.renderParameters.isEmpty()) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot call sendRedirect after windowState, portletMode, or renderParameters have been set");
|
||||
}
|
||||
Assert.notNull(url, "Redirect URL must not be null");
|
||||
this.redirectedUrl = url;
|
||||
}
|
||||
|
||||
public String getRedirectedUrl() {
|
||||
return redirectedUrl;
|
||||
}
|
||||
|
||||
public void setRenderParameters(Map parameters) {
|
||||
if (this.redirectedUrl != null) {
|
||||
throw new IllegalStateException("Cannot set render parameters after sendRedirect has been called");
|
||||
}
|
||||
Assert.notNull(parameters, "Parameters Map must not be null");
|
||||
this.renderParameters.clear();
|
||||
for (Iterator it = parameters.entrySet().iterator(); it.hasNext();) {
|
||||
Map.Entry entry = (Map.Entry) it.next();
|
||||
Assert.isTrue(entry.getKey() instanceof String, "Key must be of type String");
|
||||
Assert.isTrue(entry.getValue() instanceof String[], "Value must be of type String[]");
|
||||
this.renderParameters.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public void setRenderParameter(String key, String value) {
|
||||
if (this.redirectedUrl != null) {
|
||||
throw new IllegalStateException("Cannot set render parameters after sendRedirect has been called");
|
||||
}
|
||||
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 String getRenderParameter(String name) {
|
||||
String[] arr = (String[]) this.renderParameters.get(name);
|
||||
return (arr != null && arr.length > 0 ? arr[0] : null);
|
||||
}
|
||||
|
||||
public void setRenderParameter(String key, String[] values) {
|
||||
if (this.redirectedUrl != null) {
|
||||
throw new IllegalStateException("Cannot set render parameters after sendRedirect has been called");
|
||||
}
|
||||
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[] getRenderParameterValues(String key) {
|
||||
Assert.notNull(key, "Parameter key must not be null");
|
||||
return (String[]) this.renderParameters.get(key);
|
||||
}
|
||||
|
||||
public Iterator getRenderParameterNames() {
|
||||
return this.renderParameters.keySet().iterator();
|
||||
}
|
||||
|
||||
public Map getRenderParameterMap() {
|
||||
return Collections.unmodifiableMap(this.renderParameters);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
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
|
||||
* @since 2.0
|
||||
* @see org.springframework.mock.web.MockMultipartFile
|
||||
*/
|
||||
public class MockMultipartActionRequest extends MockActionRequest implements MultipartActionRequest {
|
||||
|
||||
private final Map multipartFiles = new LinkedHashMap(4);
|
||||
|
||||
|
||||
/**
|
||||
* 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.put(file.getName(), file);
|
||||
}
|
||||
|
||||
public Iterator getFileNames() {
|
||||
return getFileMap().keySet().iterator();
|
||||
}
|
||||
|
||||
public MultipartFile getFile(String name) {
|
||||
return (MultipartFile) this.multipartFiles.get(name);
|
||||
}
|
||||
|
||||
public Map getFileMap() {
|
||||
return Collections.unmodifiableMap(this.multipartFiles);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright 2002-2006 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.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Vector;
|
||||
|
||||
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 Properties properties = new Properties();
|
||||
|
||||
private final Vector portletModes;
|
||||
|
||||
private final Vector 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 Vector(3);
|
||||
this.portletModes.add(PortletMode.VIEW);
|
||||
this.portletModes.add(PortletMode.EDIT);
|
||||
this.portletModes.add(PortletMode.HELP);
|
||||
|
||||
this.windowStates = new Vector(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 supportedPortletModes, List supportedWindowStates) {
|
||||
this.portletModes = new Vector(supportedPortletModes);
|
||||
this.windowStates = new Vector(supportedWindowStates);
|
||||
}
|
||||
|
||||
|
||||
public String getPortalInfo() {
|
||||
return "MockPortal/1.0";
|
||||
}
|
||||
|
||||
public String getProperty(String name) {
|
||||
return this.properties.getProperty(name);
|
||||
}
|
||||
|
||||
public Enumeration getPropertyNames() {
|
||||
return this.properties.propertyNames();
|
||||
}
|
||||
|
||||
public Enumeration getSupportedPortletModes() {
|
||||
return this.portletModes.elements();
|
||||
}
|
||||
|
||||
public Enumeration getSupportedWindowStates() {
|
||||
return this.windowStates.elements();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import javax.portlet.PortletConfig;
|
||||
import javax.portlet.PortletContext;
|
||||
|
||||
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 HashMap resourceBundles = new HashMap();
|
||||
|
||||
private final Properties initParameters = new Properties();
|
||||
|
||||
|
||||
/**
|
||||
* 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 (ResourceBundle) this.resourceBundles.get(locale);
|
||||
}
|
||||
|
||||
public void addInitParameter(String name, String value) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
this.initParameters.setProperty(name, value);
|
||||
}
|
||||
|
||||
public String getInitParameter(String name) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
return this.initParameters.getProperty(name);
|
||||
}
|
||||
|
||||
public Enumeration getInitParameterNames() {
|
||||
return this.initParameters.keys();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* Copyright 2002-2006 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.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
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 Hashtable attributes = new Hashtable();
|
||||
|
||||
private final Properties initParameters = new Properties();
|
||||
|
||||
private String portletContextName = "MockPortletContext";
|
||||
|
||||
|
||||
/**
|
||||
* 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 1;
|
||||
}
|
||||
|
||||
public int getMinorVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public String getMimeType(String filePath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
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 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 resourcePaths = new HashSet(fileList.length);
|
||||
for (int i = 0; i < fileList.length; i++) {
|
||||
resourcePaths.add(prefix + fileList[i]);
|
||||
}
|
||||
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 getAttributeNames() {
|
||||
return this.attributes.keys();
|
||||
}
|
||||
|
||||
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.setProperty(name, value);
|
||||
}
|
||||
|
||||
public String getInitParameter(String name) {
|
||||
Assert.notNull(name, "Parameter name must not be null");
|
||||
return this.initParameters.getProperty(name);
|
||||
}
|
||||
|
||||
public Enumeration getInitParameterNames() {
|
||||
return this.initParameters.keys();
|
||||
}
|
||||
|
||||
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 portletContextName;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* 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.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 preferences = new LinkedHashMap(16);
|
||||
|
||||
private final Set readOnly = new HashSet();
|
||||
|
||||
|
||||
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 = (String[]) 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 = (String[]) 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 getNames() {
|
||||
return Collections.enumeration(this.preferences.keySet());
|
||||
}
|
||||
|
||||
public Map 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,471 @@
|
|||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Iterator;
|
||||
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 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 properties = new LinkedHashMap(16);
|
||||
|
||||
private final Hashtable attributes = new Hashtable();
|
||||
|
||||
private final Map parameters = new LinkedHashMap(16);
|
||||
|
||||
private String authType = null;
|
||||
|
||||
private String contextPath = "";
|
||||
|
||||
private String remoteUser = null;
|
||||
|
||||
private Principal userPrincipal = null;
|
||||
|
||||
private final Set userRoles = new HashSet();
|
||||
|
||||
private boolean secure = false;
|
||||
|
||||
private boolean requestedSessionIdValid = true;
|
||||
|
||||
private final Vector responseContentTypes = new Vector();
|
||||
|
||||
private final Vector locales = new Vector();
|
||||
|
||||
private String scheme = "http";
|
||||
|
||||
private String serverName = "localhost";
|
||||
|
||||
private int serverPort = 80;
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Lifecycle methods
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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 list = new LinkedList();
|
||||
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 oldList = (List) this.properties.get(key);
|
||||
if (oldList != null) {
|
||||
oldList.add(value);
|
||||
}
|
||||
else {
|
||||
List list = new LinkedList();
|
||||
list.add(value);
|
||||
this.properties.put(key, list);
|
||||
}
|
||||
}
|
||||
|
||||
public String getProperty(String key) {
|
||||
Assert.notNull(key, "Property key must not be null");
|
||||
List list = (List) this.properties.get(key);
|
||||
return (list != null && list.size() > 0 ? (String) list.get(0) : null);
|
||||
}
|
||||
|
||||
public Enumeration getProperties(String key) {
|
||||
Assert.notNull(key, "property key must not be null");
|
||||
return Collections.enumeration((List) this.properties.get(key));
|
||||
}
|
||||
|
||||
public Enumeration 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 getAttributeNames() {
|
||||
checkActive();
|
||||
return this.attributes.keys();
|
||||
}
|
||||
|
||||
public void setParameters(Map parameters) {
|
||||
Assert.notNull(parameters, "Parameters Map must not be null");
|
||||
this.parameters.clear();
|
||||
for (Iterator it = parameters.entrySet().iterator(); it.hasNext();) {
|
||||
Map.Entry entry = (Map.Entry) it.next();
|
||||
Assert.isTrue(entry.getKey() instanceof String, "Key must be of type String");
|
||||
Assert.isTrue(entry.getValue() instanceof String[], "Value must be of type String[]");
|
||||
this.parameters.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
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 = (String[]) 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 = (String[]) this.parameters.get(name);
|
||||
return (arr != null && arr.length > 0 ? arr[0] : null);
|
||||
}
|
||||
|
||||
public Enumeration getParameterNames() {
|
||||
return Collections.enumeration(this.parameters.keySet());
|
||||
}
|
||||
|
||||
public String[] getParameterValues(String name) {
|
||||
return (String[]) this.parameters.get(name);
|
||||
}
|
||||
|
||||
public Map 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 (String) this.responseContentTypes.get(0);
|
||||
}
|
||||
|
||||
public Enumeration getResponseContentTypes() {
|
||||
return this.responseContentTypes.elements();
|
||||
}
|
||||
|
||||
public void addLocale(Locale locale) {
|
||||
this.locales.add(locale);
|
||||
}
|
||||
|
||||
public void addPreferredLocale(Locale locale) {
|
||||
this.locales.add(0, locale);
|
||||
}
|
||||
|
||||
public Locale getLocale() {
|
||||
return (Locale) this.locales.get(0);
|
||||
}
|
||||
|
||||
public Enumeration getLocales() {
|
||||
return this.locales.elements();
|
||||
}
|
||||
|
||||
public void setScheme(String scheme) {
|
||||
this.scheme = scheme;
|
||||
}
|
||||
|
||||
public String getScheme() {
|
||||
return scheme;
|
||||
}
|
||||
|
||||
public void setServerName(String serverName) {
|
||||
this.serverName = serverName;
|
||||
}
|
||||
|
||||
public String getServerName() {
|
||||
return serverName;
|
||||
}
|
||||
|
||||
public void setServerPort(int serverPort) {
|
||||
this.serverPort = serverPort;
|
||||
}
|
||||
|
||||
public int getServerPort() {
|
||||
return serverPort;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright 2002-2006 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.PortletRequestDispatcher;
|
||||
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 {
|
||||
Assert.notNull(request, "Request must not be null");
|
||||
Assert.notNull(response, "Response must not be null");
|
||||
if (!(response instanceof MockRenderResponse)) {
|
||||
throw new IllegalArgumentException("MockPortletRequestDispatcher requires MockRenderResponse");
|
||||
}
|
||||
((MockRenderResponse) response).setIncludedUrl(this.url);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("MockPortletRequestDispatcher: including URL [" + this.url + "]");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.portlet.PortalContext;
|
||||
import javax.portlet.PortletResponse;
|
||||
|
||||
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 properties = new LinkedHashMap(16);
|
||||
|
||||
|
||||
/**
|
||||
* 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 portalContext;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// PortletResponse methods
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
public void addProperty(String key, String value) {
|
||||
Assert.notNull(key, "Property key must not be null");
|
||||
String[] oldArr = (String[]) 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 getPropertyNames() {
|
||||
return this.properties.keySet();
|
||||
}
|
||||
|
||||
public String getProperty(String key) {
|
||||
Assert.notNull(key, "Property key must not be null");
|
||||
String[] arr = (String[]) 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 (String[]) this.properties.get(key);
|
||||
}
|
||||
|
||||
public String encodeURL(String path) {
|
||||
return path;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* Copyright 2002-2006 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.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import javax.portlet.PortletContext;
|
||||
import javax.portlet.PortletSession;
|
||||
|
||||
/**
|
||||
* 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 Hashtable portletAttributes = new Hashtable();
|
||||
|
||||
private final Hashtable applicationAttributes = new Hashtable();
|
||||
|
||||
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 getAttributeNames() {
|
||||
return this.portletAttributes.keys();
|
||||
}
|
||||
|
||||
public Enumeration getAttributeNames(int scope) {
|
||||
if (scope == PortletSession.PORTLET_SCOPE) {
|
||||
return this.portletAttributes.keys();
|
||||
}
|
||||
else if (scope == PortletSession.APPLICATION_SCOPE) {
|
||||
return this.applicationAttributes.keys();
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
public void invalidate() {
|
||||
this.invalid = true;
|
||||
this.portletAttributes.clear();
|
||||
this.applicationAttributes.clear();
|
||||
}
|
||||
|
||||
public boolean isInvalid() {
|
||||
return 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 portletContext;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.portlet.PortalContext;
|
||||
import javax.portlet.PortletMode;
|
||||
import javax.portlet.PortletModeException;
|
||||
import javax.portlet.PortletSecurityException;
|
||||
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 implements PortletURL {
|
||||
|
||||
public static final String URL_TYPE_RENDER = "render";
|
||||
|
||||
public static final String URL_TYPE_ACTION = "action";
|
||||
|
||||
private static final String ENCODING = "UTF-8";
|
||||
|
||||
|
||||
private final PortalContext portalContext;
|
||||
|
||||
private final String urlType;
|
||||
|
||||
private WindowState windowState;
|
||||
|
||||
private PortletMode portletMode;
|
||||
|
||||
private final Map parameters = new LinkedHashMap(16);
|
||||
|
||||
private boolean secure = false;
|
||||
|
||||
|
||||
/**
|
||||
* 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 void setPortletMode(PortletMode portletMode) throws PortletModeException {
|
||||
if (!CollectionUtils.contains(this.portalContext.getSupportedPortletModes(), portletMode)) {
|
||||
throw new PortletModeException("PortletMode not supported", portletMode);
|
||||
}
|
||||
this.portletMode = portletMode;
|
||||
}
|
||||
|
||||
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 parameters) {
|
||||
Assert.notNull(parameters, "Parameters Map must not be null");
|
||||
this.parameters.clear();
|
||||
for (Iterator it = parameters.entrySet().iterator(); it.hasNext();) {
|
||||
Map.Entry entry = (Map.Entry) it.next();
|
||||
Assert.isTrue(entry.getKey() instanceof String, "Key must be of type String");
|
||||
Assert.isTrue(entry.getValue() instanceof String[], "Value must be of type String[]");
|
||||
this.parameters.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public Set getParameterNames() {
|
||||
return this.parameters.keySet();
|
||||
}
|
||||
|
||||
public String getParameter(String name) {
|
||||
String[] arr = (String[]) this.parameters.get(name);
|
||||
return (arr != null && arr.length > 0 ? arr[0] : null);
|
||||
}
|
||||
|
||||
public String[] getParameterValues(String name) {
|
||||
return (String[]) this.parameters.get(name);
|
||||
}
|
||||
|
||||
public Map getParameterMap() {
|
||||
return Collections.unmodifiableMap(this.parameters);
|
||||
}
|
||||
|
||||
public void setSecure(boolean secure) throws PortletSecurityException {
|
||||
this.secure = secure;
|
||||
}
|
||||
|
||||
public boolean isSecure() {
|
||||
return secure;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer query = new StringBuffer();
|
||||
query.append(encodeParameter("urlType", this.urlType));
|
||||
if (this.windowState != null) {
|
||||
query.append(";" + encodeParameter("windowState", this.windowState.toString()));
|
||||
}
|
||||
if (this.portletMode != null) {
|
||||
query.append(";" + encodeParameter("portletMode", this.portletMode.toString()));
|
||||
}
|
||||
for (Iterator it = this.parameters.entrySet().iterator(); it.hasNext();) {
|
||||
Map.Entry entry = (Map.Entry) it.next();
|
||||
String name = (String) entry.getKey();
|
||||
String[] values = (String[]) entry.getValue();
|
||||
query.append(";" + encodeParameter("param_" + name, values));
|
||||
}
|
||||
return (this.secure ? "https:" : "http:") +
|
||||
"//localhost/mockportlet?" + query.toString();
|
||||
}
|
||||
|
||||
|
||||
private String encodeParameter(String name, String value) {
|
||||
try {
|
||||
return URLEncoder.encode(name, ENCODING) + "=" +
|
||||
URLEncoder.encode(value, ENCODING);
|
||||
}
|
||||
catch (UnsupportedEncodingException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String encodeParameter(String name, String[] values) {
|
||||
try {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
for (int i = 0, n = values.length; i < n; i++) {
|
||||
buf.append((i > 0 ? ";" : "") +
|
||||
URLEncoder.encode(name, ENCODING) + "=" +
|
||||
URLEncoder.encode(values[i], ENCODING));
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
catch (UnsupportedEncodingException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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.mock.web.portlet;
|
||||
|
||||
import javax.portlet.PortalContext;
|
||||
import javax.portlet.PortletContext;
|
||||
import javax.portlet.PortletMode;
|
||||
import javax.portlet.RenderRequest;
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
* @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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
* Copyright 2002-2006 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.Locale;
|
||||
|
||||
import javax.portlet.PortalContext;
|
||||
import javax.portlet.PortletURL;
|
||||
import javax.portlet.RenderResponse;
|
||||
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link javax.portlet.RenderResponse} interface.
|
||||
*
|
||||
* @author John A. Lewis
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MockRenderResponse extends MockPortletResponse implements RenderResponse {
|
||||
|
||||
private String contentType;
|
||||
|
||||
private String namespace = "MockPortlet";
|
||||
|
||||
private String title;
|
||||
|
||||
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 boolean committed;
|
||||
|
||||
private String includedUrl;
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// RenderResponse methods
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
public String getContentType() {
|
||||
return this.contentType;
|
||||
}
|
||||
|
||||
public PortletURL createRenderURL() {
|
||||
PortletURL url = new MockPortletURL(getPortalContext(), MockPortletURL.URL_TYPE_RENDER);
|
||||
return url;
|
||||
}
|
||||
|
||||
public PortletURL createActionURL() {
|
||||
PortletURL url = new MockPortletURL(getPortalContext(), MockPortletURL.URL_TYPE_ACTION);
|
||||
return url;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return this.namespace;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setContentType(String contentType) {
|
||||
this.contentType = 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;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Methods for MockPortletRequestDispatcher
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
public void setIncludedUrl(String includedUrl) {
|
||||
this.includedUrl = includedUrl;
|
||||
}
|
||||
|
||||
public String getIncludedUrl() {
|
||||
return includedUrl;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
A comprehensive set of Portlet API 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.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
* 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
|
||||
*/
|
||||
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,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.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()
|
||||
*/
|
||||
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,182 @@
|
|||
/*
|
||||
* 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 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
|
||||
*/
|
||||
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 contextKeyToContextMap = new HashMap();
|
||||
|
||||
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Determines if the supplied context <code>key</code> is <em>empty</em>.
|
||||
* </p>
|
||||
* <p>
|
||||
* By default, <code>null</code> values, empty strings, and zero-length
|
||||
* arrays are considered <em>empty</em>.
|
||||
* </p>
|
||||
*
|
||||
* @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 = (ConfigurableApplicationContext) 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 = (ConfigurableApplicationContext) 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,196 @@
|
|||
/*
|
||||
* 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;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* 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()
|
||||
*/
|
||||
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,397 @@
|
|||
/*
|
||||
* 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 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
|
||||
*/
|
||||
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(final 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(final PlatformTransactionManager transactionManager) {
|
||||
|
||||
this.transactionManager = transactionManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Determines whether or not to rollback transactions for the current test.
|
||||
* </p>
|
||||
* <p>
|
||||
* The default implementation delegates to {@link #isDefaultRollback()}.
|
||||
* Subclasses can override as necessary.
|
||||
* </p>
|
||||
*
|
||||
* @return The <em>rollback</em> flag for the current test.
|
||||
*/
|
||||
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(final 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,256 @@
|
|||
/*
|
||||
* 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.Assert;
|
||||
import junit.framework.AssertionFailedError;
|
||||
|
||||
/**
|
||||
* 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>
|
||||
*
|
||||
* You might want to compare this class with the
|
||||
* {@link junit.extensions.ExceptionTestCase} class.
|
||||
*
|
||||
* <p>Note: This class requires JDK 1.4 or higher.
|
||||
*
|
||||
* @author Rick Evans
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
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() {
|
||||
Assert.fail(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() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
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())) {
|
||||
AssertionFailedError error =
|
||||
new AssertionFailedError(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) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
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,98 @@
|
|||
/*
|
||||
* 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
|
||||
*/
|
||||
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() + " 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,309 @@
|
|||
/*
|
||||
* 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
|
||||
*/
|
||||
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() + " 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,51 @@
|
|||
/*
|
||||
* 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 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 annotation to indicate that a test method <em>dirties</em> the context
|
||||
* for the current test.
|
||||
* </p>
|
||||
* <p>
|
||||
* Using this annotation in conjunction with
|
||||
* {@link AbstractAnnotationAwareTransactionalTests} is less error-prone than
|
||||
* calling
|
||||
* {@link org.springframework.test.AbstractSingleSpringContextTests#setDirty() setDirty()}
|
||||
* explicitly because the call to <code>setDirty()</code> is guaranteed to
|
||||
* occur, even if the test failed. If only a particular code path in the test
|
||||
* dirties the context, prefer calling <code>setDirty()</code> explicitly --
|
||||
* and take care!
|
||||
* </p>
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Sam Brannen
|
||||
* @since 2.0
|
||||
* @see org.springframework.test.AbstractSingleSpringContextTests
|
||||
*/
|
||||
@Target( { ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface DirtiesContext {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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 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
|
||||
*/
|
||||
@Target( { ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface ExpectedException {
|
||||
|
||||
Class<? extends Throwable> value();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* 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.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: {@link IfProfileValue @IfProfileValue} can be applied at either the
|
||||
* class or method level.
|
||||
* </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">
|
||||
* {@link IfProfileValue @IfProfileValue}(name="java.vendor", value="Sun Microsystems Inc.")
|
||||
* testSomething() {
|
||||
* // ...
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* You can alternatively configure {@link IfProfileValue @IfProfileValue} 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">
|
||||
* {@link IfProfileValue @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 AbstractAnnotationAwareTransactionalTests
|
||||
* @see org.springframework.test.context.junit38.AbstractJUnit38SpringContextTests
|
||||
* @see org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests
|
||||
* @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
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,37 @@
|
|||
/*
|
||||
* 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 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 not transactional.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Sam Brannen
|
||||
* @since 2.0
|
||||
*/
|
||||
@Target( { ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
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-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 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
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Inherited
|
||||
@Documented
|
||||
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,213 @@
|
|||
/*
|
||||
* 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 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);
|
||||
if (ifProfileValue == null) {
|
||||
return true;
|
||||
}
|
||||
ProfileValueSource profileValueSource = retrieveProfileValueSource(testClass);
|
||||
return isTestEnabledInThisEnvironment(profileValueSource, 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.
|
||||
* <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) {
|
||||
IfProfileValue ifProfileValue = testMethod.getAnnotation(IfProfileValue.class);
|
||||
if (ifProfileValue == null) {
|
||||
ifProfileValue = testClass.getAnnotation(IfProfileValue.class);
|
||||
if (ifProfileValue == null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
ProfileValueSource profileValueSource = retrieveProfileValueSource(testClass);
|
||||
return isTestEnabledInThisEnvironment(profileValueSource, 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.
|
||||
* <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 = testMethod.getAnnotation(IfProfileValue.class);
|
||||
if (ifProfileValue == null) {
|
||||
ifProfileValue = testClass.getAnnotation(IfProfileValue.class);
|
||||
if (ifProfileValue == null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return isTestEnabledInThisEnvironment(profileValueSource, ifProfileValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return <code>true</code> if the test is <em>enabled</em> in the
|
||||
* current environment
|
||||
*/
|
||||
private static boolean isTestEnabledInThisEnvironment(ProfileValueSource profileValueSource,
|
||||
IfProfileValue ifProfileValue) {
|
||||
|
||||
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-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.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
|
||||
*/
|
||||
@Target( { ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Repeat {
|
||||
|
||||
int value() default 1;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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 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
|
||||
*/
|
||||
@Target( { ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Rollback {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Whether or not the transaction for the annotated method should be rolled
|
||||
* back after the method has completed.
|
||||
* </p>
|
||||
*/
|
||||
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-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.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
|
||||
*/
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
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,7 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
Support classes for annotation-driven tests.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.Serializable;
|
||||
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} by
|
||||
* {@link Serializable serializable} key. 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<String, ApplicationContext> contextKeyToContextMap =
|
||||
new ConcurrentHashMap<String, ApplicationContext>();
|
||||
|
||||
private int hitCount;
|
||||
|
||||
private int missCount;
|
||||
|
||||
|
||||
/**
|
||||
* Clears all contexts from the cache.
|
||||
*/
|
||||
void clear() {
|
||||
this.contextKeyToContextMap.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(String key) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
return this.contextKeyToContextMap.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(String key) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
ApplicationContext context = this.contextKeyToContextMap.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 a 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(String key, ApplicationContext context) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
Assert.notNull(context, "ApplicationContext must not be null");
|
||||
this.contextKeyToContextMap.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(String key) {
|
||||
return this.contextKeyToContextMap.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 only 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(String 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.contextKeyToContextMap.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,85 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* ContextConfiguration defines class-level metadata which can be used to
|
||||
* instruct client code with regard to how to load and configure an
|
||||
* {@link org.springframework.context.ApplicationContext}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 2.5
|
||||
* @see ContextLoader
|
||||
* @see org.springframework.context.ApplicationContext
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface ContextConfiguration {
|
||||
|
||||
/**
|
||||
* The resource locations to use for loading an
|
||||
* {@link org.springframework.context.ApplicationContext ApplicationContext}.
|
||||
*/
|
||||
String[] locations() default {};
|
||||
|
||||
/**
|
||||
* Whether or not {@link #locations() resource locations} 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> the resource locations defined by an
|
||||
* annotated superclass. Specifically, the resource locations for an
|
||||
* annotated class will be appended to the list of resource locations
|
||||
* defined by an annotated superclass. Thus, subclasses have the option of
|
||||
* <em>extending</em> the list of resource locations. In the following
|
||||
* example, the {@link org.springframework.context.ApplicationContext}
|
||||
* for <code>ExtendedTest</code> 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">
|
||||
* {@link ContextConfiguration @ContextConfiguration}(locations={"base-context.xml"})
|
||||
* public class BaseTest {
|
||||
* // ...
|
||||
* }
|
||||
* {@link ContextConfiguration @ContextConfiguration}(locations={"extended-context.xml"})
|
||||
* public class ExtendedTest extends BaseTest {
|
||||
* // ...
|
||||
* }
|
||||
* </pre>
|
||||
* If <code>inheritLocations</code> is set to <code>false</code>, the
|
||||
* resource locations for the annotated class will <em>shadow</em> and
|
||||
* effectively replace any resource locations defined by a superclass.
|
||||
*/
|
||||
boolean inheritLocations() default true;
|
||||
|
||||
/**
|
||||
* The type of {@link ContextLoader} to use for loading an
|
||||
* {@link org.springframework.context.ApplicationContext}.
|
||||
*/
|
||||
Class<? extends ContextLoader> loader() default ContextLoader.class;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
/**
|
||||
* Strategy interface for loading an
|
||||
* {@link ApplicationContext application context}.
|
||||
*
|
||||
* <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
|
||||
*/
|
||||
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}
|
||||
* and {@link javax.annotation.Resource @Resource}.
|
||||
* <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,319 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
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.AttributeAccessorSupport;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* TestContext 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 String DEFAULT_CONTEXT_LOADER_CLASS_NAME = "org.springframework.test.context.support.GenericXmlContextLoader";
|
||||
|
||||
private static final long serialVersionUID = -5827157174866681233L;
|
||||
|
||||
private static final Log logger = LogFactory.getLog(TestContext.class);
|
||||
|
||||
private final ContextCache contextCache;
|
||||
|
||||
private final ContextLoader contextLoader;
|
||||
|
||||
private final String[] locations;
|
||||
|
||||
private final Class<?> testClass;
|
||||
|
||||
private Object testInstance;
|
||||
|
||||
private Method testMethod;
|
||||
|
||||
private Throwable testException;
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new test context for the supplied {@link Class test class}
|
||||
* and {@link ContextCache context cache} and parses the corresponding
|
||||
* {@link ContextConfiguration @ContextConfiguration} annotation, if present.
|
||||
* @param testClass the {@link Class} object corresponding to 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>)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
TestContext(Class<?> testClass, ContextCache contextCache) {
|
||||
Assert.notNull(testClass, "Test class must not be null");
|
||||
Assert.notNull(contextCache, "ContextCache must not be null");
|
||||
|
||||
ContextConfiguration contextConfiguration = testClass.getAnnotation(ContextConfiguration.class);
|
||||
String[] locations = null;
|
||||
ContextLoader contextLoader = null;
|
||||
|
||||
if (contextConfiguration == null) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("@ContextConfiguration not found for class [" + testClass + "]");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Retrieved @ContextConfiguration [" + contextConfiguration + "] for class [" + testClass + "]");
|
||||
}
|
||||
|
||||
Class<? extends ContextLoader> contextLoaderClass = contextConfiguration.loader();
|
||||
if (ContextLoader.class.equals(contextLoaderClass)) {
|
||||
try {
|
||||
contextLoaderClass = (Class<? extends ContextLoader>) getClass().getClassLoader().loadClass(
|
||||
DEFAULT_CONTEXT_LOADER_CLASS_NAME);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalStateException("Could not load default ContextLoader class ["
|
||||
+ DEFAULT_CONTEXT_LOADER_CLASS_NAME + "]. Specify @ContextConfiguration's 'loader' "
|
||||
+ "attribute or make the default loader class available.");
|
||||
}
|
||||
}
|
||||
contextLoader = (ContextLoader) BeanUtils.instantiateClass(contextLoaderClass);
|
||||
locations = retrieveContextLocations(contextLoader, testClass);
|
||||
}
|
||||
|
||||
this.testClass = testClass;
|
||||
this.contextCache = contextCache;
|
||||
this.contextLoader = contextLoader;
|
||||
this.locations = locations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve {@link ApplicationContext} resource locations for the supplied
|
||||
* {@link Class class}, using the supplied {@link ContextLoader} to
|
||||
* {@link ContextLoader#processLocations(Class, String...) process} the
|
||||
* locations.
|
||||
* <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>, locations defined in the annotated class
|
||||
* will be appended to the locations defined in superclasses.
|
||||
* @param contextLoader the ContextLoader to use for processing the locations
|
||||
* (must not be <code>null</code>)
|
||||
* @param clazz the class for which to retrieve the resource locations
|
||||
* (must not be <code>null</code>)
|
||||
* @return the list of ApplicationContext resource locations for the specified
|
||||
* class, including locations from superclasses if appropriate
|
||||
* @throws IllegalArgumentException if {@link ContextConfiguration @ContextConfiguration}
|
||||
* is not <em>present</em> on the supplied class
|
||||
*/
|
||||
private String[] retrieveContextLocations(ContextLoader contextLoader, Class<?> clazz) {
|
||||
Assert.notNull(contextLoader, "ContextLoader must not be null");
|
||||
Assert.notNull(clazz, "Class must not be null");
|
||||
|
||||
List<String> locationsList = new ArrayList<String>();
|
||||
Class<ContextConfiguration> annotationType = ContextConfiguration.class;
|
||||
Class<?> declaringClass = AnnotationUtils.findAnnotationDeclaringClass(annotationType, clazz);
|
||||
Assert.notNull(declaringClass, "Could not find an 'annotation declaring class' for annotation type [" +
|
||||
annotationType + "] and class [" + clazz + "]");
|
||||
|
||||
while (declaringClass != null) {
|
||||
ContextConfiguration contextConfiguration = declaringClass.getAnnotation(annotationType);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Retrieved @ContextConfiguration [" + contextConfiguration + "] for declaring class ["
|
||||
+ declaringClass + "]");
|
||||
}
|
||||
String[] locations = contextLoader.processLocations(declaringClass, contextConfiguration.locations());
|
||||
locationsList.addAll(0, Arrays.<String> asList(locations));
|
||||
declaringClass = contextConfiguration.inheritLocations() ? AnnotationUtils.findAnnotationDeclaringClass(
|
||||
annotationType, declaringClass.getSuperclass()) : null;
|
||||
}
|
||||
|
||||
return locationsList.toArray(new String[locationsList.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an {@link ApplicationContext} for this test context using the
|
||||
* configured {@link #getContextLoader() ContextLoader} and
|
||||
* {@link #getLocations() resource locations}.
|
||||
* @throws Exception if an error occurs while building the application context
|
||||
*/
|
||||
private ApplicationContext loadApplicationContext() throws Exception {
|
||||
Assert.notNull(getContextLoader(),
|
||||
"Can not build an ApplicationContext with a NULL 'contextLoader'. Consider annotating your test class with @ContextConfiguration.");
|
||||
Assert.notNull(getLocations(),
|
||||
"Can not build an ApplicationContext with a NULL 'locations' array. Consider annotating your test class with @ContextConfiguration.");
|
||||
return getContextLoader().loadContext(getLocations());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the supplied context <code>key</code> to a String
|
||||
* representation for use in caching, logging, etc.
|
||||
* @param key the context key to convert to a String
|
||||
*/
|
||||
private String contextKeyString(Serializable key) {
|
||||
return ObjectUtils.nullSafeToString(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link ApplicationContext application context} for this test
|
||||
* context, possibly cached.
|
||||
* @return the application context; may be <code>null</code> if the
|
||||
* current test context is not configured to use an application context
|
||||
* @throws IllegalStateException if an error occurs while retrieving the application context
|
||||
*/
|
||||
public ApplicationContext getApplicationContext() {
|
||||
ApplicationContext context = null;
|
||||
ContextCache cache = getContextCache();
|
||||
synchronized (cache) {
|
||||
context = cache.get(contextKeyString(getLocations()));
|
||||
if (context == null) {
|
||||
try {
|
||||
context = loadApplicationContext();
|
||||
cache.put(contextKeyString(getLocations()), context);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Failed to load ApplicationContext", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link ContextCache context cache} for this test context.
|
||||
* @return the context cache (never <code>null</code>)
|
||||
*/
|
||||
ContextCache getContextCache() {
|
||||
return this.contextCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link ContextLoader} to use for loading the
|
||||
* {@link ApplicationContext} for this test context.
|
||||
* @return the context loader. May be <code>null</code> if the current
|
||||
* test context is not configured to use an application context.
|
||||
*/
|
||||
ContextLoader getContextLoader() {
|
||||
return this.contextLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resource locations to use for loading the
|
||||
* {@link ApplicationContext} for this test context.
|
||||
* @return the application context resource locations.
|
||||
* May be <code>null</code> if the current test context is
|
||||
* not configured to use an application context.
|
||||
*/
|
||||
String[] getLocations() {
|
||||
return this.locations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Class test class} for this test context.
|
||||
* @return the test class (never <code>null</code>)
|
||||
*/
|
||||
public final Class<?> getTestClass() {
|
||||
return this.testClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets 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 this.testInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets 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 this.testMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets 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 this.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() {
|
||||
getContextCache().setDirty(contextKeyString(getLocations()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates 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
|
||||
*/
|
||||
synchronized void updateState(Object testInstance, Method testMethod, Throwable testException) {
|
||||
this.testInstance = testInstance;
|
||||
this.testMethod = testMethod;
|
||||
this.testException = testException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a string representation of this test context's
|
||||
* {@link #getTestClass() test class},
|
||||
* {@link #getLocations() application context resource locations},
|
||||
* {@link #getTestInstance() test instance},
|
||||
* {@link #getTestMethod() test method}, and
|
||||
* {@link #getTestException() test exception}.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).
|
||||
append("testClass", getTestClass()).
|
||||
append("locations", getLocations()).append("testInstance", getTestInstance()).
|
||||
append("testMethod", getTestMethod()).append("testException", getTestException()).
|
||||
toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,360 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* <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 #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>
|
||||
* </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>();
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new <code>TestContextManager</code> for the specified
|
||||
* {@link Class test class} and automatically
|
||||
* {@link #registerTestExecutionListeners(TestExecutionListener...) registers}
|
||||
* the {@link TestExecutionListener TestExecutionListeners} configured for
|
||||
* the test class via the
|
||||
* {@link TestExecutionListeners @TestExecutionListeners} annotation.
|
||||
* @param testClass the Class object corresponding to the test class to be managed
|
||||
* @see #registerTestExecutionListeners(TestExecutionListener...)
|
||||
* @see #retrieveTestExecutionListeners(Class)
|
||||
*/
|
||||
public TestContextManager(Class<?> testClass) {
|
||||
this.testContext = new TestContext(testClass, contextCache);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an {@link Collections#unmodifiableList(List) unmodifiable} copy of
|
||||
* the {@link TestExecutionListener TestExecutionListeners} registered for
|
||||
* this <code>TestContextManager</code>.
|
||||
*/
|
||||
public final List<TestExecutionListener> getTestExecutionListeners() {
|
||||
return Collections.unmodifiableList(this.testExecutionListeners);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves 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 Class object corresponding to 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>[] classes = testExecutionListeners.value();
|
||||
if (classes != null) {
|
||||
classesList.addAll(0, Arrays.<Class<? extends TestExecutionListener>> asList(classes));
|
||||
}
|
||||
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((TestExecutionListener) 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 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);
|
||||
|
||||
// Traverse the TestExecutionListeners in reverse order to ensure proper
|
||||
// "wrapper"-style execution of listeners.
|
||||
List<TestExecutionListener> listenersReversed =
|
||||
new ArrayList<TestExecutionListener>(getTestExecutionListeners());
|
||||
Collections.reverse(listenersReversed);
|
||||
|
||||
Exception afterTestMethodException = null;
|
||||
for (TestExecutionListener testExecutionListener : listenersReversed) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* <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 {
|
||||
|
||||
/**
|
||||
* Prepares the {@link Object test instance} of the supplied
|
||||
* {@link TestContext test context}, for example for injecting
|
||||
* dependencies.
|
||||
* <p>This method should be called immediately after instantiation 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 just <em>before</em> execution of the
|
||||
* {@link java.lang.reflect.Method test method} in the supplied
|
||||
* {@link TestContext test context}, for example for setting up test
|
||||
* fixtures.
|
||||
* @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 just <em>after</em> execution of the
|
||||
* {@link java.lang.reflect.Method test method} in the supplied
|
||||
* {@link TestContext test context}, for example for tearing down test
|
||||
* fixtures.
|
||||
* @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;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* 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.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;
|
||||
|
||||
/**
|
||||
* TestExecutionListeners defines class-level metadata for configuring which
|
||||
* {@link TestExecutionListener TestExecutionListeners} should be registered
|
||||
* with a {@link TestContextManager}. Typically,
|
||||
* {@link TestExecutionListeners @TestExecutionListeners} will be used in
|
||||
* conjunction with {@link ContextConfiguration @ContextConfiguration}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 2.5
|
||||
* @see TestExecutionListener
|
||||
* @see TestContextManager
|
||||
* @see ContextConfiguration
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Inherited
|
||||
@Documented
|
||||
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>[] value();
|
||||
|
||||
/**
|
||||
* <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">
|
||||
* {@link TestExecutionListeners @TestExecutionListeners}({ DependencyInjectionTestExecutionListener.class,
|
||||
* DirtiesContextTestExecutionListener.class })
|
||||
* public abstract class AbstractBaseTest {
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* {@link TestExecutionListeners @TestExecutionListeners}({ TransactionalTestExecutionListener.class })
|
||||
* public class TransactionalTest extends BaseTest {
|
||||
* // ...
|
||||
* }
|
||||
* </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,368 @@
|
|||
/*
|
||||
* 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 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
|
||||
* <em>Spring TestContext Framework</em> with 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}.
|
||||
* <em>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.</em></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>.
|
||||
* <em>(Note that additional annotations may be supported by various
|
||||
* {@link org.springframework.test.context.TestExecutionListener TestExecutionListeners})</em>
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>{@link org.springframework.test.annotation.DirtiesContext @DirtiesContext}
|
||||
* (via the configured {@link DirtiesContextTestExecutionListener})</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>
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
@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,155 @@
|
|||
/*
|
||||
* 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
|
||||
*/
|
||||
@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,8 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
<p>Support classes for ApplicationContext-based and transactional
|
||||
tests run with JUnit 3.8 and the <em>Spring TestContext Framework</em>.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* 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.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.4</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,147 @@
|
|||
/*
|
||||
* 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.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
|
||||
*/
|
||||
@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,163 @@
|
|||
/*
|
||||
* 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.junit4;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.internal.runners.InitializationError;
|
||||
import org.junit.internal.runners.JUnit4ClassRunner;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runner.notification.RunNotifier;
|
||||
|
||||
import org.springframework.test.annotation.ProfileValueUtils;
|
||||
import org.springframework.test.context.TestContextManager;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* SpringJUnit4ClassRunner is a custom extension of {@link JUnit4ClassRunner}
|
||||
* which provides functionality of the <em>Spring TestContext Framework</em>
|
||||
* to standard JUnit 4.4+ 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 SpringJUnit4ClassRunner.
|
||||
* <em>(Note that additional annotations may be supported by various
|
||||
* {@link org.springframework.test.context.TestExecutionListener TestExecutionListeners})</em>
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>{@link org.junit.Test#expected() @Test(expected=...)}</li>
|
||||
* <li>{@link org.springframework.test.annotation.ExpectedException @ExpectedException}</li>
|
||||
* <li>{@link org.junit.Test#timeout() @Test(timeout=...)}</li>
|
||||
* <li>{@link org.springframework.test.annotation.Timed @Timed}</li>
|
||||
* <li>{@link org.springframework.test.annotation.Repeat @Repeat}</li>
|
||||
* <li>{@link org.junit.Ignore @Ignore}</li>
|
||||
* <li>{@link org.springframework.test.annotation.ProfileValueSourceConfiguration @ProfileValueSourceConfiguration}</li>
|
||||
* <li>{@link org.springframework.test.annotation.IfProfileValue @IfProfileValue}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see TestContextManager
|
||||
*/
|
||||
public class SpringJUnit4ClassRunner extends JUnit4ClassRunner {
|
||||
|
||||
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 Class object corresponding to 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);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
/**
|
||||
* 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> listener methods.
|
||||
* @see org.springframework.test.annotation.IfProfileValue
|
||||
* @see org.springframework.test.context.TestExecutionListener
|
||||
*/
|
||||
public void run(RunNotifier notifier) {
|
||||
if (!ProfileValueUtils.isTestEnabledInThisEnvironment(getTestClass().getJavaClass())) {
|
||||
notifier.fireTestIgnored(getDescription());
|
||||
return;
|
||||
}
|
||||
super.run(notifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to {@link JUnit4ClassRunner#createTest()} to create the test
|
||||
* instance and then to a {@link TestContextManager} to
|
||||
* {@link TestContextManager#prepareTestInstance(Object) prepare} the test
|
||||
* instance for Spring testing functionality.
|
||||
* @see JUnit4ClassRunner#createTest()
|
||||
* @see TestContextManager#prepareTestInstance(Object)
|
||||
*/
|
||||
@Override
|
||||
protected Object createTest() throws Exception {
|
||||
Object testInstance = super.createTest();
|
||||
getTestContextManager().prepareTestInstance(testInstance);
|
||||
return testInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link TestContextManager}. Can be overridden by subclasses.
|
||||
* @param clazz the Class object corresponding to the test class to be managed
|
||||
*/
|
||||
protected TestContextManager createTestContextManager(Class<?> clazz) {
|
||||
return new TestContextManager(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link TestContextManager} associated with this runner.
|
||||
*/
|
||||
protected final TestContextManager getTestContextManager() {
|
||||
return this.testContextManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the supplied {@link Method test method} and notifies the supplied
|
||||
* {@link RunNotifier} of the appropriate events.
|
||||
* @see #createTest()
|
||||
* @see JUnit4ClassRunner#invokeTestMethod(Method,RunNotifier)
|
||||
*/
|
||||
@Override
|
||||
protected void invokeTestMethod(Method method, RunNotifier notifier) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Invoking test method [" + method.toGenericString() + "]");
|
||||
}
|
||||
|
||||
// The following is a 1-to-1 copy of the original JUnit 4.4 code, except
|
||||
// that we use custom implementations for TestMethod and MethodRoadie.
|
||||
|
||||
Description description = methodDescription(method);
|
||||
Object testInstance;
|
||||
try {
|
||||
testInstance = createTest();
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
notifier.testAborted(description, ex.getCause());
|
||||
return;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
notifier.testAborted(description, ex);
|
||||
return;
|
||||
}
|
||||
|
||||
SpringTestMethod testMethod = new SpringTestMethod(method, getTestClass());
|
||||
new SpringMethodRoadie(getTestContextManager(), testInstance, testMethod, notifier, description).run();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,352 @@
|
|||
/*
|
||||
* 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.junit4;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Assume.AssumptionViolatedException;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runner.notification.Failure;
|
||||
import org.junit.runner.notification.RunNotifier;
|
||||
|
||||
import org.springframework.test.annotation.Repeat;
|
||||
import org.springframework.test.annotation.Timed;
|
||||
import org.springframework.test.context.TestContextManager;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* <code>SpringMethodRoadie</code> is a custom implementation of JUnit 4.4's
|
||||
* {@link org.junit.internal.runners.MethodRoadie MethodRoadie}, which provides
|
||||
* the following enhancements:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>Notifies a {@link TestContextManager} of
|
||||
* {@link TestContextManager#beforeTestMethod(Object,Method) before} and
|
||||
* {@link TestContextManager#afterTestMethod(Object,Method,Throwable) after}
|
||||
* events.</li>
|
||||
* <li>Uses a {@link SpringTestMethod} instead of JUnit 4.4's
|
||||
* {@link org.junit.internal.runners.TestMethod TestMethod}.</li>
|
||||
* <li>Tracks the exception thrown during execution of the test method.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Due to method and field visibility constraints, the code of
|
||||
* <code>MethodRoadie</code> has been duplicated here instead of subclassing
|
||||
* <code>MethodRoadie</code> directly.
|
||||
* </p>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
*/
|
||||
class SpringMethodRoadie {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(SpringMethodRoadie.class);
|
||||
|
||||
private final TestContextManager testContextManager;
|
||||
|
||||
private final Object testInstance;
|
||||
|
||||
private final SpringTestMethod testMethod;
|
||||
|
||||
private final RunNotifier notifier;
|
||||
|
||||
private final Description description;
|
||||
|
||||
private Throwable testException;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new <code>SpringMethodRoadie</code>.
|
||||
* @param testContextManager the TestContextManager to notify
|
||||
* @param testInstance the test instance upon which to invoke the test method
|
||||
* @param testMethod the test method to invoke
|
||||
* @param notifier the RunNotifier to notify
|
||||
* @param description the test description
|
||||
*/
|
||||
public SpringMethodRoadie(TestContextManager testContextManager, Object testInstance,
|
||||
SpringTestMethod testMethod, RunNotifier notifier, Description description) {
|
||||
|
||||
this.testContextManager = testContextManager;
|
||||
this.testInstance = testInstance;
|
||||
this.testMethod = testMethod;
|
||||
this.notifier = notifier;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the <em>test</em>, including notification of events to the
|
||||
* {@link RunNotifier} and {@link TestContextManager} as well as proper
|
||||
* handling of {@link org.junit.Ignore @Ignore},
|
||||
* {@link org.junit.Test#expected() expected exceptions},
|
||||
* {@link org.junit.Test#timeout() test timeouts}, and
|
||||
* {@link org.junit.Assume.AssumptionViolatedException assumptions}.
|
||||
*/
|
||||
public void run() {
|
||||
if (this.testMethod.isIgnored()) {
|
||||
this.notifier.fireTestIgnored(this.description);
|
||||
return;
|
||||
}
|
||||
|
||||
this.notifier.fireTestStarted(this.description);
|
||||
try {
|
||||
Timed timedAnnotation = this.testMethod.getMethod().getAnnotation(Timed.class);
|
||||
long springTimeout = (timedAnnotation != null && timedAnnotation.millis() > 0 ?
|
||||
timedAnnotation.millis() : 0);
|
||||
long junitTimeout = this.testMethod.getTimeout();
|
||||
if (springTimeout > 0 && junitTimeout > 0) {
|
||||
throw new IllegalStateException("Test method [" + this.testMethod.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.");
|
||||
}
|
||||
else if (springTimeout > 0) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
try {
|
||||
runTest();
|
||||
}
|
||||
finally {
|
||||
long elapsed = System.currentTimeMillis() - startTime;
|
||||
if (elapsed > springTimeout) {
|
||||
addFailure(new TimeoutException("Took " + elapsed + " ms; limit was " + springTimeout));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (junitTimeout > 0) {
|
||||
runWithTimeout(junitTimeout);
|
||||
}
|
||||
else {
|
||||
runTest();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
this.notifier.fireTestFinished(this.description);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the test method on the test instance with the specified
|
||||
* <code>timeout</code>.
|
||||
* @param timeout the timeout in milliseconds
|
||||
* @see #runWithRepetitions(Runnable)
|
||||
* @see #runTestMethod()
|
||||
*/
|
||||
protected void runWithTimeout(final long timeout) throws CancellationException {
|
||||
runWithRepetitions(new Runnable() {
|
||||
public void run() {
|
||||
ExecutorService service = Executors.newSingleThreadExecutor();
|
||||
Future result = service.submit(new RunBeforesThenTestThenAfters());
|
||||
service.shutdown();
|
||||
try {
|
||||
boolean terminated = service.awaitTermination(timeout, TimeUnit.MILLISECONDS);
|
||||
if (!terminated) {
|
||||
service.shutdownNow();
|
||||
}
|
||||
// Throws the exception if one occurred during the invocation.
|
||||
result.get(0, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (TimeoutException ex) {
|
||||
String message = "Test timed out after " + timeout + " milliseconds";
|
||||
addFailure(new TimeoutException(message));
|
||||
// We're cancelling repetitions here since we don't want
|
||||
// the abandoned test method execution to conflict with
|
||||
// further execution attempts of the same test method.
|
||||
throw new CancellationException(message);
|
||||
}
|
||||
catch (ExecutionException ex) {
|
||||
addFailure(ex.getCause());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
addFailure(ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the test, including {@link #runBefores() @Before} and
|
||||
* {@link #runAfters() @After} methods.
|
||||
* @see #runWithRepetitions(Runnable)
|
||||
* @see #runTestMethod()
|
||||
*/
|
||||
protected void runTest() {
|
||||
runWithRepetitions(new RunBeforesThenTestThenAfters());
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the supplied <code>test</code> with repetitions. Checks for the
|
||||
* presence of {@link Repeat @Repeat} to determine if the test should be run
|
||||
* more than once. The test will be run at least once.
|
||||
* @param test the runnable test
|
||||
* @see Repeat
|
||||
*/
|
||||
protected void runWithRepetitions(Runnable test) {
|
||||
Method method = this.testMethod.getMethod();
|
||||
Repeat repeat = method.getAnnotation(Repeat.class);
|
||||
int runs = (repeat != null && repeat.value() > 1 ? repeat.value() : 1);
|
||||
|
||||
for (int i = 0; i < runs; i++) {
|
||||
if (runs > 1 && logger.isInfoEnabled()) {
|
||||
logger.info("Repetition " + (i + 1) + " of test " + method.getName());
|
||||
}
|
||||
try {
|
||||
test.run();
|
||||
}
|
||||
catch (CancellationException ex) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the test method on the test instance, processing exceptions
|
||||
* (both expected and unexpected), assumptions, and registering
|
||||
* failures as necessary.
|
||||
*/
|
||||
protected void runTestMethod() {
|
||||
this.testException = null;
|
||||
try {
|
||||
this.testMethod.invoke(this.testInstance);
|
||||
if (this.testMethod.expectsException()) {
|
||||
addFailure(new AssertionError("Expected exception: " + this.testMethod.getExpectedException().getName()));
|
||||
}
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
this.testException = ex.getTargetException();
|
||||
if (!(this.testException instanceof AssumptionViolatedException)) {
|
||||
if (!this.testMethod.expectsException()) {
|
||||
addFailure(this.testException);
|
||||
}
|
||||
else if (this.testMethod.isUnexpected(this.testException)) {
|
||||
addFailure(new Exception("Unexpected exception, expected <" +
|
||||
this.testMethod.getExpectedException().getName() + "> but was <" +
|
||||
this.testException.getClass().getName() + ">", this.testException));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
addFailure(ex);
|
||||
}
|
||||
finally {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Test method [" + this.testMethod.getMethod() + "] threw exception: " +
|
||||
this.testException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link TestContextManager#beforeTestMethod} and then runs
|
||||
* {@link org.junit.Before @Before methods}, registering failures
|
||||
* and throwing {@link FailedBefore} exceptions as necessary.
|
||||
* @throws FailedBefore if an error occurs while executing a <em>before</em> method
|
||||
*/
|
||||
protected void runBefores() throws FailedBefore {
|
||||
try {
|
||||
this.testContextManager.beforeTestMethod(this.testInstance, this.testMethod.getMethod());
|
||||
List<Method> befores = this.testMethod.getBefores();
|
||||
for (Method before : befores) {
|
||||
before.invoke(this.testInstance);
|
||||
}
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
Throwable targetEx = ex.getTargetException();
|
||||
if (!(targetEx instanceof AssumptionViolatedException)) {
|
||||
addFailure(targetEx);
|
||||
}
|
||||
throw new FailedBefore();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
addFailure(ex);
|
||||
throw new FailedBefore();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs {@link org.junit.After @After methods}, registering failures as
|
||||
* necessary, and then calls {@link TestContextManager#afterTestMethod}.
|
||||
*/
|
||||
protected void runAfters() {
|
||||
List<Method> afters = this.testMethod.getAfters();
|
||||
for (Method after : afters) {
|
||||
try {
|
||||
after.invoke(this.testInstance);
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
addFailure(ex.getTargetException());
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
addFailure(ex);
|
||||
}
|
||||
}
|
||||
try {
|
||||
this.testContextManager.afterTestMethod(this.testInstance, this.testMethod.getMethod(), this.testException);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
addFailure(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire a failure for the supplied <code>exception</code> with the
|
||||
* {@link RunNotifier}.
|
||||
* @param exception the exception upon which to base the failure
|
||||
*/
|
||||
protected void addFailure(Throwable exception) {
|
||||
this.notifier.fireTestFailure(new Failure(this.description, exception));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Runs the test method, executing <code>@Before</code> and <code>@After</code>
|
||||
* methods accordingly.
|
||||
*/
|
||||
private class RunBeforesThenTestThenAfters implements Runnable {
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
runBefores();
|
||||
runTestMethod();
|
||||
}
|
||||
catch (FailedBefore ex) {
|
||||
}
|
||||
finally {
|
||||
runAfters();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Marker exception to signal that an exception was encountered while
|
||||
* executing an {@link org.junit.Before @Before} method.
|
||||
*/
|
||||
private static class FailedBefore extends Exception {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* 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.junit4;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.Test.None;
|
||||
import org.junit.internal.runners.TestClass;
|
||||
|
||||
import org.springframework.test.annotation.ExpectedException;
|
||||
import org.springframework.test.annotation.ProfileValueSource;
|
||||
import org.springframework.test.annotation.ProfileValueUtils;
|
||||
|
||||
/**
|
||||
* SpringTestMethod is a custom implementation of JUnit 4.4's
|
||||
* {@link org.junit.internal.runners.TestMethod TestMethod}. Due to method and
|
||||
* field visibility constraints, the code of TestMethod has been duplicated here
|
||||
* instead of subclassing TestMethod directly.
|
||||
*
|
||||
* <p>SpringTestMethod also provides support for
|
||||
* {@link org.springframework.test.annotation.IfProfileValue @IfProfileValue}
|
||||
* and {@link ExpectedException @ExpectedException}. See {@link #isIgnored()}
|
||||
* and {@link #getExpectedException()} for further details.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 2.5
|
||||
*/
|
||||
class SpringTestMethod {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(SpringTestMethod.class);
|
||||
|
||||
private final Method method;
|
||||
|
||||
private final TestClass testClass;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a test method for the supplied {@link Method method} and
|
||||
* {@link TestClass test class}; and retrieves the configured (or default)
|
||||
* {@link ProfileValueSource}.
|
||||
* @param method The test method
|
||||
* @param testClass the test class
|
||||
*/
|
||||
public SpringTestMethod(Method method, TestClass testClass) {
|
||||
this.method = method;
|
||||
this.testClass = testClass;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if this test method is {@link Test#expected() expected} to
|
||||
* throw an exception.
|
||||
*/
|
||||
public boolean expectsException() {
|
||||
return (getExpectedException() != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link After @After} methods for this test method.
|
||||
*/
|
||||
public List<Method> getAfters() {
|
||||
return getTestClass().getAnnotatedMethods(After.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Before @Before} methods for this test method.
|
||||
*/
|
||||
public List<Method> getBefores() {
|
||||
return getTestClass().getAnnotatedMethods(Before.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the <code>exception</code> that this 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
|
||||
*/
|
||||
public Class<? extends Throwable> getExpectedException() throws IllegalStateException {
|
||||
ExpectedException expectedExAnn = getMethod().getAnnotation(ExpectedException.class);
|
||||
Test testAnnotation = getMethod().getAnnotation(Test.class);
|
||||
|
||||
Class<? extends Throwable> expectedException = null;
|
||||
Class<? extends Throwable> springExpectedException =
|
||||
(expectedExAnn != null && expectedExAnn.value() != null ? expectedExAnn.value() : null);
|
||||
Class<? extends Throwable> junitExpectedException =
|
||||
(testAnnotation != null && testAnnotation.expected() != None.class ? testAnnotation.expected() : null);
|
||||
|
||||
if (springExpectedException != null && junitExpectedException != null) {
|
||||
String msg = "Test method [" + 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);
|
||||
}
|
||||
else if (springExpectedException != null) {
|
||||
expectedException = springExpectedException;
|
||||
}
|
||||
else if (junitExpectedException != null) {
|
||||
expectedException = junitExpectedException;
|
||||
}
|
||||
|
||||
return expectedException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the actual {@link Method method} referenced by this test method.
|
||||
*/
|
||||
public final Method getMethod() {
|
||||
return this.method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link TestClass test class} for this test method.
|
||||
*/
|
||||
public final TestClass getTestClass() {
|
||||
return this.testClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configured <code>timeout</code> for this test method.
|
||||
* <p>Supports JUnit's {@link Test#timeout() @Test(timeout=...)} annotation.
|
||||
* @return the timeout, or <code>0</code> if none was specified
|
||||
*/
|
||||
public long getTimeout() {
|
||||
Test testAnnotation = getMethod().getAnnotation(Test.class);
|
||||
return (testAnnotation != null && testAnnotation.timeout() > 0 ? testAnnotation.timeout() : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for {@link Method#invoke(Object,Object...) invoking}
|
||||
* the method associated with this test method. Throws exceptions consistent
|
||||
* with {@link Method#invoke(Object,Object...) Method.invoke()}.
|
||||
* @param testInstance the test instance upon which to invoke the method
|
||||
*/
|
||||
public void invoke(Object testInstance) throws IllegalAccessException, InvocationTargetException {
|
||||
getMethod().invoke(testInstance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this test method should be ignored.
|
||||
* @return <code>true</code> if this test method should be ignored
|
||||
* @see ProfileValueUtils#isTestEnabledInThisEnvironment
|
||||
*/
|
||||
public boolean isIgnored() {
|
||||
return (getMethod().isAnnotationPresent(Ignore.class) ||
|
||||
!ProfileValueUtils.isTestEnabledInThisEnvironment(this.method, this.testClass.getJavaClass()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this test method {@link Test#expected() expects} exceptions
|
||||
* of the type of the supplied <code>exception</code> to be thrown.
|
||||
* @param exception the thrown exception
|
||||
* @return <code>true</code> if the supplied exception was of an expected type
|
||||
*/
|
||||
public boolean isUnexpected(Throwable exception) {
|
||||
return !getExpectedException().isAssignableFrom(exception.getClass());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
<p>Support classes for ApplicationContext-based and transactional
|
||||
tests run with JUnit 4.4 and the <em>Spring TestContext Framework</em>.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
<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.4, 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>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.io.support.ResourcePatternUtils;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
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, which provides a basis for all concrete
|
||||
* implementations of the {@link ContextLoader} strategy. Provides a
|
||||
* <em>Template Method</em> based approach for
|
||||
* {@link #processLocations(Class,String...) processing} locations.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see #generateDefaultLocations
|
||||
* @see #modifyLocations
|
||||
*/
|
||||
public abstract class AbstractContextLoader implements ContextLoader {
|
||||
|
||||
/**
|
||||
* If the supplied <code>locations</code> are <code>null</code> or
|
||||
* <em>empty</em> and {@link #isGenerateDefaultLocations()} is
|
||||
* <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(Class,String...) 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 an array of application context resource locations
|
||||
* @see #generateDefaultLocations
|
||||
* @see #modifyLocations
|
||||
* @see org.springframework.test.context.ContextLoader#processLocations
|
||||
*/
|
||||
public final String[] processLocations(Class<?> clazz, String... locations) {
|
||||
return (ObjectUtils.isEmpty(locations) && isGenerateDefaultLocations()) ?
|
||||
generateDefaultLocations(clazz) : modifyLocations(clazz, locations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates 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>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
|
||||
* @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");
|
||||
return new String[] { ResourceUtils.CLASSPATH_URL_PREFIX + "/" +
|
||||
ClassUtils.convertClassNameToResourcePath(clazz.getName()) + suffix };
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a modified version of the supplied locations array and returns it.
|
||||
* <p>A plain path, e.g. "context.xml", will be treated as a
|
||||
* classpath resource from the same package in which the specified class is
|
||||
* defined. A path starting with a slash is treated as a fully qualified
|
||||
* class path location, e.g.:
|
||||
* "/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
|
||||
*/
|
||||
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("/")) {
|
||||
modifiedLocations[i] = ResourceUtils.CLASSPATH_URL_PREFIX + path;
|
||||
}
|
||||
else if (!ResourcePatternUtils.isUrl(path)) {
|
||||
modifiedLocations[i] = ResourceUtils.CLASSPATH_URL_PREFIX + "/"
|
||||
+ StringUtils.cleanPath(ClassUtils.classPackageAsResourcePath(clazz) + "/" + 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(Class,String...) processLocations()} are
|
||||
* <code>null</code> or empty.
|
||||
* <p>Can be overridden by subclasses to change the default behavior.
|
||||
* @return always <code>true</code> by default
|
||||
*/
|
||||
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
|
||||
* @see #generateDefaultLocations(Class)
|
||||
*/
|
||||
protected abstract String getResourceSuffix();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* 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.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.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Abstract, generic extension of {@link AbstractContextLoader} which loads a
|
||||
* {@link GenericApplicationContext} from the <em>locations</em> provided to
|
||||
* {@link #loadContext(String...)}.
|
||||
*
|
||||
* <p>Concrete subclasses must provide an appropriate
|
||||
* {@link #createBeanDefinitionReader(GenericApplicationContext) BeanDefinitionReader}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see #loadContext(String...)
|
||||
*/
|
||||
public abstract class AbstractGenericContextLoader extends AbstractContextLoader {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(AbstractGenericContextLoader.class);
|
||||
|
||||
|
||||
/**
|
||||
* Loads a Spring ApplicationContext from the supplied <code>locations</code>.
|
||||
* <p>Implementation details:
|
||||
* <ul>
|
||||
* <li>Creates a standard {@link GenericApplicationContext} instance.</li>
|
||||
* <li>Populates it from the specified config locations through a
|
||||
* {@link #createBeanDefinitionReader(GenericApplicationContext) BeanDefinitionReader}.</li>
|
||||
* <li>Calls {@link #customizeBeanFactory(DefaultListableBeanFactory)} to
|
||||
* allow for customizing the context's DefaultListableBeanFactory.</li>
|
||||
* <li>Delegates to {@link AnnotationConfigUtils} for
|
||||
* {@link AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry) registering}
|
||||
* annotation configuration processors.</li>
|
||||
* <li>Calls {@link #customizeContext(GenericApplicationContext)} 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>Subclasses must provide an appropriate implementation of
|
||||
* {@link #createBeanDefinitionReader(GenericApplicationContext)}.
|
||||
* @return a new application context
|
||||
* @see org.springframework.test.context.ContextLoader#loadContext
|
||||
* @see GenericApplicationContext
|
||||
* @see #customizeBeanFactory(DefaultListableBeanFactory)
|
||||
* @see #createBeanDefinitionReader(GenericApplicationContext)
|
||||
* @see BeanDefinitionReader
|
||||
*/
|
||||
public final ConfigurableApplicationContext loadContext(String... locations) throws Exception {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Loading ApplicationContext for locations [" +
|
||||
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 ContextLoader.
|
||||
* Called <i>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 #loadContext
|
||||
* @see org.springframework.context.support.GenericApplicationContext#setResourceLoader
|
||||
* @see org.springframework.context.support.GenericApplicationContext#setId
|
||||
*/
|
||||
protected void prepareContext(GenericApplicationContext context) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize the internal bean factory of the ApplicationContext created by
|
||||
* this ContextLoader.
|
||||
* <p>The default implementation is empty but can be overridden in subclasses
|
||||
* to customize DefaultListableBeanFactory's standard settings.
|
||||
* @param beanFactory the bean factory created by this ContextLoader
|
||||
* @see #loadContext
|
||||
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowBeanDefinitionOverriding(boolean)
|
||||
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowEagerClassLoading(boolean)
|
||||
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowCircularReferences(boolean)
|
||||
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping(boolean)
|
||||
*/
|
||||
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for creating new {@link BeanDefinitionReader}s for
|
||||
* loading bean definitions into the supplied
|
||||
* {@link GenericApplicationContext context}.
|
||||
* @param context the context for which the BeanDefinitionReader should be created
|
||||
* @return a BeanDefinitionReader for the supplied context
|
||||
* @see #loadContext
|
||||
* @see BeanDefinitionReader
|
||||
*/
|
||||
protected abstract BeanDefinitionReader createBeanDefinitionReader(GenericApplicationContext context);
|
||||
|
||||
/**
|
||||
* Customize the {@link GenericApplicationContext} created by this ContextLoader
|
||||
* <i>after</i> bean definitions have been loaded into the context but
|
||||
* before 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(String...)
|
||||
*/
|
||||
protected void customizeContext(GenericApplicationContext context) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.test.context.TestExecutionListener;
|
||||
|
||||
/**
|
||||
* Abstract implementation of the {@link TestExecutionListener} interface which
|
||||
* provides empty method stubs. Subclasses can extend this class and override
|
||||
* only those methods suitable for the task at hand.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
*/
|
||||
public abstract class AbstractTestExecutionListener implements TestExecutionListener {
|
||||
|
||||
/**
|
||||
* The default implementation is <em>empty</em>. Can be overridden by
|
||||
* subclasses as necessary.
|
||||
*/
|
||||
public void prepareTestInstance(TestContext testContext) throws Exception {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
/**
|
||||
* The default implementation is <em>empty</em>. Can be overridden by
|
||||
* subclasses as necessary.
|
||||
*/
|
||||
public void beforeTestMethod(TestContext testContext) throws Exception {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
/**
|
||||
* The default implementation is <em>empty</em>. Can be overridden by
|
||||
* subclasses as necessary.
|
||||
*/
|
||||
public void afterTestMethod(TestContext testContext) throws Exception {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.core.Conventions;
|
||||
import org.springframework.test.context.TestContext;
|
||||
|
||||
/**
|
||||
* <code>TestExecutionListener</code> which provides support for dependency
|
||||
* injection and initialization of test instances.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
*/
|
||||
public class DependencyInjectionTestExecutionListener extends AbstractTestExecutionListener {
|
||||
|
||||
/**
|
||||
* Attribute name for a {@link TestContext} attribute which indicates
|
||||
* whether or not the dependencies of a test instance should be
|
||||
* <em>reinjected</em> in
|
||||
* {@link #beforeTestMethod(TestContext) beforeTestMethod()}. Note that
|
||||
* dependencies will be injected in
|
||||
* {@link #prepareTestInstance(TestContext) prepareTestInstance()} in any
|
||||
* case.
|
||||
* <p>Clients of a {@link TestContext} (e.g., other
|
||||
* {@link org.springframework.test.context.TestExecutionListener TestExecutionListeners})
|
||||
* may therefore choose to set this attribute to signal that dependencies
|
||||
* should be reinjected <em>between</em> execution of individual test
|
||||
* methods.
|
||||
* <p>Permissible values include {@link Boolean#TRUE} and {@link Boolean#FALSE}.
|
||||
*/
|
||||
public static final String REINJECT_DEPENDENCIES_ATTRIBUTE = Conventions.getQualifiedAttributeName(
|
||||
DependencyInjectionTestExecutionListener.class, "reinjectDependencies");
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DependencyInjectionTestExecutionListener.class);
|
||||
|
||||
|
||||
/**
|
||||
* Performs dependency injection on the
|
||||
* {@link TestContext#getTestInstance() test instance} of the supplied
|
||||
* {@link TestContext test context} by
|
||||
* {@link AutowireCapableBeanFactory#autowireBeanProperties(Object, int, boolean) autowiring}
|
||||
* and
|
||||
* {@link AutowireCapableBeanFactory#initializeBean(Object, String) initializing}
|
||||
* the test instance via its own
|
||||
* {@link TestContext#getApplicationContext() application context} (without
|
||||
* checking dependencies).
|
||||
* <p>The {@link #REINJECT_DEPENDENCIES_ATTRIBUTE} will be subsequently removed
|
||||
* from the test context, regardless of its value.
|
||||
*/
|
||||
@Override
|
||||
public void prepareTestInstance(final TestContext testContext) throws Exception {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Performing dependency injection for test context [" + testContext + "].");
|
||||
}
|
||||
injectDependencies(testContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the {@link #REINJECT_DEPENDENCIES_ATTRIBUTE} in the supplied
|
||||
* {@link TestContext test context} has a value of {@link Boolean#TRUE},
|
||||
* this method will have the same effect as
|
||||
* {@link #prepareTestInstance(TestContext) prepareTestInstance()};
|
||||
* otherwise, this method will have no effect.
|
||||
*/
|
||||
@Override
|
||||
public void beforeTestMethod(final TestContext testContext) throws Exception {
|
||||
if (Boolean.TRUE.equals(testContext.getAttribute(REINJECT_DEPENDENCIES_ATTRIBUTE))) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Reinjecting dependencies for test context [" + testContext + "].");
|
||||
}
|
||||
injectDependencies(testContext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs dependency injection and bean initialization for the supplied
|
||||
* {@link TestContext} as described in
|
||||
* {@link #prepareTestInstance(TestContext) prepareTestInstance()}.
|
||||
* <p>The {@link #REINJECT_DEPENDENCIES_ATTRIBUTE} will be subsequently removed
|
||||
* from the test context, regardless of its value.
|
||||
* @param testContext the test context for which dependency injection should
|
||||
* be performed (never <code>null</code>)
|
||||
* @throws Exception allows any exception to propagate
|
||||
* @see #prepareTestInstance(TestContext)
|
||||
* @see #beforeTestMethod(TestContext)
|
||||
*/
|
||||
protected void injectDependencies(final TestContext testContext) throws Exception {
|
||||
Object bean = testContext.getTestInstance();
|
||||
AutowireCapableBeanFactory beanFactory = testContext.getApplicationContext().getAutowireCapableBeanFactory();
|
||||
beanFactory.autowireBeanProperties(bean, AutowireCapableBeanFactory.AUTOWIRE_NO, false);
|
||||
beanFactory.initializeBean(bean, testContext.getTestClass().getName());
|
||||
testContext.removeAttribute(REINJECT_DEPENDENCIES_ATTRIBUTE);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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.support;
|
||||
|
||||
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.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* <code>TestExecutionListener</code> which processes test methods configured
|
||||
* with the {@link DirtiesContext @DirtiesContext} annotation.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see DirtiesContext
|
||||
*/
|
||||
public class DirtiesContextTestExecutionListener extends AbstractTestExecutionListener {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DirtiesContextTestExecutionListener.class);
|
||||
|
||||
|
||||
/**
|
||||
* If the current test method of the supplied
|
||||
* {@link TestContext test context} has been annotated with
|
||||
* {@link DirtiesContext @DirtiesContext}, the
|
||||
* {@link ApplicationContext application context} of the test context will
|
||||
* be {@link TestContext#markApplicationContextDirty() marked as dirty},
|
||||
* and the
|
||||
* {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE}
|
||||
* will be set to <code>true</code> in the test context.
|
||||
*/
|
||||
@Override
|
||||
public void afterTestMethod(TestContext testContext) throws Exception {
|
||||
Method testMethod = testContext.getTestMethod();
|
||||
Assert.notNull(testMethod, "The test method of the supplied TestContext must not be null");
|
||||
|
||||
boolean dirtiesContext = testMethod.isAnnotationPresent(DirtiesContext.class);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("After test method: context [" + testContext + "], dirtiesContext [" + dirtiesContext + "].");
|
||||
}
|
||||
|
||||
if (dirtiesContext) {
|
||||
testContext.markApplicationContextDirty();
|
||||
testContext.setAttribute(DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE,
|
||||
Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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.context.support;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReader;
|
||||
import org.springframework.beans.factory.support.PropertiesBeanDefinitionReader;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Concrete implementation of {@link AbstractGenericContextLoader} which reads
|
||||
* bean definitions from Java {@link Properties} resources.
|
||||
* </p>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 2.5
|
||||
*/
|
||||
public class GenericPropertiesContextLoader extends AbstractGenericContextLoader {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Creates a new {@link PropertiesBeanDefinitionReader}.
|
||||
* </p>
|
||||
*
|
||||
* @return a new PropertiesBeanDefinitionReader.
|
||||
* @see AbstractGenericContextLoader#createBeanDefinitionReader(GenericApplicationContext)
|
||||
* @see PropertiesBeanDefinitionReader
|
||||
*/
|
||||
@Override
|
||||
protected BeanDefinitionReader createBeanDefinitionReader(final GenericApplicationContext context) {
|
||||
return new PropertiesBeanDefinitionReader(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns "<code>-context.properties</code>".
|
||||
*
|
||||
* @see org.springframework.test.context.support.AbstractContextLoader#getResourceSuffix()
|
||||
*/
|
||||
@Override
|
||||
public String getResourceSuffix() {
|
||||
return "-context.properties";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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.context.support;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReader;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Concrete implementation of {@link AbstractGenericContextLoader} which reads
|
||||
* bean definitions from XML resources.
|
||||
* </p>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 2.5
|
||||
*/
|
||||
public class GenericXmlContextLoader extends AbstractGenericContextLoader {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Creates a new {@link XmlBeanDefinitionReader}.
|
||||
* </p>
|
||||
*
|
||||
* @return a new XmlBeanDefinitionReader.
|
||||
* @see AbstractGenericContextLoader#createBeanDefinitionReader(GenericApplicationContext)
|
||||
* @see XmlBeanDefinitionReader
|
||||
*/
|
||||
@Override
|
||||
protected BeanDefinitionReader createBeanDefinitionReader(final GenericApplicationContext context) {
|
||||
return new XmlBeanDefinitionReader(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns "<code>-context.xml</code>".
|
||||
*
|
||||
* @see org.springframework.test.context.support.AbstractContextLoader#getResourceSuffix()
|
||||
*/
|
||||
@Override
|
||||
public String getResourceSuffix() {
|
||||
return "-context.xml";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
<p>Support classes for the <em>Spring TestContext Framework</em>.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* 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.testng;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.testng.IHookCallBack;
|
||||
import org.testng.IHookable;
|
||||
import org.testng.ITestResult;
|
||||
import org.testng.annotations.AfterMethod;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
|
||||
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>TestNG</strong>
|
||||
* environment.
|
||||
* </p>
|
||||
* <p>
|
||||
* Concrete subclasses:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>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></li>
|
||||
* <li>Must have constructors which either implicitly or explicitly delegate to
|
||||
* <code>super();</code>.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see TestContext
|
||||
* @see TestContextManager
|
||||
* @see TestExecutionListeners
|
||||
* @see AbstractTransactionalTestNGSpringContextTests
|
||||
* @see org.springframework.test.context.junit38.AbstractJUnit38SpringContextTests
|
||||
* @see org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests
|
||||
*/
|
||||
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class})
|
||||
public abstract class AbstractTestNGSpringContextTests implements IHookable, 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;
|
||||
|
||||
private final TestContextManager testContextManager;
|
||||
|
||||
private Throwable testException;
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new AbstractTestNGSpringContextTests instance and
|
||||
* initializes the internal {@link TestContextManager} for the current test.
|
||||
*/
|
||||
public AbstractTestNGSpringContextTests() {
|
||||
this.testContextManager = new TestContextManager(getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ApplicationContext} to be used by this test instance,
|
||||
* provided via {@link ApplicationContextAware} semantics.
|
||||
* @param applicationContext the applicationContext to set
|
||||
*/
|
||||
public final void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delegates to the configured {@link TestContextManager} to
|
||||
* {@link TestContextManager#prepareTestInstance(Object) prepare} this test
|
||||
* instance prior to execution of any individual tests, for example for
|
||||
* injecting dependencies, etc.
|
||||
* @throws Exception if a registered TestExecutionListener throws an exception
|
||||
*/
|
||||
@BeforeClass(alwaysRun = true)
|
||||
protected void springTestContextPrepareTestInstance() throws Exception {
|
||||
this.testContextManager.prepareTestInstance(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to the configured {@link TestContextManager} to
|
||||
* {@link TestContextManager#beforeTestMethod(Object,Method) pre-process}
|
||||
* the test method before the actual test is executed.
|
||||
* @param testMethod the test method which is about to be executed.
|
||||
* @throws Exception allows all exceptions to propagate.
|
||||
*/
|
||||
@BeforeMethod(alwaysRun = true)
|
||||
protected void springTestContextBeforeTestMethod(Method testMethod) throws Exception {
|
||||
this.testContextManager.beforeTestMethod(this, testMethod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to the
|
||||
* {@link IHookCallBack#runTestMethod(ITestResult) test method} in the
|
||||
* supplied <code>callback</code> to execute the actual test and then
|
||||
* tracks the exception thrown during test execution, if any.
|
||||
* @see org.testng.IHookable#run(org.testng.IHookCallBack, org.testng.ITestResult)
|
||||
*/
|
||||
public void run(IHookCallBack callBack, ITestResult testResult) {
|
||||
callBack.runTestMethod(testResult);
|
||||
this.testException = testResult.getThrowable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to the configured {@link TestContextManager} to
|
||||
* {@link TestContextManager#afterTestMethod(Object, Method, Throwable) post-process}
|
||||
* the test method after the actual test has executed.
|
||||
* @param testMethod the test method which has just been executed on the test instance
|
||||
* @throws Exception allows all exceptions to propagate
|
||||
*/
|
||||
@AfterMethod(alwaysRun = true)
|
||||
protected void springTestContextAfterTestMethod(Method testMethod) throws Exception {
|
||||
try {
|
||||
this.testContextManager.afterTestMethod(this, testMethod, this.testException);
|
||||
}
|
||||
finally {
|
||||
this.testException = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* 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.testng;
|
||||
|
||||
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 AbstractTestNGSpringContextTests} 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 AbstractTestNGSpringContextTests}.
|
||||
* </p>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see AbstractTestNGSpringContextTests
|
||||
* @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.jdbc.SimpleJdbcTestUtils
|
||||
* @see org.springframework.test.context.junit38.AbstractTransactionalJUnit38SpringContextTests
|
||||
* @see org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests
|
||||
*/
|
||||
@TestExecutionListeners({TransactionalTestExecutionListener.class})
|
||||
@Transactional
|
||||
public abstract class AbstractTransactionalTestNGSpringContextTests extends AbstractTestNGSpringContextTests {
|
||||
|
||||
/**
|
||||
* The SimpleJdbcTemplate that this base class manages, available to subclasses.
|
||||
*/
|
||||
protected SimpleJdbcTemplate simpleJdbcTemplate;
|
||||
|
||||
private String sqlScriptEncoding;
|
||||
|
||||
|
||||
/**
|
||||
* Set the DataSource, typically provided via Dependency Injection.
|
||||
* @param dataSource the DataSource to inject
|
||||
*/
|
||||
@Autowired
|
||||
public void setDataSource(final 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,8 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
<p>Support classes for ApplicationContext-based and transactional
|
||||
tests run with TestNG and the <em>Spring TestContext Framework</em>.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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.context.transaction;
|
||||
|
||||
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 annotation to indicate that the annotated <code>public void</code>
|
||||
* method should be executed <em>after</em> a transaction is ended for test
|
||||
* methods configured to run within a transaction via the
|
||||
* <code>@Transactional</code> annotation.
|
||||
* </p>
|
||||
* <p>
|
||||
* The <code>@AfterTransaction</code> methods of superclasses will be
|
||||
* executed after those of the current class.
|
||||
* </p>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 2.5
|
||||
* @see org.springframework.transaction.annotation.Transactional
|
||||
*/
|
||||
@Target( { ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface AfterTransaction {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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.context.transaction;
|
||||
|
||||
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 annotation to indicate that the annotated <code>public void</code>
|
||||
* method should be executed <em>before</em> a transaction is started for test
|
||||
* methods configured to run within a transaction via the
|
||||
* <code>@Transactional</code> annotation.
|
||||
* </p>
|
||||
* <p>
|
||||
* The <code>@BeforeTransaction</code> methods of superclasses will be
|
||||
* executed before those of the current class.
|
||||
* </p>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 2.5
|
||||
* @see org.springframework.transaction.annotation.Transactional
|
||||
*/
|
||||
@Target( { ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface BeforeTransaction {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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.transaction;
|
||||
|
||||
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;
|
||||
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
/**
|
||||
* TransactionConfiguration defines class-level metadata for configuring
|
||||
* transactional tests.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 2.5
|
||||
* @see ContextConfiguration
|
||||
* @see TransactionalTestExecutionListener
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface TransactionConfiguration {
|
||||
|
||||
/**
|
||||
* The bean name of the {@link PlatformTransactionManager} that is to be
|
||||
* used to drive transactions. This attribute is not required and only needs
|
||||
* to be specified explicitly if the bean name of the desired
|
||||
* PlatformTransactionManager is not "transactionManager".
|
||||
*/
|
||||
String transactionManager() default "transactionManager";
|
||||
|
||||
/**
|
||||
* Should transactions be rolled back by default?
|
||||
*/
|
||||
boolean defaultRollback() default true;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* 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.transaction;
|
||||
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Configuration attributes for configuring transactional tests.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see TransactionConfiguration
|
||||
*/
|
||||
public class TransactionConfigurationAttributes {
|
||||
|
||||
private final String transactionManagerName;
|
||||
|
||||
private final boolean defaultRollback;
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new TransactionConfigurationAttributes instance from the
|
||||
* supplied arguments.
|
||||
* @param transactionManagerName the bean name of the
|
||||
* {@link PlatformTransactionManager} that is to be used to drive transactions
|
||||
* @param defaultRollback whether or not transactions should be rolled back by default
|
||||
*/
|
||||
public TransactionConfigurationAttributes(String transactionManagerName, boolean defaultRollback) {
|
||||
Assert.notNull(transactionManagerName, "transactionManagerName can not be null");
|
||||
this.transactionManagerName = transactionManagerName;
|
||||
this.defaultRollback = defaultRollback;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the bean name of the {@link PlatformTransactionManager} that is to
|
||||
* be used to drive transactions.
|
||||
*/
|
||||
public final String getTransactionManagerName() {
|
||||
return this.transactionManagerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not transactions should be rolled back by default.
|
||||
* @return the <em>default rollback</em> flag
|
||||
*/
|
||||
public final boolean isDefaultRollback() {
|
||||
return this.defaultRollback;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this)
|
||||
.append("transactionManagerName", this.transactionManagerName)
|
||||
.append("defaultRollback", this.defaultRollback)
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,509 @@
|
|||
/*
|
||||
* 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.transaction;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.test.annotation.NotTransactional;
|
||||
import org.springframework.test.annotation.Rollback;
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.test.context.support.AbstractTestExecutionListener;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionException;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
|
||||
import org.springframework.transaction.interceptor.DelegatingTransactionAttribute;
|
||||
import org.springframework.transaction.interceptor.TransactionAttribute;
|
||||
import org.springframework.transaction.interceptor.TransactionAttributeSource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* <code>TestExecutionListener</code> which provides support for executing
|
||||
* tests within transactions by using
|
||||
* {@link org.springframework.transaction.annotation.Transactional @Transactional}
|
||||
* and {@link NotTransactional @NotTransactional} annotations.
|
||||
* </p>
|
||||
* <p>
|
||||
* Changes to the database during a test run with @Transactional will be
|
||||
* run within a transaction that will, by default, be automatically
|
||||
* <em>rolled back</em> after completion of the test; whereas, changes to the
|
||||
* database during a test run with @NotTransactional will <strong>not</strong>
|
||||
* be run within a transaction. Similarly, test methods that are not annotated
|
||||
* with either @Transactional (at the class or method level) or
|
||||
* @NotTransactional will not be run within a transaction.
|
||||
* </p>
|
||||
* <p>
|
||||
* Transactional commit and rollback behavior can be configured via the
|
||||
* class-level {@link TransactionConfiguration @TransactionConfiguration} and
|
||||
* method-level {@link Rollback @Rollback} annotations.
|
||||
* {@link TransactionConfiguration @TransactionConfiguration} also provides
|
||||
* configuration of the bean name of the {@link PlatformTransactionManager} that
|
||||
* is to be used to drive transactions.
|
||||
* </p>
|
||||
* <p>
|
||||
* When executing transactional tests, it is sometimes useful to be able execute
|
||||
* certain <em>set up</em> or <em>tear down</em> code outside of a
|
||||
* transaction. <code>TransactionalTestExecutionListener</code> provides such
|
||||
* support for methods annotated with
|
||||
* {@link BeforeTransaction @BeforeTransaction} and
|
||||
* {@link AfterTransaction @AfterTransaction}.
|
||||
* </p>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see TransactionConfiguration
|
||||
* @see org.springframework.transaction.annotation.Transactional
|
||||
* @see org.springframework.test.annotation.NotTransactional
|
||||
* @see org.springframework.test.annotation.Rollback
|
||||
* @see BeforeTransaction
|
||||
* @see AfterTransaction
|
||||
*/
|
||||
public class TransactionalTestExecutionListener extends AbstractTestExecutionListener {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(TransactionalTestExecutionListener.class);
|
||||
|
||||
protected final TransactionAttributeSource attributeSource = new AnnotationTransactionAttributeSource();
|
||||
|
||||
private TransactionConfigurationAttributes configAttributes;
|
||||
|
||||
private volatile int transactionsStarted = 0;
|
||||
|
||||
private final Map<Method, TransactionContext> transactionContextCache =
|
||||
Collections.synchronizedMap(new IdentityHashMap<Method, TransactionContext>());
|
||||
|
||||
|
||||
/**
|
||||
* If the test method of the supplied {@link TestContext test context} is
|
||||
* configured to run within a transaction, this method will run
|
||||
* {@link BeforeTransaction @BeforeTransaction methods} and start a new
|
||||
* transaction.
|
||||
* <p>Note that if a {@link BeforeTransaction @BeforeTransaction method} fails,
|
||||
* remaining {@link BeforeTransaction @BeforeTransaction methods} will not
|
||||
* be invoked, and a transaction will not be started.
|
||||
* @see org.springframework.transaction.annotation.Transactional
|
||||
* @see org.springframework.test.annotation.NotTransactional
|
||||
*/
|
||||
@Override
|
||||
public void beforeTestMethod(TestContext testContext) throws Exception {
|
||||
final Method testMethod = testContext.getTestMethod();
|
||||
Assert.notNull(testMethod, "The test method of the supplied TestContext must not be null");
|
||||
|
||||
if (this.transactionContextCache.remove(testMethod) != null) {
|
||||
throw new IllegalStateException("Cannot start new transaction without ending existing transaction: " +
|
||||
"Invoke endTransaction() before startNewTransaction().");
|
||||
}
|
||||
|
||||
if (testMethod.isAnnotationPresent(NotTransactional.class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TransactionAttribute transactionAttribute =
|
||||
this.attributeSource.getTransactionAttribute(testMethod, testContext.getTestClass());
|
||||
TransactionDefinition transactionDefinition = null;
|
||||
if (transactionAttribute != null) {
|
||||
transactionDefinition = new DelegatingTransactionAttribute(transactionAttribute) {
|
||||
public String getName() {
|
||||
return testMethod.getName();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (transactionDefinition != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Explicit transaction definition [" + transactionDefinition +
|
||||
"] found for test context [" + testContext + "]");
|
||||
}
|
||||
TransactionContext txContext =
|
||||
new TransactionContext(getTransactionManager(testContext), transactionDefinition);
|
||||
runBeforeTransactionMethods(testContext);
|
||||
startNewTransaction(testContext, txContext);
|
||||
this.transactionContextCache.put(testMethod, txContext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If a transaction is currently active for the test method of the supplied
|
||||
* {@link TestContext test context}, this method will end the transaction
|
||||
* and run {@link AfterTransaction @AfterTransaction methods}.
|
||||
* <p>{@link AfterTransaction @AfterTransaction methods} are guaranteed to be
|
||||
* invoked even if an error occurs while ending the transaction.
|
||||
*/
|
||||
@Override
|
||||
public void afterTestMethod(TestContext testContext) throws Exception {
|
||||
Method testMethod = testContext.getTestMethod();
|
||||
Assert.notNull(testMethod, "The test method of the supplied TestContext must not be null");
|
||||
|
||||
// If the transaction is still active...
|
||||
TransactionContext txContext = this.transactionContextCache.remove(testMethod);
|
||||
if (txContext != null && !txContext.transactionStatus.isCompleted()) {
|
||||
try {
|
||||
endTransaction(testContext, txContext);
|
||||
}
|
||||
finally {
|
||||
runAfterTransactionMethods(testContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all {@link BeforeTransaction @BeforeTransaction methods} for the
|
||||
* specified {@link TestContext test context}. If one of the methods fails,
|
||||
* however, the caught exception will be rethrown in a wrapped
|
||||
* {@link RuntimeException}, and the remaining methods will <strong>not</strong>
|
||||
* be given a chance to execute.
|
||||
* @param testContext the current test context
|
||||
*/
|
||||
protected void runBeforeTransactionMethods(TestContext testContext) throws Exception {
|
||||
try {
|
||||
List<Method> methods = getAnnotatedMethods(testContext.getTestClass(), BeforeTransaction.class);
|
||||
Collections.reverse(methods);
|
||||
for (Method method : methods) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Executing @BeforeTransaction method [" + method + "] for test context ["
|
||||
+ testContext + "]");
|
||||
}
|
||||
method.invoke(testContext.getTestInstance());
|
||||
}
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
logger.error("Exception encountered while executing @BeforeTransaction methods for test context ["
|
||||
+ testContext + "]", ex.getTargetException());
|
||||
ReflectionUtils.rethrowException(ex.getTargetException());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all {@link AfterTransaction @AfterTransaction methods} for the
|
||||
* specified {@link TestContext test context}. If one of the methods fails,
|
||||
* the caught exception will be logged as an error, and the remaining
|
||||
* methods will be given a chance to execute. After all methods have
|
||||
* executed, the first caught exception, if any, will be rethrown.
|
||||
* @param testContext the current test context
|
||||
*/
|
||||
protected void runAfterTransactionMethods(TestContext testContext) throws Exception {
|
||||
Throwable afterTransactionException = null;
|
||||
|
||||
List<Method> methods = getAnnotatedMethods(testContext.getTestClass(), AfterTransaction.class);
|
||||
for (Method method : methods) {
|
||||
try {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Executing @AfterTransaction method [" + method + "] for test context [" +
|
||||
testContext + "]");
|
||||
}
|
||||
method.invoke(testContext.getTestInstance());
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
Throwable targetException = ex.getTargetException();
|
||||
if (afterTransactionException == null) {
|
||||
afterTransactionException = targetException;
|
||||
}
|
||||
logger.error("Exception encountered while executing @AfterTransaction method [" + method +
|
||||
"] for test context [" + testContext + "]", targetException);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
if (afterTransactionException == null) {
|
||||
afterTransactionException = ex;
|
||||
}
|
||||
logger.error("Exception encountered while executing @AfterTransaction method [" + method +
|
||||
"] for test context [" + testContext + "]", ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (afterTransactionException != null) {
|
||||
ReflectionUtils.rethrowException(afterTransactionException);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a new transaction for the supplied {@link TestContext test context}.
|
||||
* <p>Only call this method if {@link #endTransaction} has been called or if no
|
||||
* transaction has been previously started.
|
||||
* @param testContext the current test context
|
||||
* @throws TransactionException if starting the transaction fails
|
||||
* @throws Exception if an error occurs while retrieving the transaction manager
|
||||
*/
|
||||
private void startNewTransaction(TestContext testContext, TransactionContext txContext) throws Exception {
|
||||
txContext.startTransaction();
|
||||
++this.transactionsStarted;
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Began transaction (" + this.transactionsStarted + "): transaction manager [" +
|
||||
txContext.transactionManager + "]; rollback [" + isRollback(testContext) + "]");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Immediately force a <em>commit</em> or <em>rollback</em> of the
|
||||
* transaction for the supplied {@link TestContext test context}, according
|
||||
* to the commit and rollback flags.
|
||||
* @param testContext the current test context
|
||||
* @throws Exception if an error occurs while retrieving the transaction manager
|
||||
*/
|
||||
private void endTransaction(TestContext testContext, TransactionContext txContext) throws Exception {
|
||||
boolean rollback = isRollback(testContext);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Ending transaction for test context [" + testContext + "]; transaction manager [" +
|
||||
txContext.transactionStatus + "]; rollback [" + rollback + "]");
|
||||
}
|
||||
txContext.endTransaction(rollback);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info((rollback ? "Rolled back" : "Committed") +
|
||||
" transaction after test execution for test context [" + testContext + "]");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link PlatformTransactionManager transaction manager} to use
|
||||
* for the supplied {@link TestContext test context}.
|
||||
* @param testContext the test context for which the transaction manager
|
||||
* should be retrieved
|
||||
* @return the transaction manager to use, or <code>null</code> if not found
|
||||
* @throws BeansException if an error occurs while retrieving the transaction manager
|
||||
*/
|
||||
protected final PlatformTransactionManager getTransactionManager(TestContext testContext) {
|
||||
if (this.configAttributes == null) {
|
||||
this.configAttributes = retrieveTransactionConfigurationAttributes(testContext.getTestClass());
|
||||
}
|
||||
String transactionManagerName = this.configAttributes.getTransactionManagerName();
|
||||
try {
|
||||
return (PlatformTransactionManager) testContext.getApplicationContext().getBean(
|
||||
transactionManagerName, PlatformTransactionManager.class);
|
||||
}
|
||||
catch (BeansException ex) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Caught exception while retrieving transaction manager with bean name [" +
|
||||
transactionManagerName + "] for test context [" + testContext + "]", ex);
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not to rollback transactions by default for the
|
||||
* supplied {@link TestContext test context}.
|
||||
* @param testContext the test context for which the default rollback flag
|
||||
* should be retrieved
|
||||
* @return the <em>default rollback</em> flag for the supplied test context
|
||||
* @throws Exception if an error occurs while determining the default rollback flag
|
||||
*/
|
||||
protected final boolean isDefaultRollback(TestContext testContext) throws Exception {
|
||||
return retrieveTransactionConfigurationAttributes(testContext.getTestClass()).isDefaultRollback();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not to rollback transactions for the supplied
|
||||
* {@link TestContext test context} by taking into consideration the
|
||||
* {@link #isDefaultRollback(TestContext) default rollback} flag and a
|
||||
* possible method-level override via the {@link Rollback} annotation.
|
||||
* @param testContext the test context for which the rollback flag
|
||||
* should be retrieved
|
||||
* @return the <em>rollback</em> flag for the supplied test context
|
||||
* @throws Exception if an error occurs while determining the rollback flag
|
||||
*/
|
||||
protected final boolean isRollback(TestContext testContext) throws Exception {
|
||||
boolean rollback = isDefaultRollback(testContext);
|
||||
Rollback rollbackAnnotation = testContext.getTestMethod().getAnnotation(Rollback.class);
|
||||
if (rollbackAnnotation != null) {
|
||||
boolean rollbackOverride = rollbackAnnotation.value();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Method-level @Rollback(" + rollbackOverride + ") overrides default rollback [" + rollback
|
||||
+ "] for test context [" + testContext + "]");
|
||||
}
|
||||
rollback = rollbackOverride;
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("No method-level @Rollback override: using default rollback [" + rollback
|
||||
+ "] for test context [" + testContext + "]");
|
||||
}
|
||||
}
|
||||
return rollback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all superclasses of the supplied {@link Class class}, including the
|
||||
* class itself. The ordering of the returned list will begin with the
|
||||
* supplied class and continue up the class hierarchy.
|
||||
* <p>Note: This code has been borrowed from
|
||||
* {@link org.junit.internal.runners.TestClass#getSuperClasses(Class)} and
|
||||
* adapted.
|
||||
* @param clazz the class for which to retrieve the superclasses.
|
||||
* @return all superclasses of the supplied class.
|
||||
*/
|
||||
private List<Class<?>> getSuperClasses(Class<?> clazz) {
|
||||
ArrayList<Class<?>> results = new ArrayList<Class<?>>();
|
||||
Class<?> current = clazz;
|
||||
while (current != null) {
|
||||
results.add(current);
|
||||
current = current.getSuperclass();
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all methods in the supplied {@link Class class} and its superclasses
|
||||
* which are annotated with the supplied <code>annotationType</code> but
|
||||
* which are not <em>shadowed</em> by methods overridden in subclasses.
|
||||
* <p>Note: This code has been borrowed from
|
||||
* {@link org.junit.internal.runners.TestClass#getAnnotatedMethods(Class)}
|
||||
* and adapted.
|
||||
* @param clazz the class for which to retrieve the annotated methods
|
||||
* @param annotationType the annotation type for which to search
|
||||
* @return all annotated methods in the supplied class and its superclasses
|
||||
*/
|
||||
private List<Method> getAnnotatedMethods(Class<?> clazz, Class<? extends Annotation> annotationType) {
|
||||
List<Method> results = new ArrayList<Method>();
|
||||
for (Class<?> eachClass : getSuperClasses(clazz)) {
|
||||
Method[] methods = eachClass.getDeclaredMethods();
|
||||
for (Method eachMethod : methods) {
|
||||
Annotation annotation = eachMethod.getAnnotation(annotationType);
|
||||
if (annotation != null && !isShadowed(eachMethod, results)) {
|
||||
results.add(eachMethod);
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the supplied {@link Method method} is <em>shadowed</em>
|
||||
* by a method in supplied {@link List list} of previous methods.
|
||||
* <p>Note: This code has been borrowed from
|
||||
* {@link org.junit.internal.runners.TestClass#isShadowed(Method,List)}.
|
||||
* @param method the method to check for shadowing
|
||||
* @param previousMethods the list of methods which have previously been processed
|
||||
* @return <code>true</code> if the supplied method is shadowed by a
|
||||
* method in the <code>previousMethods</code> list
|
||||
*/
|
||||
private boolean isShadowed(Method method, List<Method> previousMethods) {
|
||||
for (Method each : previousMethods) {
|
||||
if (isShadowed(method, each)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the supplied {@link Method current method} is
|
||||
* <em>shadowed</em> by a {@link Method previous method}.
|
||||
* <p>Note: This code has been borrowed from
|
||||
* {@link org.junit.internal.runners.TestClass#isShadowed(Method,Method)}.
|
||||
* @param current the current method
|
||||
* @param previous the previous method
|
||||
* @return <code>true</code> if the previous method shadows the current one
|
||||
*/
|
||||
private boolean isShadowed(Method current, Method previous) {
|
||||
if (!previous.getName().equals(current.getName())) {
|
||||
return false;
|
||||
}
|
||||
if (previous.getParameterTypes().length != current.getParameterTypes().length) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < previous.getParameterTypes().length; i++) {
|
||||
if (!previous.getParameterTypes()[i].equals(current.getParameterTypes()[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Retrieves the {@link TransactionConfigurationAttributes} for the
|
||||
* specified {@link Class class} which may optionally declare or inherit a
|
||||
* {@link TransactionConfiguration @TransactionConfiguration}. If a
|
||||
* {@link TransactionConfiguration} annotation is not present for the
|
||||
* supplied class, the <em>default values</em> for attributes defined in
|
||||
* {@link TransactionConfiguration} will be used instead.
|
||||
* @param clazz the Class object corresponding to the test class for which
|
||||
* the configuration attributes should be retrieved
|
||||
* @return a new TransactionConfigurationAttributes instance
|
||||
*/
|
||||
private TransactionConfigurationAttributes retrieveTransactionConfigurationAttributes(Class<?> clazz) {
|
||||
Class<TransactionConfiguration> annotationType = TransactionConfiguration.class;
|
||||
TransactionConfiguration config = clazz.getAnnotation(annotationType);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Retrieved @TransactionConfiguration [" + config + "] for test class [" + clazz + "]");
|
||||
}
|
||||
|
||||
String transactionManagerName;
|
||||
boolean defaultRollback;
|
||||
if (config != null) {
|
||||
transactionManagerName = config.transactionManager();
|
||||
defaultRollback = config.defaultRollback();
|
||||
}
|
||||
else {
|
||||
transactionManagerName = (String) AnnotationUtils.getDefaultValue(annotationType, "transactionManager");
|
||||
defaultRollback = (Boolean) AnnotationUtils.getDefaultValue(annotationType, "defaultRollback");
|
||||
}
|
||||
|
||||
TransactionConfigurationAttributes configAttributes =
|
||||
new TransactionConfigurationAttributes(transactionManagerName, defaultRollback);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Retrieved TransactionConfigurationAttributes [" + configAttributes + "] for class [" + clazz + "]");
|
||||
}
|
||||
return configAttributes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal context holder for a specific test method.
|
||||
*/
|
||||
private static class TransactionContext {
|
||||
|
||||
private final PlatformTransactionManager transactionManager;
|
||||
|
||||
private final TransactionDefinition transactionDefinition;
|
||||
|
||||
private TransactionStatus transactionStatus;
|
||||
|
||||
public TransactionContext(PlatformTransactionManager transactionManager, TransactionDefinition transactionDefinition) {
|
||||
this.transactionManager = transactionManager;
|
||||
this.transactionDefinition = transactionDefinition;
|
||||
}
|
||||
|
||||
public void startTransaction() {
|
||||
this.transactionStatus = this.transactionManager.getTransaction(this.transactionDefinition);
|
||||
}
|
||||
|
||||
public void endTransaction(boolean rollback) {
|
||||
if (rollback) {
|
||||
this.transactionManager.rollback(this.transactionStatus);
|
||||
}
|
||||
else {
|
||||
this.transactionManager.commit(this.transactionStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
<p>Transactional support classes for the <em>Spring TestContext
|
||||
Framework</em>.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright 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.jdbc;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.io.LineNumberReader;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* JdbcTestUtils is a collection of JDBC related utility methods for
|
||||
* use in unit and integration testing scenarios.
|
||||
*
|
||||
* @author Thomas Risberg
|
||||
* @since 2.5.4
|
||||
*/
|
||||
public class JdbcTestUtils {
|
||||
|
||||
/**
|
||||
* Read a script from the LineNumberReaded and build a String containing the lines.
|
||||
*
|
||||
* @param lineNumberReader the <code>LineNumberReader</> containing the script to be processed
|
||||
* @return <code>String</code> containing the script lines
|
||||
* @throws IOException
|
||||
*/
|
||||
public static String readScript(LineNumberReader lineNumberReader) throws IOException {
|
||||
String currentStatement = lineNumberReader.readLine();
|
||||
StringBuffer scriptBuilder = new StringBuffer();
|
||||
while (currentStatement != null) {
|
||||
if (StringUtils.hasText(currentStatement)) {
|
||||
if (scriptBuilder.length() > 0) {
|
||||
scriptBuilder.append('\n');
|
||||
}
|
||||
scriptBuilder.append(currentStatement);
|
||||
}
|
||||
currentStatement = lineNumberReader.readLine();
|
||||
}
|
||||
return scriptBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the provided SQL script contain the specified delimiter?
|
||||
*
|
||||
* @param script the SQL script
|
||||
* @param delim charecter delimiting each statement - typically a ';' character
|
||||
*/
|
||||
public static boolean containsSqlScriptDelimiters(String script, char delim) {
|
||||
boolean inLiteral = false;
|
||||
char[] content = script.toCharArray();
|
||||
|
||||
for (int i = 0; i < script.length(); i++) {
|
||||
if (content[i] == '\'') {
|
||||
inLiteral = inLiteral ? false : true;
|
||||
}
|
||||
if (content[i] == delim && !inLiteral) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split an SQL script into separate statements delimited with the provided delimiter character. Each
|
||||
* individual statement will be added to the provided <code>List</code>.
|
||||
* @param script the SQL script
|
||||
* @param delim charecter delimiting each statement - typically a ';' character
|
||||
* @param statements the List that will contain the individual statements
|
||||
*/
|
||||
public static void splitSqlScript(String script, char delim, List statements) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
boolean inLiteral = false;
|
||||
char[] content = script.toCharArray();
|
||||
|
||||
for (int i = 0; i < script.length(); i++) {
|
||||
if (content[i] == '\'') {
|
||||
inLiteral = inLiteral ? false : true;
|
||||
}
|
||||
if (content[i] == delim && !inLiteral) {
|
||||
if (sb.length() > 0) {
|
||||
statements.add(sb.toString());
|
||||
sb = new StringBuffer();
|
||||
}
|
||||
}
|
||||
else {
|
||||
sb.append(content[i]);
|
||||
}
|
||||
}
|
||||
if (sb.length() > 0) {
|
||||
statements.add(sb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* 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.jdbc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.LineNumberReader;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.EncodedResource;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A Java-5-based collection of JDBC related utility functions intended to
|
||||
* simplify standard database testing scenarios.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @author Thomas Risberg
|
||||
* @since 2.5
|
||||
*/
|
||||
public abstract class SimpleJdbcTestUtils {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(SimpleJdbcTestUtils.class);
|
||||
|
||||
|
||||
/**
|
||||
* Count the rows in the given table.
|
||||
* @param simpleJdbcTemplate the SimpleJdbcTemplate with which to perform JDBC operations
|
||||
* @param tableName table name to count rows in
|
||||
* @return the number of rows in the table
|
||||
*/
|
||||
public static int countRowsInTable(SimpleJdbcTemplate simpleJdbcTemplate, String tableName) {
|
||||
return simpleJdbcTemplate.queryForInt("SELECT COUNT(0) FROM " + tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all rows from the specified tables.
|
||||
* @param simpleJdbcTemplate the SimpleJdbcTemplate with which to perform JDBC operations
|
||||
* @param tableNames the names of the tables from which to delete
|
||||
* @return the total number of rows deleted from all specified tables
|
||||
*/
|
||||
public static int deleteFromTables(SimpleJdbcTemplate simpleJdbcTemplate, String... tableNames) {
|
||||
int totalRowCount = 0;
|
||||
for (int i = 0; i < tableNames.length; i++) {
|
||||
int rowCount = simpleJdbcTemplate.update("DELETE FROM " + tableNames[i]);
|
||||
totalRowCount += rowCount;
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Deleted " + rowCount + " rows from table " + tableNames[i]);
|
||||
}
|
||||
}
|
||||
return totalRowCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given SQL script.
|
||||
* <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 simpleJdbcTemplate the SimpleJdbcTemplate with which to perform JDBC operations
|
||||
* @param resourceLoader the resource loader (with which to load the SQL script
|
||||
* @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>
|
||||
*/
|
||||
public static void executeSqlScript(SimpleJdbcTemplate simpleJdbcTemplate,
|
||||
ResourceLoader resourceLoader, String sqlResourcePath, boolean continueOnError)
|
||||
throws DataAccessException {
|
||||
|
||||
Resource resource = resourceLoader.getResource(sqlResourcePath);
|
||||
executeSqlScript(simpleJdbcTemplate, resource, continueOnError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given SQL script. The script will 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 simpleJdbcTemplate the SimpleJdbcTemplate with which to perform JDBC operations
|
||||
* @param resource the resource to load the SQL script from.
|
||||
* @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>
|
||||
*/
|
||||
public static void executeSqlScript(SimpleJdbcTemplate simpleJdbcTemplate,
|
||||
Resource resource, boolean continueOnError) throws DataAccessException {
|
||||
|
||||
executeSqlScript(simpleJdbcTemplate, new EncodedResource(resource), continueOnError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given SQL script.
|
||||
* <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 simpleJdbcTemplate the SimpleJdbcTemplate with which to perform JDBC operations
|
||||
* @param resource the resource (potentially associated with a specific encoding)
|
||||
* to load the SQL script from.
|
||||
* @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>
|
||||
*/
|
||||
public static void executeSqlScript(SimpleJdbcTemplate simpleJdbcTemplate,
|
||||
EncodedResource resource, boolean continueOnError) throws DataAccessException {
|
||||
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Executing SQL script from " + resource);
|
||||
}
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
List<String> statements = new LinkedList<String>();
|
||||
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<String> itr = statements.iterator(); itr.hasNext();) {
|
||||
String statement = itr.next();
|
||||
try {
|
||||
int rowsAffected = simpleJdbcTemplate.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;
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Done executing SQL scriptBuilder from " + resource + " in " + elapsedTime + " ms.");
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new DataAccessResourceFailureException("Failed to open SQL script from " + resource, ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
Support classes for tests based on JDBC.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2002-2006 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.jpa;
|
||||
|
||||
import org.aspectj.weaver.loadtime.ClassPreProcessorAgentAdapter;
|
||||
|
||||
import org.springframework.instrument.classloading.ResourceOverridingShadowingClassLoader;
|
||||
|
||||
/**
|
||||
* Subclass of AbstractJpaTests that activates AspectJ load-time weaving
|
||||
* and allows the ability to specify a custom location for AspectJ's
|
||||
* <code>aop.xml</code> file.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public abstract class AbstractAspectjJpaTests extends AbstractJpaTests {
|
||||
|
||||
/**
|
||||
* Default location of the <code>aop.xml</code> file in the class path:
|
||||
* "META-INF/aop.xml"
|
||||
*/
|
||||
public static final String DEFAULT_AOP_XML_LOCATION = "META-INF/aop.xml";
|
||||
|
||||
|
||||
@Override
|
||||
protected void customizeResourceOverridingShadowingClassLoader(ClassLoader shadowingClassLoader) {
|
||||
ResourceOverridingShadowingClassLoader orxl = (ResourceOverridingShadowingClassLoader) shadowingClassLoader;
|
||||
orxl.override(DEFAULT_AOP_XML_LOCATION, getActualAopXmlLocation());
|
||||
orxl.addTransformer(new ClassPreProcessorAgentAdapter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the actual location of the <code>aop.xml</code> file
|
||||
* in the class path. The default is "META-INF/aop.xml".
|
||||
* <p>Override this method to point to a specific <code>aop.xml</code>
|
||||
* file within your test suite, allowing for different config files
|
||||
* to co-exist within the same class path.
|
||||
*/
|
||||
protected String getActualAopXmlLocation() {
|
||||
return DEFAULT_AOP_XML_LOCATION;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,363 @@
|
|||
/*
|
||||
* 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.jpa;
|
||||
|
||||
import java.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
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.instrument.classloading.InstrumentationLoadTimeWeaver;
|
||||
import org.springframework.instrument.classloading.LoadTimeWeaver;
|
||||
import org.springframework.instrument.classloading.ResourceOverridingShadowingClassLoader;
|
||||
import org.springframework.instrument.classloading.ShadowingClassLoader;
|
||||
import org.springframework.orm.jpa.ExtendedEntityManagerCreator;
|
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
import org.springframework.orm.jpa.SharedEntityManagerCreator;
|
||||
import org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager;
|
||||
import org.springframework.test.annotation.AbstractAnnotationAwareTransactionalTests;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Convenient support class for JPA-related tests. Offers the same contract as
|
||||
* AbstractTransactionalDataSourceSpringContextTests and equally good performance,
|
||||
* even when performing the instrumentation required by the JPA specification.
|
||||
*
|
||||
* <p>Exposes an EntityManagerFactory and a shared EntityManager.
|
||||
* Requires an EntityManagerFactory to be injected, plus the DataSource and
|
||||
* JpaTransactionManager through the superclass.
|
||||
*
|
||||
* <p>When using Xerces, make sure a post 2.0.2 version is available on the classpath
|
||||
* to avoid a critical
|
||||
* <a href="http://nagoya.apache.org/bugzilla/show_bug.cgi?id=16014"/>bug</a>
|
||||
* that leads to StackOverflow. Maven users are likely to encounter this problem since
|
||||
* 2.0.2 is used by default.
|
||||
*
|
||||
* <p>A workaround is to explicitly specify the Xerces version inside the Maven POM:
|
||||
* <pre>
|
||||
* <dependency>
|
||||
* <groupId>xerces</groupId>
|
||||
* <artifactId>xercesImpl</artifactId>
|
||||
* <version>2.8.1</version>
|
||||
* </dependency>
|
||||
* </pre>
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public abstract class AbstractJpaTests extends AbstractAnnotationAwareTransactionalTests {
|
||||
|
||||
private static final String DEFAULT_ORM_XML_LOCATION = "META-INF/orm.xml";
|
||||
|
||||
/**
|
||||
* Map from String defining unique combination of config locations, to ApplicationContext.
|
||||
* Values are intentionally not strongly typed, to avoid potential class cast exceptions
|
||||
* through use between different class loaders.
|
||||
*/
|
||||
private static Map<String, Object> contextCache = new HashMap<String, Object>();
|
||||
|
||||
private static Map<String, ClassLoader> classLoaderCache = new HashMap<String, ClassLoader>();
|
||||
|
||||
protected EntityManagerFactory entityManagerFactory;
|
||||
|
||||
/**
|
||||
* If this instance is in a shadow loader, this variable
|
||||
* will contain the parent instance of the subclass.
|
||||
* The class will not be the same as the class of the
|
||||
* shadow instance, as it was loaded by a different class loader,
|
||||
* but it can be invoked reflectively. The shadowParent
|
||||
* and the shadow loader can communicate reflectively
|
||||
* but not through direct invocation.
|
||||
*/
|
||||
private Object shadowParent;
|
||||
|
||||
/**
|
||||
* Subclasses can use this in test cases.
|
||||
* It will participate in any current transaction.
|
||||
*/
|
||||
protected EntityManager sharedEntityManager;
|
||||
|
||||
|
||||
public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
|
||||
this.entityManagerFactory = entityManagerFactory;
|
||||
this.sharedEntityManager = SharedEntityManagerCreator.createSharedEntityManager(this.entityManagerFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an EntityManager that will always automatically enlist itself in current
|
||||
* transactions, in contrast to an EntityManager returned by
|
||||
* <code>EntityManagerFactory.createEntityManager()</code>
|
||||
* (which requires an explicit <code>joinTransaction()</code> call).
|
||||
*/
|
||||
protected EntityManager createContainerManagedEntityManager() {
|
||||
return ExtendedEntityManagerCreator.createContainerManagedEntityManager(this.entityManagerFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses should override this method if they wish to disable shadow class loading.
|
||||
* <p>The default implementation deactivates shadow class loading if Spring's
|
||||
* InstrumentationSavingAgent has been configured on VM startup.
|
||||
*/
|
||||
protected boolean shouldUseShadowLoader() {
|
||||
return !InstrumentationLoadTimeWeaver.isInstrumentationAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDirty() {
|
||||
super.setDirty();
|
||||
contextCache.remove(cacheKeys());
|
||||
classLoaderCache.remove(cacheKeys());
|
||||
|
||||
// If we are a shadow loader, we need to invoke
|
||||
// the shadow parent to set it dirty, as
|
||||
// it is the shadow parent that maintains the cache state,
|
||||
// not the child
|
||||
if (this.shadowParent != null) {
|
||||
try {
|
||||
Method m = shadowParent.getClass().getMethod("setDirty", (Class[]) null);
|
||||
m.invoke(shadowParent, (Object[]) null);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void runBare() throws Throwable {
|
||||
if (!shouldUseShadowLoader()) {
|
||||
super.runBare();
|
||||
return;
|
||||
}
|
||||
|
||||
String combinationOfContextLocationsForThisTestClass = cacheKeys();
|
||||
ClassLoader classLoaderForThisTestClass = getClass().getClassLoader();
|
||||
// save the TCCL
|
||||
ClassLoader initialClassLoader = Thread.currentThread().getContextClassLoader();
|
||||
|
||||
if (this.shadowParent != null) {
|
||||
Thread.currentThread().setContextClassLoader(classLoaderForThisTestClass);
|
||||
super.runBare();
|
||||
}
|
||||
|
||||
else {
|
||||
ShadowingClassLoader shadowingClassLoader = (ShadowingClassLoader) classLoaderCache.get(combinationOfContextLocationsForThisTestClass);
|
||||
|
||||
if (shadowingClassLoader == null) {
|
||||
shadowingClassLoader = (ShadowingClassLoader) createShadowingClassLoader(classLoaderForThisTestClass);
|
||||
classLoaderCache.put(combinationOfContextLocationsForThisTestClass, shadowingClassLoader);
|
||||
}
|
||||
try {
|
||||
Thread.currentThread().setContextClassLoader(shadowingClassLoader);
|
||||
String[] configLocations = getConfigLocations();
|
||||
|
||||
// Do not strongly type, to avoid ClassCastException.
|
||||
Object cachedContext = contextCache.get(combinationOfContextLocationsForThisTestClass);
|
||||
|
||||
if (cachedContext == null) {
|
||||
|
||||
// Create the LoadTimeWeaver.
|
||||
Class shadowingLoadTimeWeaverClass = shadowingClassLoader.loadClass(ShadowingLoadTimeWeaver.class.getName());
|
||||
Constructor constructor = shadowingLoadTimeWeaverClass.getConstructor(ClassLoader.class);
|
||||
constructor.setAccessible(true);
|
||||
Object ltw = constructor.newInstance(shadowingClassLoader);
|
||||
|
||||
// Create the BeanFactory.
|
||||
Class beanFactoryClass = shadowingClassLoader.loadClass(DefaultListableBeanFactory.class.getName());
|
||||
Object beanFactory = BeanUtils.instantiateClass(beanFactoryClass);
|
||||
|
||||
// Create the BeanDefinitionReader.
|
||||
Class beanDefinitionReaderClass = shadowingClassLoader.loadClass(XmlBeanDefinitionReader.class.getName());
|
||||
Class beanDefinitionRegistryClass = shadowingClassLoader.loadClass(BeanDefinitionRegistry.class.getName());
|
||||
Object reader = beanDefinitionReaderClass.getConstructor(beanDefinitionRegistryClass).newInstance(beanFactory);
|
||||
|
||||
// Load the bean definitions into the BeanFactory.
|
||||
Method loadBeanDefinitions = beanDefinitionReaderClass.getMethod("loadBeanDefinitions", String[].class);
|
||||
loadBeanDefinitions.invoke(reader, new Object[] {configLocations});
|
||||
|
||||
// Create LoadTimeWeaver-injecting BeanPostProcessor.
|
||||
Class loadTimeWeaverInjectingBeanPostProcessorClass = shadowingClassLoader.loadClass(LoadTimeWeaverInjectingBeanPostProcessor.class.getName());
|
||||
Class loadTimeWeaverClass = shadowingClassLoader.loadClass(LoadTimeWeaver.class.getName());
|
||||
Constructor bppConstructor = loadTimeWeaverInjectingBeanPostProcessorClass.getConstructor(loadTimeWeaverClass);
|
||||
bppConstructor.setAccessible(true);
|
||||
Object beanPostProcessor = bppConstructor.newInstance(ltw);
|
||||
|
||||
// Add LoadTimeWeaver-injecting BeanPostProcessor.
|
||||
Class beanPostProcessorClass = shadowingClassLoader.loadClass(BeanPostProcessor.class.getName());
|
||||
Method addBeanPostProcessor = beanFactoryClass.getMethod("addBeanPostProcessor", beanPostProcessorClass);
|
||||
addBeanPostProcessor.invoke(beanFactory, beanPostProcessor);
|
||||
|
||||
// Create the GenericApplicationContext.
|
||||
Class genericApplicationContextClass = shadowingClassLoader.loadClass(GenericApplicationContext.class.getName());
|
||||
Class defaultListableBeanFactoryClass = shadowingClassLoader.loadClass(DefaultListableBeanFactory.class.getName());
|
||||
cachedContext = genericApplicationContextClass.getConstructor(defaultListableBeanFactoryClass).newInstance(beanFactory);
|
||||
|
||||
// Invoke the context's "refresh" method.
|
||||
genericApplicationContextClass.getMethod("refresh").invoke(cachedContext);
|
||||
|
||||
// Store the context reference in the cache.
|
||||
contextCache.put(combinationOfContextLocationsForThisTestClass, cachedContext);
|
||||
}
|
||||
// create the shadowed test
|
||||
Class shadowedTestClass = shadowingClassLoader.loadClass(getClass().getName());
|
||||
|
||||
// So long as JUnit is excluded from shadowing we
|
||||
// can minimize reflective invocation here
|
||||
TestCase shadowedTestCase = (TestCase) BeanUtils.instantiateClass(shadowedTestClass);
|
||||
|
||||
/* shadowParent = this */
|
||||
Class thisShadowedClass = shadowingClassLoader.loadClass(AbstractJpaTests.class.getName());
|
||||
Field shadowed = thisShadowedClass.getDeclaredField("shadowParent");
|
||||
shadowed.setAccessible(true);
|
||||
shadowed.set(shadowedTestCase, this);
|
||||
|
||||
/* AbstractSpringContextTests.addContext(Object, ApplicationContext) */
|
||||
Class applicationContextClass = shadowingClassLoader.loadClass(ConfigurableApplicationContext.class.getName());
|
||||
Method addContextMethod = shadowedTestClass.getMethod("addContext", Object.class, applicationContextClass);
|
||||
addContextMethod.invoke(shadowedTestCase, configLocations, cachedContext);
|
||||
|
||||
// Invoke tests on shadowed test case
|
||||
shadowedTestCase.setName(getName());
|
||||
shadowedTestCase.runBare();
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
// Unwrap this for better exception reporting
|
||||
// when running tests
|
||||
throw ex.getTargetException();
|
||||
}
|
||||
finally {
|
||||
Thread.currentThread().setContextClassLoader(initialClassLoader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected String cacheKeys() {
|
||||
return StringUtils.arrayToCommaDelimitedString(getConfigLocations());
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: This method must <b>not</b> have a return type of ShadowingClassLoader as that would cause that
|
||||
* class to be loaded eagerly when this test case loads, creating verify errors at runtime.
|
||||
*/
|
||||
protected ClassLoader createShadowingClassLoader(ClassLoader classLoader) {
|
||||
OrmXmlOverridingShadowingClassLoader orxl = new OrmXmlOverridingShadowingClassLoader(classLoader,
|
||||
getActualOrmXmlLocation());
|
||||
customizeResourceOverridingShadowingClassLoader(orxl);
|
||||
return orxl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize the shadowing class loader.
|
||||
* @param shadowingClassLoader this parameter is actually of type
|
||||
* ResourceOverridingShadowingClassLoader, and can safely to be cast to
|
||||
* that type. However, the signature must not be of that type as that
|
||||
* would cause the present class loader to load that type.
|
||||
*/
|
||||
protected void customizeResourceOverridingShadowingClassLoader(ClassLoader shadowingClassLoader) {
|
||||
// empty
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can override this to return the real location path for
|
||||
* orm.xml or null if they do not wish to find any orm.xml
|
||||
* @return orm.xml path or null to hide any such file
|
||||
*/
|
||||
protected String getActualOrmXmlLocation() {
|
||||
return DEFAULT_ORM_XML_LOCATION;
|
||||
}
|
||||
|
||||
|
||||
private static class LoadTimeWeaverInjectingBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {
|
||||
|
||||
private final LoadTimeWeaver ltw;
|
||||
|
||||
public LoadTimeWeaverInjectingBeanPostProcessor(LoadTimeWeaver ltw) {
|
||||
this.ltw = ltw;
|
||||
}
|
||||
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof LocalContainerEntityManagerFactoryBean) {
|
||||
((LocalContainerEntityManagerFactoryBean) bean).setLoadTimeWeaver(this.ltw);
|
||||
}
|
||||
if (bean instanceof DefaultPersistenceUnitManager) {
|
||||
((DefaultPersistenceUnitManager) bean).setLoadTimeWeaver(this.ltw);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class ShadowingLoadTimeWeaver implements LoadTimeWeaver {
|
||||
|
||||
private final ClassLoader shadowingClassLoader;
|
||||
|
||||
public ShadowingLoadTimeWeaver(ClassLoader shadowingClassLoader) {
|
||||
this.shadowingClassLoader = shadowingClassLoader;
|
||||
}
|
||||
|
||||
public void addTransformer(ClassFileTransformer transformer) {
|
||||
try {
|
||||
Method addClassFileTransformer =
|
||||
this.shadowingClassLoader.getClass().getMethod("addTransformer", ClassFileTransformer.class);
|
||||
addClassFileTransformer.setAccessible(true);
|
||||
addClassFileTransformer.invoke(this.shadowingClassLoader, transformer);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public ClassLoader getInstrumentableClassLoader() {
|
||||
return this.shadowingClassLoader;
|
||||
}
|
||||
|
||||
public ClassLoader getThrowawayClassLoader() {
|
||||
// Be sure to copy the same resource overrides and same class file transformers:
|
||||
// We want the throwaway class loader to behave like the instrumentable class loader.
|
||||
ResourceOverridingShadowingClassLoader roscl =
|
||||
new ResourceOverridingShadowingClassLoader(getClass().getClassLoader());
|
||||
if (this.shadowingClassLoader instanceof ShadowingClassLoader) {
|
||||
roscl.copyTransformers((ShadowingClassLoader) this.shadowingClassLoader);
|
||||
}
|
||||
if (this.shadowingClassLoader instanceof ResourceOverridingShadowingClassLoader) {
|
||||
roscl.copyOverrides((ResourceOverridingShadowingClassLoader) this.shadowingClassLoader);
|
||||
}
|
||||
return roscl;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue