Initial import of web module

This commit is contained in:
Arjen Poutsma 2008-10-24 09:42:55 +00:00
parent d1061e7e9f
commit 04eaa47ec1
139 changed files with 17188 additions and 0 deletions

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="org.springframework.orm">
<property file="${basedir}/../build.properties"/>
<import file="${basedir}/../build-spring-framework/package-bundle.xml"/>
<import file="${basedir}/../spring-build/standard/default.xml"/>
</project>

View File

@ -0,0 +1,47 @@
<?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>
<!-- compile dependencies -->
<dependency org="org.apache.commons" name="com.springsource.org.apache.commons.logging" rev="1.1.1" conf="compile->runtime" />
<dependency org="org.springframework" name="org.springframework.core" rev="latest.integration" conf="compile->compile" />
<dependency org="org.springframework" name="org.springframework.beans" rev="latest.integration" conf="compile->compile" />
<dependency org="org.springframework" name="org.springframework.context" rev="latest.integration" conf="optional->compile" />
<!--
<dependency org="org.springframework" name="org.springframework.aop" rev="latest.integration" conf="compile->compile" />
<dependency org="org.springframework" name="org.springframework.transaction" rev="latest.integration" conf="compile->compile" />
<dependency org="org.springframework" name="org.springframework.jdbc" rev="latest.integration" conf="compile->compile" />
-->
<!-- optional dependencies -->
<dependency org="javax.el" name="com.springsource.javax.el" rev="2.1.0" conf="optional->compile" />
<dependency org="javax.servlet" name="com.springsource.javax.servlet" rev="2.5.0" conf="optional->compile" />
<dependency org="javax.servlet" name="com.springsource.javax.servlet.jsp" rev="2.1.0" conf="optional->compile" />
<dependency org="org.apache.myfaces" name="com.springsource.org.apache.myfaces.javax.faces" rev="1.2.2" conf="optional->compile" />
<dependency org="org.apache.taglibs" name="com.springsource.org.apache.taglibs.standard" rev="1.1.2" conf="optional->compile" />
<dependency org="org.apache.log4j" name="com.springsource.org.apache.log4j" rev="1.2.15" conf="optional->runtime" />
<dependency org="javax.xml.rpc" name="com.springsource.javax.xml.rpc" rev="1.1.0" conf="optional->runtime" />
<dependency org="org.apache.axis" name="com.springsource.org.apache.axis" rev="1.4.0" conf="optional->runtime" />
<dependency org="com.caucho" name="com.springsource.com.caucho" rev="3.1.5" conf="optional->runtime" />
<!-- test dependencies -->
<dependency org="org.junit" name="com.springsource.org.junit" rev="4.4.0" conf="test->runtime" />
</dependencies>
</ivy-module>

View File

@ -0,0 +1,59 @@
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.core</artifactId>
<packaging>jar</packaging>
<name>Spring Core Abstractions and Utilities</name>
<version>3.0.0.M1</version>
<repositories>
<repository>
<id>com.springsource.repository.bundles.external</id>
<name>SpringSource Enterprise Bundle Repository - External Bundle Releases</name>
<url>http://repository.springsource.com/maven/bundles/external</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>com.springsource.org.apache.commons.logging</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.log4j</groupId>
<artifactId>com.springsource.org.apache.log4j</artifactId>
<version>1.2.15</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>com.springsource.org.apache.commons.collections</artifactId>
<version>3.2.0</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>com.springsource.org.aspectj.weaver</artifactId>
<version>1.6.2.RELEASE</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.objectweb.asm</groupId>
<artifactId>com.springsource.org.objectweb.asm.commons</artifactId>
<version>2.2.3</version>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,187 @@
/*
* 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.remoting.caucho;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.UndeclaredThrowableException;
import java.net.ConnectException;
import java.net.MalformedURLException;
import com.caucho.burlap.client.BurlapProxyFactory;
import com.caucho.burlap.client.BurlapRuntimeException;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.remoting.RemoteAccessException;
import org.springframework.remoting.RemoteConnectFailureException;
import org.springframework.remoting.RemoteLookupFailureException;
import org.springframework.remoting.RemoteProxyFailureException;
import org.springframework.remoting.support.UrlBasedRemoteAccessor;
import org.springframework.util.Assert;
/**
* {@link org.aopalliance.intercept.MethodInterceptor} for accessing a Burlap service.
* Supports authentication via username and password.
* The service URL must be an HTTP URL exposing a Burlap service.
*
* <p>Burlap is a slim, XML-based RPC protocol.
* For information on Burlap, see the
* <a href="http://www.caucho.com/burlap">Burlap website</a>
*
* <p>Note: There is no requirement for services accessed with this proxy factory
* to have been exported using Spring's {@link BurlapServiceExporter}, as there is
* no special handling involved. As a consequence, you can also access services that
* have been exported using Caucho's {@link com.caucho.burlap.server.BurlapServlet}.
*
* @author Juergen Hoeller
* @since 29.09.2003
* @see #setServiceInterface
* @see #setServiceUrl
* @see #setUsername
* @see #setPassword
* @see BurlapServiceExporter
* @see BurlapProxyFactoryBean
* @see com.caucho.burlap.client.BurlapProxyFactory
* @see com.caucho.burlap.server.BurlapServlet
*/
public class BurlapClientInterceptor extends UrlBasedRemoteAccessor implements MethodInterceptor {
private BurlapProxyFactory proxyFactory = new BurlapProxyFactory();
private Object burlapProxy;
/**
* Set the BurlapProxyFactory instance to use.
* If not specified, a default BurlapProxyFactory will be created.
* <p>Allows to use an externally configured factory instance,
* in particular a custom BurlapProxyFactory subclass.
*/
public void setProxyFactory(BurlapProxyFactory proxyFactory) {
this.proxyFactory = (proxyFactory != null ? proxyFactory : new BurlapProxyFactory());
}
/**
* Set the username that this factory should use to access the remote service.
* Default is none.
* <p>The username will be sent by Burlap via HTTP Basic Authentication.
* @see com.caucho.burlap.client.BurlapProxyFactory#setUser
*/
public void setUsername(String username) {
this.proxyFactory.setUser(username);
}
/**
* Set the password that this factory should use to access the remote service.
* Default is none.
* <p>The password will be sent by Burlap via HTTP Basic Authentication.
* @see com.caucho.burlap.client.BurlapProxyFactory#setPassword
*/
public void setPassword(String password) {
this.proxyFactory.setPassword(password);
}
/**
* Set whether overloaded methods should be enabled for remote invocations.
* Default is "false".
* @see com.caucho.burlap.client.BurlapProxyFactory#setOverloadEnabled
*/
public void setOverloadEnabled(boolean overloadEnabled) {
this.proxyFactory.setOverloadEnabled(overloadEnabled);
}
public void afterPropertiesSet() {
super.afterPropertiesSet();
prepare();
}
/**
* Initialize the Burlap proxy for this interceptor.
* @throws RemoteLookupFailureException if the service URL is invalid
*/
public void prepare() throws RemoteLookupFailureException {
try {
this.burlapProxy = createBurlapProxy(this.proxyFactory);
}
catch (MalformedURLException ex) {
throw new RemoteLookupFailureException("Service URL [" + getServiceUrl() + "] is invalid", ex);
}
}
/**
* Create the Burlap proxy that is wrapped by this interceptor.
* @param proxyFactory the proxy factory to use
* @return the Burlap proxy
* @throws MalformedURLException if thrown by the proxy factory
* @see com.caucho.burlap.client.BurlapProxyFactory#create
*/
protected Object createBurlapProxy(BurlapProxyFactory proxyFactory) throws MalformedURLException {
Assert.notNull(getServiceInterface(), "Property 'serviceInterface' is required");
return proxyFactory.create(getServiceInterface(), getServiceUrl());
}
public Object invoke(MethodInvocation invocation) throws Throwable {
if (this.burlapProxy == null) {
throw new IllegalStateException("BurlapClientInterceptor is not properly initialized - " +
"invoke 'prepare' before attempting any operations");
}
ClassLoader originalClassLoader = overrideThreadContextClassLoader();
try {
return invocation.getMethod().invoke(this.burlapProxy, invocation.getArguments());
}
catch (InvocationTargetException ex) {
if (ex.getTargetException() instanceof BurlapRuntimeException) {
BurlapRuntimeException bre = (BurlapRuntimeException) ex.getTargetException();
Throwable rootCause = (bre.getRootCause() != null ? bre.getRootCause() : bre);
throw convertBurlapAccessException(rootCause);
}
else if (ex.getTargetException() instanceof UndeclaredThrowableException) {
UndeclaredThrowableException utex = (UndeclaredThrowableException) ex.getTargetException();
throw convertBurlapAccessException(utex.getUndeclaredThrowable());
}
throw ex.getTargetException();
}
catch (Throwable ex) {
throw new RemoteProxyFailureException(
"Failed to invoke Burlap proxy for remote service [" + getServiceUrl() + "]", ex);
}
finally {
resetThreadContextClassLoader(originalClassLoader);
}
}
/**
* Convert the given Burlap access exception to an appropriate
* Spring RemoteAccessException.
* @param ex the exception to convert
* @return the RemoteAccessException to throw
*/
protected RemoteAccessException convertBurlapAccessException(Throwable ex) {
if (ex instanceof ConnectException) {
return new RemoteConnectFailureException(
"Cannot connect to Burlap remote service at [" + getServiceUrl() + "]", ex);
}
else {
return new RemoteAccessException(
"Cannot access Burlap remote service at [" + getServiceUrl() + "]", ex);
}
}
}

View File

@ -0,0 +1,110 @@
/*
* 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.remoting.caucho;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import com.caucho.burlap.io.BurlapInput;
import com.caucho.burlap.io.BurlapOutput;
import com.caucho.burlap.server.BurlapSkeleton;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.remoting.support.RemoteExporter;
import org.springframework.util.Assert;
/**
* General stream-based protocol exporter for a Burlap endpoint.
*
* <p>Burlap is a slim, XML-based RPC protocol.
* For information on Burlap, see the
* <a href="http://www.caucho.com/burlap">Burlap website</a>.
*
* <p>This exporter will work with both Burlap 2.x and 3.x (respectively
* Resin 2.x and 3.x), autodetecting the corresponding skeleton class.
*
* @author Juergen Hoeller
* @since 2.5.1
* @see #invoke(java.io.InputStream, java.io.OutputStream)
* @see BurlapServiceExporter
* @see SimpleBurlapServiceExporter
*/
public class BurlapExporter extends RemoteExporter implements InitializingBean {
private BurlapSkeleton skeleton;
public void afterPropertiesSet() {
prepare();
}
/**
* Initialize this service exporter.
*/
public void prepare() {
try {
try {
// Try Burlap 3.x (with service interface argument).
Constructor ctor = BurlapSkeleton.class.getConstructor(new Class[] {Object.class, Class.class});
checkService();
checkServiceInterface();
this.skeleton = (BurlapSkeleton)
ctor.newInstance(new Object[] {getProxyForService(), getServiceInterface()});
}
catch (NoSuchMethodException ex) {
// Fall back to Burlap 2.x (without service interface argument).
Constructor ctor = BurlapSkeleton.class.getConstructor(new Class[] {Object.class});
this.skeleton = (BurlapSkeleton) ctor.newInstance(new Object[] {getProxyForService()});
}
}
catch (Exception ex) {
throw new BeanInitializationException("Burlap skeleton initialization failed", ex);
}
}
/**
* Perform an invocation on the exported object.
* @param inputStream the request stream
* @param outputStream the response stream
* @throws Throwable if invocation failed
*/
public void invoke(InputStream inputStream, OutputStream outputStream) throws Throwable {
Assert.notNull(this.skeleton, "Burlap exporter has not been initialized");
ClassLoader originalClassLoader = overrideThreadContextClassLoader();
try {
this.skeleton.invoke(new BurlapInput(inputStream), new BurlapOutput(outputStream));
}
finally {
try {
inputStream.close();
}
catch (IOException ex) {
}
try {
outputStream.close();
}
catch (IOException ex) {
}
resetThreadContextClassLoader(originalClassLoader);
}
}
}

View File

@ -0,0 +1,66 @@
/*
* 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.remoting.caucho;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.FactoryBean;
/**
* FactoryBean for Burlap proxies. Exposes the proxied service for
* use as a bean reference, using the specified service interface.
*
* <p>Burlap is a slim, XML-based RPC protocol.
* For information on Burlap, see the
* <a href="http://www.caucho.com/burlap">Burlap website</a>
*
* <p>The service URL must be an HTTP URL exposing a Burlap service.
* For details, see the {@link BurlapClientInterceptor} javadoc.
*
* @author Juergen Hoeller
* @since 13.05.2003
* @see #setServiceInterface
* @see #setServiceUrl
* @see BurlapClientInterceptor
* @see BurlapServiceExporter
* @see org.springframework.remoting.caucho.HessianProxyFactoryBean
* @see org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean
* @see org.springframework.remoting.rmi.RmiProxyFactoryBean
*/
public class BurlapProxyFactoryBean extends BurlapClientInterceptor implements FactoryBean {
private Object serviceProxy;
public void afterPropertiesSet() {
super.afterPropertiesSet();
this.serviceProxy = new ProxyFactory(getServiceInterface(), this).getProxy(getBeanClassLoader());
}
public Object getObject() {
return this.serviceProxy;
}
public Class getObjectType() {
return getServiceInterface();
}
public boolean isSingleton() {
return true;
}
}

View File

@ -0,0 +1,75 @@
/*
* 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.remoting.caucho;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.util.NestedServletException;
/**
* Servlet-API-based HTTP request handler that exports the specified service bean
* as Burlap service endpoint, accessible via a Burlap proxy.
*
* <p><b>Note:</b> Spring also provides an alternative version of this exporter,
* for Sun's JRE 1.6 HTTP server: {@link SimpleBurlapServiceExporter}.
*
* <p>Burlap is a slim, XML-based RPC protocol.
* For information on Burlap, see the
* <a href="http://www.caucho.com/burlap">Burlap website</a>.
*
* <p>This exporter will work with both Burlap 2.x and 3.x (respectively
* Resin 2.x and 3.x), autodetecting the corresponding skeleton class.
*
* <p>Note: Burlap services exported with this class can be accessed by
* any Burlap client, as there isn't any special handling involved.
*
* @author Juergen Hoeller
* @since 13.05.2003
* @see BurlapClientInterceptor
* @see BurlapProxyFactoryBean
* @see org.springframework.remoting.caucho.HessianServiceExporter
* @see org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter
* @see org.springframework.remoting.rmi.RmiServiceExporter
*/
public class BurlapServiceExporter extends BurlapExporter implements HttpRequestHandler {
/**
* Processes the incoming Burlap request and creates a Burlap response.
*/
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if (!"POST".equals(request.getMethod())) {
throw new HttpRequestMethodNotSupportedException(request.getMethod(),
new String[] {"POST"}, "BurlapServiceExporter only supports POST requests");
}
try {
invoke(request.getInputStream(), response.getOutputStream());
}
catch (Throwable ex) {
throw new NestedServletException("Burlap skeleton invocation failed", ex);
}
}
}

View File

@ -0,0 +1,88 @@
/*
* 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.remoting.caucho;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;
import com.caucho.hessian.io.SerializerFactory;
import com.caucho.hessian.server.HessianSkeleton;
import org.springframework.util.ClassUtils;
/**
* Concrete HessianSkeletonInvoker for the Hessian 1 protocol
* (version 3.0.19 or lower).
*
* @author Juergen Hoeller
* @since 2.0
*/
class Hessian1SkeletonInvoker extends HessianSkeletonInvoker {
private static final Method invokeMethod;
private static final boolean applySerializerFactoryToOutput;
static {
invokeMethod = ClassUtils.getMethodIfAvailable(
HessianSkeleton.class, "invoke", new Class[] {HessianInput.class, HessianOutput.class});
applySerializerFactoryToOutput =
ClassUtils.hasMethod(HessianOutput.class, "setSerializerFactory", new Class[] {SerializerFactory.class});
}
public Hessian1SkeletonInvoker(HessianSkeleton skeleton, SerializerFactory serializerFactory) {
super(skeleton, serializerFactory);
if (invokeMethod == null) {
throw new IllegalStateException("Hessian 1 (version 3.0.19-) not present");
}
}
public void invoke(InputStream inputStream, OutputStream outputStream) throws Throwable {
HessianInput in = new HessianInput(inputStream);
HessianOutput out = new HessianOutput(outputStream);
if (this.serializerFactory != null) {
in.setSerializerFactory(this.serializerFactory);
if (applySerializerFactoryToOutput) {
out.setSerializerFactory(this.serializerFactory);
}
}
try {
invokeMethod.invoke(this.skeleton, new Object[] {in, out});
}
finally {
try {
in.close();
inputStream.close();
}
catch (IOException ex) {
}
try {
out.close();
outputStream.close();
}
catch (IOException ex) {
}
}
}
}

View File

@ -0,0 +1,123 @@
/*
* 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.remoting.caucho;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import com.caucho.hessian.io.AbstractHessianOutput;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.caucho.hessian.io.HessianDebugInputStream;
import com.caucho.hessian.io.HessianDebugOutputStream;
import com.caucho.hessian.io.HessianOutput;
import com.caucho.hessian.io.SerializerFactory;
import com.caucho.hessian.server.HessianSkeleton;
import org.apache.commons.logging.Log;
import org.springframework.util.ClassUtils;
import org.springframework.util.CommonsLogWriter;
/**
* Concrete HessianSkeletonInvoker for the Hessian 2 protocol
* (version 3.0.20 or higher).
*
* @author Juergen Hoeller
* @author Andy Piper
* @since 2.0
*/
class Hessian2SkeletonInvoker extends HessianSkeletonInvoker {
private static final boolean debugOutputStreamAvailable = ClassUtils.isPresent(
"com.caucho.hessian.io.HessianDebugOutputStream", Hessian2SkeletonInvoker.class.getClassLoader());
private final Log debugLogger;
public Hessian2SkeletonInvoker(HessianSkeleton skeleton, SerializerFactory serializerFactory, Log debugLog) {
super(skeleton, serializerFactory);
this.debugLogger = debugLog;
}
public void invoke(final InputStream inputStream, final OutputStream outputStream) throws Throwable {
InputStream isToUse = inputStream;
OutputStream osToUse = outputStream;
if (this.debugLogger != null && this.debugLogger.isDebugEnabled()) {
PrintWriter debugWriter = new PrintWriter(new CommonsLogWriter(this.debugLogger));
isToUse = new HessianDebugInputStream(inputStream, debugWriter);
if (debugOutputStreamAvailable) {
osToUse = DebugStreamFactory.createDebugOutputStream(outputStream, debugWriter);
}
}
Hessian2Input in = new Hessian2Input(isToUse);
if (this.serializerFactory != null) {
in.setSerializerFactory(this.serializerFactory);
}
int code = in.read();
if (code != 'c') {
throw new IOException("expected 'c' in hessian input at " + code);
}
AbstractHessianOutput out = null;
int major = in.read();
int minor = in.read();
if (major >= 2) {
out = new Hessian2Output(osToUse);
}
else {
out = new HessianOutput(osToUse);
}
if (this.serializerFactory != null) {
out.setSerializerFactory(this.serializerFactory);
}
try {
this.skeleton.invoke(in, out);
}
finally {
try {
in.close();
isToUse.close();
}
catch (IOException ex) {
}
try {
out.close();
osToUse.close();
}
catch (IOException ex) {
}
}
}
/**
* Inner class to avoid hard dependency on Hessian 3.1.3's HessianDebugOutputStream.
*/
private static class DebugStreamFactory {
public static OutputStream createDebugOutputStream(OutputStream os, PrintWriter debug) {
return new HessianDebugOutputStream(os, debug);
}
}
}

View File

@ -0,0 +1,259 @@
/*
* 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.remoting.caucho;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.UndeclaredThrowableException;
import java.net.ConnectException;
import java.net.MalformedURLException;
import com.caucho.hessian.client.HessianProxyFactory;
import com.caucho.hessian.client.HessianRuntimeException;
import com.caucho.hessian.io.SerializerFactory;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.remoting.RemoteAccessException;
import org.springframework.remoting.RemoteConnectFailureException;
import org.springframework.remoting.RemoteLookupFailureException;
import org.springframework.remoting.RemoteProxyFailureException;
import org.springframework.remoting.support.UrlBasedRemoteAccessor;
import org.springframework.util.Assert;
/**
* {@link org.aopalliance.intercept.MethodInterceptor} for accessing a Hessian service.
* Supports authentication via username and password.
* The service URL must be an HTTP URL exposing a Hessian service.
*
* <p>Hessian is a slim, binary RPC protocol.
* For information on Hessian, see the
* <a href="http://www.caucho.com/hessian">Hessian website</a>
*
* <p>Note: There is no requirement for services accessed with this proxy factory
* to have been exported using Spring's {@link HessianServiceExporter}, as there is
* no special handling involved. As a consequence, you can also access services that
* have been exported using Caucho's {@link com.caucho.hessian.server.HessianServlet}.
*
* @author Juergen Hoeller
* @since 29.09.2003
* @see #setServiceInterface
* @see #setServiceUrl
* @see #setUsername
* @see #setPassword
* @see HessianServiceExporter
* @see HessianProxyFactoryBean
* @see com.caucho.hessian.client.HessianProxyFactory
* @see com.caucho.hessian.server.HessianServlet
*/
public class HessianClientInterceptor extends UrlBasedRemoteAccessor implements MethodInterceptor {
private HessianProxyFactory proxyFactory = new HessianProxyFactory();
private Object hessianProxy;
/**
* Set the HessianProxyFactory instance to use.
* If not specified, a default HessianProxyFactory will be created.
* <p>Allows to use an externally configured factory instance,
* in particular a custom HessianProxyFactory subclass.
*/
public void setProxyFactory(HessianProxyFactory proxyFactory) {
this.proxyFactory = (proxyFactory != null ? proxyFactory : new HessianProxyFactory());
}
/**
* Specify the Hessian SerializerFactory to use.
* <p>This will typically be passed in as an inner bean definition
* of type <code>com.caucho.hessian.io.SerializerFactory</code>,
* with custom bean property values applied.
*/
public void setSerializerFactory(SerializerFactory serializerFactory) {
this.proxyFactory.setSerializerFactory(serializerFactory);
}
/**
* Set whether to send the Java collection type for each serialized
* collection. Default is "true".
*/
public void setSendCollectionType(boolean sendCollectionType) {
this.proxyFactory.getSerializerFactory().setSendCollectionType(sendCollectionType);
}
/**
* Set whether overloaded methods should be enabled for remote invocations.
* Default is "false".
* @see com.caucho.hessian.client.HessianProxyFactory#setOverloadEnabled
*/
public void setOverloadEnabled(boolean overloadEnabled) {
this.proxyFactory.setOverloadEnabled(overloadEnabled);
}
/**
* Set the username that this factory should use to access the remote service.
* Default is none.
* <p>The username will be sent by Hessian via HTTP Basic Authentication.
* @see com.caucho.hessian.client.HessianProxyFactory#setUser
*/
public void setUsername(String username) {
this.proxyFactory.setUser(username);
}
/**
* Set the password that this factory should use to access the remote service.
* Default is none.
* <p>The password will be sent by Hessian via HTTP Basic Authentication.
* @see com.caucho.hessian.client.HessianProxyFactory#setPassword
*/
public void setPassword(String password) {
this.proxyFactory.setPassword(password);
}
/**
* Set whether Hessian's debug mode should be enabled.
* Default is "false".
* @see com.caucho.hessian.client.HessianProxyFactory#setDebug
*/
public void setDebug(boolean debug) {
this.proxyFactory.setDebug(debug);
}
/**
* Set whether to use a chunked post for sending a Hessian request.
* @see com.caucho.hessian.client.HessianProxyFactory#setChunkedPost
*/
public void setChunkedPost(boolean chunkedPost) {
this.proxyFactory.setChunkedPost(chunkedPost);
}
/**
* Set the timeout to use when waiting for a reply from the Hessian service.
* @see com.caucho.hessian.client.HessianProxyFactory#setReadTimeout
*/
public void setReadTimeout(long timeout) {
this.proxyFactory.setReadTimeout(timeout);
}
/**
* Set whether version 2 of the Hessian protocol should be used for
* parsing requests and replies. Default is "false".
* @see com.caucho.hessian.client.HessianProxyFactory#setHessian2Request
*/
public void setHessian2(boolean hessian2) {
this.proxyFactory.setHessian2Request(hessian2);
this.proxyFactory.setHessian2Reply(hessian2);
}
/**
* Set whether version 2 of the Hessian protocol should be used for
* parsing requests. Default is "false".
* @see com.caucho.hessian.client.HessianProxyFactory#setHessian2Request
*/
public void setHessian2Request(boolean hessian2) {
this.proxyFactory.setHessian2Request(hessian2);
}
/**
* Set whether version 2 of the Hessian protocol should be used for
* parsing replies. Default is "false".
* @see com.caucho.hessian.client.HessianProxyFactory#setHessian2Reply
*/
public void setHessian2Reply(boolean hessian2) {
this.proxyFactory.setHessian2Reply(hessian2);
}
public void afterPropertiesSet() {
super.afterPropertiesSet();
prepare();
}
/**
* Initialize the Hessian proxy for this interceptor.
* @throws RemoteLookupFailureException if the service URL is invalid
*/
public void prepare() throws RemoteLookupFailureException {
try {
this.hessianProxy = createHessianProxy(this.proxyFactory);
}
catch (MalformedURLException ex) {
throw new RemoteLookupFailureException("Service URL [" + getServiceUrl() + "] is invalid", ex);
}
}
/**
* Create the Hessian proxy that is wrapped by this interceptor.
* @param proxyFactory the proxy factory to use
* @return the Hessian proxy
* @throws MalformedURLException if thrown by the proxy factory
* @see com.caucho.hessian.client.HessianProxyFactory#create
*/
protected Object createHessianProxy(HessianProxyFactory proxyFactory) throws MalformedURLException {
Assert.notNull(getServiceInterface(), "'serviceInterface' is required");
return proxyFactory.create(getServiceInterface(), getServiceUrl());
}
public Object invoke(MethodInvocation invocation) throws Throwable {
if (this.hessianProxy == null) {
throw new IllegalStateException("HessianClientInterceptor is not properly initialized - " +
"invoke 'prepare' before attempting any operations");
}
ClassLoader originalClassLoader = overrideThreadContextClassLoader();
try {
return invocation.getMethod().invoke(this.hessianProxy, invocation.getArguments());
}
catch (InvocationTargetException ex) {
if (ex.getTargetException() instanceof HessianRuntimeException) {
HessianRuntimeException hre = (HessianRuntimeException) ex.getTargetException();
Throwable rootCause = (hre.getRootCause() != null ? hre.getRootCause() : hre);
throw convertHessianAccessException(rootCause);
}
else if (ex.getTargetException() instanceof UndeclaredThrowableException) {
UndeclaredThrowableException utex = (UndeclaredThrowableException) ex.getTargetException();
throw convertHessianAccessException(utex.getUndeclaredThrowable());
}
throw ex.getTargetException();
}
catch (Throwable ex) {
throw new RemoteProxyFailureException(
"Failed to invoke Hessian proxy for remote service [" + getServiceUrl() + "]", ex);
}
finally {
resetThreadContextClassLoader(originalClassLoader);
}
}
/**
* Convert the given Hessian access exception to an appropriate
* Spring RemoteAccessException.
* @param ex the exception to convert
* @return the RemoteAccessException to throw
*/
protected RemoteAccessException convertHessianAccessException(Throwable ex) {
if (ex instanceof ConnectException) {
return new RemoteConnectFailureException(
"Cannot connect to Hessian remote service at [" + getServiceUrl() + "]", ex);
}
else {
return new RemoteAccessException(
"Cannot access Hessian remote service at [" + getServiceUrl() + "]", ex);
}
}
}

View File

@ -0,0 +1,149 @@
/*
* 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.remoting.caucho;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import com.caucho.hessian.io.SerializerFactory;
import com.caucho.hessian.server.HessianSkeleton;
import org.apache.commons.logging.Log;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.remoting.support.RemoteExporter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* General stream-based protocol exporter for a Hessian endpoint.
*
* <p>Hessian is a slim, binary RPC protocol.
* For information on Hessian, see the
* <a href="http://www.caucho.com/hessian">Hessian website</a>.
*
* <p>This exporter will work with both Hessian 2.x and 3.x (respectively
* Resin 2.x and 3.x), autodetecting the corresponding skeleton class.
* As of Spring 2.0, it is also compatible with the new Hessian 2 protocol
* (a.k.a. Hessian 3.0.20+), while remaining compatible with older versions.
*
* @author Juergen Hoeller
* @since 2.5.1
* @see #invoke(java.io.InputStream, java.io.OutputStream)
* @see HessianServiceExporter
* @see SimpleHessianServiceExporter
*/
public class HessianExporter extends RemoteExporter implements InitializingBean {
private static final boolean hessian2Available =
ClassUtils.isPresent("com.caucho.hessian.io.Hessian2Input", HessianServiceExporter.class.getClassLoader());
private SerializerFactory serializerFactory = new SerializerFactory();
private Log debugLogger;
private HessianSkeletonInvoker skeletonInvoker;
/**
* Specify the Hessian SerializerFactory to use.
* <p>This will typically be passed in as an inner bean definition
* of type <code>com.caucho.hessian.io.SerializerFactory</code>,
* with custom bean property values applied.
*/
public void setSerializerFactory(SerializerFactory serializerFactory) {
this.serializerFactory = (serializerFactory != null ? serializerFactory : new SerializerFactory());
}
/**
* Set whether to send the Java collection type for each serialized
* collection. Default is "true".
*/
public void setSendCollectionType(boolean sendCollectionType) {
this.serializerFactory.setSendCollectionType(sendCollectionType);
}
/**
* Set whether Hessian's debug mode should be enabled, logging to
* this exporter's Commons Logging log. Default is "false".
* @see com.caucho.hessian.client.HessianProxyFactory#setDebug
*/
public void setDebug(boolean debug) {
this.debugLogger = (debug ? logger : null);
}
public void afterPropertiesSet() {
prepare();
}
/**
* Initialize this exporter.
*/
public void prepare() {
HessianSkeleton skeleton = null;
try {
try {
// Try Hessian 3.x (with service interface argument).
Constructor ctor = HessianSkeleton.class.getConstructor(new Class[] {Object.class, Class.class});
checkService();
checkServiceInterface();
skeleton = (HessianSkeleton)
ctor.newInstance(new Object[] {getProxyForService(), getServiceInterface()});
}
catch (NoSuchMethodException ex) {
// Fall back to Hessian 2.x (without service interface argument).
Constructor ctor = HessianSkeleton.class.getConstructor(new Class[] {Object.class});
skeleton = (HessianSkeleton) ctor.newInstance(new Object[] {getProxyForService()});
}
}
catch (Throwable ex) {
throw new BeanInitializationException("Hessian skeleton initialization failed", ex);
}
if (hessian2Available) {
// Hessian 2 (version 3.0.20+).
this.skeletonInvoker = new Hessian2SkeletonInvoker(skeleton, this.serializerFactory, this.debugLogger);
}
else {
// Hessian 1 (version 3.0.19-).
this.skeletonInvoker = new Hessian1SkeletonInvoker(skeleton, this.serializerFactory);
}
}
/**
* Perform an invocation on the exported object.
* @param inputStream the request stream
* @param outputStream the response stream
* @throws Throwable if invocation failed
*/
public void invoke(InputStream inputStream, OutputStream outputStream) throws Throwable {
Assert.notNull(this.skeletonInvoker, "Hessian exporter has not been initialized");
ClassLoader originalClassLoader = overrideThreadContextClassLoader();
try {
this.skeletonInvoker.invoke(inputStream, outputStream);
}
finally {
resetThreadContextClassLoader(originalClassLoader);
}
}
}

View File

@ -0,0 +1,66 @@
/*
* 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.remoting.caucho;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.FactoryBean;
/**
* FactoryBean for Hessian proxies. Exposes the proxied service for
* use as a bean reference, using the specified service interface.
*
* <p>Hessian is a slim, binary RPC protocol.
* For information on Hessian, see the
* <a href="http://www.caucho.com/hessian">Hessian website</a>
*
* <p>The service URL must be an HTTP URL exposing a Hessian service.
* For details, see the {@link HessianClientInterceptor} javadoc.
*
* @author Juergen Hoeller
* @since 13.05.2003
* @see #setServiceInterface
* @see #setServiceUrl
* @see HessianClientInterceptor
* @see HessianServiceExporter
* @see org.springframework.remoting.caucho.BurlapProxyFactoryBean
* @see org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean
* @see org.springframework.remoting.rmi.RmiProxyFactoryBean
*/
public class HessianProxyFactoryBean extends HessianClientInterceptor implements FactoryBean {
private Object serviceProxy;
public void afterPropertiesSet() {
super.afterPropertiesSet();
this.serviceProxy = new ProxyFactory(getServiceInterface(), this).getProxy(getBeanClassLoader());
}
public Object getObject() {
return this.serviceProxy;
}
public Class getObjectType() {
return getServiceInterface();
}
public boolean isSingleton() {
return true;
}
}

View File

@ -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.remoting.caucho;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.util.NestedServletException;
/**
* Servlet-API-based HTTP request handler that exports the specified service bean
* as Hessian service endpoint, accessible via a Hessian proxy.
*
* <p><b>Note:</b> Spring also provides an alternative version of this exporter,
* for Sun's JRE 1.6 HTTP server: {@link SimpleHessianServiceExporter}.
*
* <p>Hessian is a slim, binary RPC protocol.
* For information on Hessian, see the
* <a href="http://www.caucho.com/hessian">Hessian website</a>.
*
* <p>This exporter will work with both Hessian 2.x and 3.x (respectively
* Resin 2.x and 3.x), autodetecting the corresponding skeleton class.
* As of Spring 2.0, it is also compatible with the new Hessian 2 protocol
* (a.k.a. Hessian 3.0.20+), while remaining compatible with older versions.
*
* <p>Note: Hessian services exported with this class can be accessed by
* any Hessian client, as there isn't any special handling involved.
*
* @author Juergen Hoeller
* @since 13.05.2003
* @see HessianClientInterceptor
* @see HessianProxyFactoryBean
* @see org.springframework.remoting.caucho.BurlapServiceExporter
* @see org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter
* @see org.springframework.remoting.rmi.RmiServiceExporter
*/
public class HessianServiceExporter extends HessianExporter implements HttpRequestHandler {
/**
* Processes the incoming Hessian request and creates a Hessian response.
*/
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if (!"POST".equals(request.getMethod())) {
throw new HttpRequestMethodNotSupportedException(request.getMethod(),
new String[] {"POST"}, "HessianServiceExporter only supports POST requests");
}
try {
invoke(request.getInputStream(), response.getOutputStream());
}
catch (Throwable ex) {
throw new NestedServletException("Hessian skeleton invocation failed", ex);
}
}
}

View File

@ -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.remoting.caucho;
import java.io.InputStream;
import java.io.OutputStream;
import com.caucho.hessian.server.HessianSkeleton;
import com.caucho.hessian.io.SerializerFactory;
import org.springframework.util.Assert;
/**
* Internal invoker strategy for a Hessian skeleton.
* Allows for common handling of Hessian protocol version 1 and 2.
*
* @author Juergen Hoeller
* @since 2.0
*/
abstract class HessianSkeletonInvoker {
/**
* Wrapped HessianSkeleton, available to subclasses.
*/
protected final HessianSkeleton skeleton;
/**
* Hessian SerializerFactory (if any), available to subclasses.
*/
protected final SerializerFactory serializerFactory;
/**
* Create a new HessianSkeletonInvoker for the given skeleton.
* @param skeleton the HessianSkeleton to wrap
* @param serializerFactory the Hessian SerializerFactory to use, if any
*/
public HessianSkeletonInvoker(HessianSkeleton skeleton, SerializerFactory serializerFactory) {
Assert.notNull(skeleton, "HessianSkeleton must not be null");
this.skeleton = skeleton;
this.serializerFactory = serializerFactory;
}
/**
* Invoke the given skeleton based on the given input/output streams.
* @param inputStream the stream containing the Hessian input
* @param outputStream the stream to receive the Hessian output
* @throws Throwable if the skeleton invocation failed
*/
public abstract void invoke(InputStream inputStream, OutputStream outputStream) throws Throwable;
}

View File

@ -0,0 +1,73 @@
/*
* 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.remoting.caucho;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import org.springframework.util.FileCopyUtils;
/**
* HTTP request handler that exports the specified service bean as
* Burlap service endpoint, accessible via a Burlap proxy.
* Designed for Sun's JRE 1.6 HTTP server, implementing the
* {@link com.sun.net.httpserver.HttpHandler} interface.
*
* <p>Burlap is a slim, XML-based RPC protocol.
* For information on Burlap, see the
* <a href="http://www.caucho.com/burlap">Burlap website</a>.
* This exporter requires Burlap 3.x.
*
* <p>Note: Burlap services exported with this class can be accessed by
* any Burlap client, as there isn't any special handling involved.
*
* @author Juergen Hoeller
* @since 2.5.1
* @see org.springframework.remoting.caucho.BurlapClientInterceptor
* @see org.springframework.remoting.caucho.BurlapProxyFactoryBean
* @see SimpleHessianServiceExporter
* @see org.springframework.remoting.httpinvoker.SimpleHttpInvokerServiceExporter
*/
public class SimpleBurlapServiceExporter extends BurlapExporter implements HttpHandler {
/**
* Processes the incoming Burlap request and creates a Burlap response.
*/
public void handle(HttpExchange exchange) throws IOException {
if (!"POST".equals(exchange.getRequestMethod())) {
exchange.getResponseHeaders().set("Allow", "POST");
exchange.sendResponseHeaders(405, -1);
return;
}
ByteArrayOutputStream output = new ByteArrayOutputStream(1024);
try {
invoke(exchange.getRequestBody(), output);
}
catch (Throwable ex) {
exchange.sendResponseHeaders(500, -1);
throw new IOException("Burlap skeleton invocation failed", ex);
}
exchange.sendResponseHeaders(200, output.size());
FileCopyUtils.copy(output.toByteArray(), exchange.getResponseBody());
}
}

View File

@ -0,0 +1,73 @@
/*
* 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.remoting.caucho;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import org.springframework.util.FileCopyUtils;
/**
* HTTP request handler that exports the specified service bean as
* Hessian service endpoint, accessible via a Hessian proxy.
* Designed for Sun's JRE 1.6 HTTP server, implementing the
* {@link com.sun.net.httpserver.HttpHandler} interface.
*
* <p>Hessian is a slim, binary RPC protocol.
* For information on Hessian, see the
* <a href="http://www.caucho.com/hessian">Hessian website</a>.
* This exporter requires Hessian 3.0.20 or above.
*
* <p>Note: Hessian services exported with this class can be accessed by
* any Hessian client, as there isn't any special handling involved.
*
* @author Juergen Hoeller
* @since 2.5.1
* @see org.springframework.remoting.caucho.HessianClientInterceptor
* @see org.springframework.remoting.caucho.HessianProxyFactoryBean
* @see SimpleBurlapServiceExporter
* @see org.springframework.remoting.httpinvoker.SimpleHttpInvokerServiceExporter
*/
public class SimpleHessianServiceExporter extends HessianExporter implements HttpHandler {
/**
* Processes the incoming Hessian request and creates a Hessian response.
*/
public void handle(HttpExchange exchange) throws IOException {
if (!"POST".equals(exchange.getRequestMethod())) {
exchange.getResponseHeaders().set("Allow", "POST");
exchange.sendResponseHeaders(405, -1);
return;
}
ByteArrayOutputStream output = new ByteArrayOutputStream(1024);
try {
invoke(exchange.getRequestBody(), output);
}
catch (Throwable ex) {
exchange.sendResponseHeaders(500, -1);
throw new IOException("Hessian skeleton invocation failed", ex);
}
exchange.sendResponseHeaders(200, output.size());
FileCopyUtils.copy(output.toByteArray(), exchange.getResponseBody());
}
}

View File

@ -0,0 +1,17 @@
<html>
<body>
This package provides remoting classes for Caucho's Hessian and Burlap
protocols: a proxy factory for accessing Hessian/Burlap services,
and an exporter for making beans available to Hessian/Burlap clients.
<p>Hessian is a slim, binary RPC protocol over HTTP.
For information on Hessian, see the
<a href="http://www.caucho.com/hessian">Hessian website</a>
<p>Burlap is a slim, XML-based RPC protocol over HTTP.
For information on Burlap, see the
<a href="http://www.caucho.com/burlap">Burlap website</a>
</body>
</html>

View File

@ -0,0 +1,299 @@
/*
* 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.remoting.httpinvoker;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.rmi.RemoteException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.remoting.rmi.CodebaseAwareObjectInputStream;
import org.springframework.remoting.support.RemoteInvocation;
import org.springframework.remoting.support.RemoteInvocationResult;
import org.springframework.util.Assert;
/**
* Abstract base implementation of the HttpInvokerRequestExecutor interface.
*
* <p>Pre-implements serialization of RemoteInvocation objects and
* deserialization of RemoteInvocationResults objects.
*
* @author Juergen Hoeller
* @since 1.1
* @see #doExecuteRequest
*/
public abstract class AbstractHttpInvokerRequestExecutor
implements HttpInvokerRequestExecutor, BeanClassLoaderAware {
/**
* Default content type: "application/x-java-serialized-object"
*/
public static final String CONTENT_TYPE_SERIALIZED_OBJECT = "application/x-java-serialized-object";
protected static final String HTTP_METHOD_POST = "POST";
protected static final String HTTP_HEADER_ACCEPT_LANGUAGE = "Accept-Language";
protected static final String HTTP_HEADER_ACCEPT_ENCODING = "Accept-Encoding";
protected static final String HTTP_HEADER_CONTENT_ENCODING = "Content-Encoding";
protected static final String HTTP_HEADER_CONTENT_TYPE = "Content-Type";
protected static final String HTTP_HEADER_CONTENT_LENGTH = "Content-Length";
protected static final String ENCODING_GZIP = "gzip";
private static final int SERIALIZED_INVOCATION_BYTE_ARRAY_INITIAL_SIZE = 1024;
protected final Log logger = LogFactory.getLog(getClass());
private String contentType = CONTENT_TYPE_SERIALIZED_OBJECT;
private boolean acceptGzipEncoding = true;
private ClassLoader beanClassLoader;
/**
* Specify the content type to use for sending HTTP invoker requests.
* <p>Default is "application/x-java-serialized-object".
*/
public void setContentType(String contentType) {
Assert.notNull(contentType, "'contentType' must not be null");
this.contentType = contentType;
}
/**
* Return the content type to use for sending HTTP invoker requests.
*/
public String getContentType() {
return this.contentType;
}
/**
* Set whether to accept GZIP encoding, that is, whether to
* send the HTTP "Accept-Encoding" header with "gzip" as value.
* <p>Default is "true". Turn this flag off if you do not want
* GZIP response compression even if enabled on the HTTP server.
*/
public void setAcceptGzipEncoding(boolean acceptGzipEncoding) {
this.acceptGzipEncoding = acceptGzipEncoding;
}
/**
* Return whether to accept GZIP encoding, that is, whether to
* send the HTTP "Accept-Encoding" header with "gzip" as value.
*/
public boolean isAcceptGzipEncoding() {
return this.acceptGzipEncoding;
}
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
/**
* Return the bean ClassLoader that this executor is supposed to use.
*/
protected ClassLoader getBeanClassLoader() {
return this.beanClassLoader;
}
public final RemoteInvocationResult executeRequest(
HttpInvokerClientConfiguration config, RemoteInvocation invocation) throws Exception {
ByteArrayOutputStream baos = getByteArrayOutputStream(invocation);
if (logger.isDebugEnabled()) {
logger.debug("Sending HTTP invoker request for service at [" + config.getServiceUrl() +
"], with size " + baos.size());
}
return doExecuteRequest(config, baos);
}
/**
* Serialize the given RemoteInvocation into a ByteArrayOutputStream.
* @param invocation the RemoteInvocation object
* @return a ByteArrayOutputStream with the serialized RemoteInvocation
* @throws IOException if thrown by I/O methods
*/
protected ByteArrayOutputStream getByteArrayOutputStream(RemoteInvocation invocation) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream(SERIALIZED_INVOCATION_BYTE_ARRAY_INITIAL_SIZE);
writeRemoteInvocation(invocation, baos);
return baos;
}
/**
* Serialize the given RemoteInvocation to the given OutputStream.
* <p>The default implementation gives <code>decorateOutputStream</code> a chance
* to decorate the stream first (for example, for custom encryption or compression).
* Creates an <code>ObjectOutputStream</code> for the final stream and calls
* <code>doWriteRemoteInvocation</code> to actually write the object.
* <p>Can be overridden for custom serialization of the invocation.
* @param invocation the RemoteInvocation object
* @param os the OutputStream to write to
* @throws IOException if thrown by I/O methods
* @see #decorateOutputStream
* @see #doWriteRemoteInvocation
*/
protected void writeRemoteInvocation(RemoteInvocation invocation, OutputStream os) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(decorateOutputStream(os));
try {
doWriteRemoteInvocation(invocation, oos);
oos.flush();
}
finally {
oos.close();
}
}
/**
* Return the OutputStream to use for writing remote invocations,
* potentially decorating the given original OutputStream.
* <p>The default implementation returns the given stream as-is.
* Can be overridden, for example, for custom encryption or compression.
* @param os the original OutputStream
* @return the potentially decorated OutputStream
*/
protected OutputStream decorateOutputStream(OutputStream os) throws IOException {
return os;
}
/**
* Perform the actual writing of the given invocation object to the
* given ObjectOutputStream.
* <p>The default implementation simply calls <code>writeObject</code>.
* Can be overridden for serialization of a custom wrapper object rather
* than the plain invocation, for example an encryption-aware holder.
* @param invocation the RemoteInvocation object
* @param oos the ObjectOutputStream to write to
* @throws IOException if thrown by I/O methods
* @see java.io.ObjectOutputStream#writeObject
*/
protected void doWriteRemoteInvocation(RemoteInvocation invocation, ObjectOutputStream oos) throws IOException {
oos.writeObject(invocation);
}
/**
* Execute a request to send the given serialized remote invocation.
* <p>Implementations will usually call <code>readRemoteInvocationResult</code>
* to deserialize a returned RemoteInvocationResult object.
* @param config the HTTP invoker configuration that specifies the
* target service
* @param baos the ByteArrayOutputStream that contains the serialized
* RemoteInvocation object
* @return the RemoteInvocationResult object
* @throws IOException if thrown by I/O operations
* @throws ClassNotFoundException if thrown during deserialization
* @throws Exception in case of general errors
* @see #readRemoteInvocationResult(java.io.InputStream, String)
*/
protected abstract RemoteInvocationResult doExecuteRequest(
HttpInvokerClientConfiguration config, ByteArrayOutputStream baos)
throws Exception;
/**
* Deserialize a RemoteInvocationResult object from the given InputStream.
* <p>Gives <code>decorateInputStream</code> a chance to decorate the stream
* first (for example, for custom encryption or compression). Creates an
* <code>ObjectInputStream</code> via <code>createObjectInputStream</code> and
* calls <code>doReadRemoteInvocationResult</code> to actually read the object.
* <p>Can be overridden for custom serialization of the invocation.
* @param is the InputStream to read from
* @param codebaseUrl the codebase URL to load classes from if not found locally
* @return the RemoteInvocationResult object
* @throws IOException if thrown by I/O methods
* @throws ClassNotFoundException if thrown during deserialization
* @see #decorateInputStream
* @see #createObjectInputStream
* @see #doReadRemoteInvocationResult
*/
protected RemoteInvocationResult readRemoteInvocationResult(InputStream is, String codebaseUrl)
throws IOException, ClassNotFoundException {
ObjectInputStream ois = createObjectInputStream(decorateInputStream(is), codebaseUrl);
try {
return doReadRemoteInvocationResult(ois);
}
finally {
ois.close();
}
}
/**
* Return the InputStream to use for reading remote invocation results,
* potentially decorating the given original InputStream.
* <p>The default implementation returns the given stream as-is.
* Can be overridden, for example, for custom encryption or compression.
* @param is the original InputStream
* @return the potentially decorated InputStream
*/
protected InputStream decorateInputStream(InputStream is) throws IOException {
return is;
}
/**
* Create an ObjectInputStream for the given InputStream and codebase.
* The default implementation creates a CodebaseAwareObjectInputStream.
* @param is the InputStream to read from
* @param codebaseUrl the codebase URL to load classes from if not found locally
* (can be <code>null</code>)
* @return the new ObjectInputStream instance to use
* @throws IOException if creation of the ObjectInputStream failed
* @see org.springframework.remoting.rmi.CodebaseAwareObjectInputStream
*/
protected ObjectInputStream createObjectInputStream(InputStream is, String codebaseUrl) throws IOException {
return new CodebaseAwareObjectInputStream(is, getBeanClassLoader(), codebaseUrl);
}
/**
* Perform the actual reading of an invocation object from the
* given ObjectInputStream.
* <p>The default implementation simply calls <code>readObject</code>.
* Can be overridden for deserialization of a custom wrapper object rather
* than the plain invocation, for example an encryption-aware holder.
* @param ois the ObjectInputStream to read from
* @return the RemoteInvocationResult object
* @throws IOException if thrown by I/O methods
* @throws ClassNotFoundException if the class name of a serialized object
* couldn't get resolved
* @see java.io.ObjectOutputStream#writeObject
*/
protected RemoteInvocationResult doReadRemoteInvocationResult(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
Object obj = ois.readObject();
if (!(obj instanceof RemoteInvocationResult)) {
throw new RemoteException("Deserialized object needs to be assignable to type [" +
RemoteInvocationResult.class.getName() + "]: " + obj);
}
return (RemoteInvocationResult) obj;
}
}

View File

@ -0,0 +1,258 @@
/*
* 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.remoting.httpinvoker;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.GZIPInputStream;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
import org.springframework.context.i18n.LocaleContext;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.remoting.support.RemoteInvocationResult;
import org.springframework.util.StringUtils;
/**
* {@link HttpInvokerRequestExecutor} implementation that uses
* <a href="http://jakarta.apache.org/commons/httpclient">Jakarta Commons HttpClient</a>
* to execute POST requests. Requires Commons HttpClient 3.0 or higher.
*
* <p>Allows to use a pre-configured {@link org.apache.commons.httpclient.HttpClient}
* instance, potentially with authentication, HTTP connection pooling, etc.
* Also designed for easy subclassing, providing specific template methods.
*
* @author Juergen Hoeller
* @author Mark Fisher
* @since 1.1
* @see SimpleHttpInvokerRequestExecutor
*/
public class CommonsHttpInvokerRequestExecutor extends AbstractHttpInvokerRequestExecutor {
/**
* Default timeout value if no HttpClient is explicitly provided.
*/
private static final int DEFAULT_READ_TIMEOUT_MILLISECONDS = (60 * 1000);
private HttpClient httpClient;
/**
* Create a new CommonsHttpInvokerRequestExecutor with a default
* HttpClient that uses a default MultiThreadedHttpConnectionManager.
* Sets the socket read timeout to {@link #DEFAULT_READ_TIMEOUT_MILLISECONDS}.
* @see org.apache.commons.httpclient.HttpClient
* @see org.apache.commons.httpclient.MultiThreadedHttpConnectionManager
*/
public CommonsHttpInvokerRequestExecutor() {
this.httpClient = new HttpClient(new MultiThreadedHttpConnectionManager());
this.setReadTimeout(DEFAULT_READ_TIMEOUT_MILLISECONDS);
}
/**
* Create a new CommonsHttpInvokerRequestExecutor with the given
* HttpClient instance. The socket read timeout of the provided
* HttpClient will not be changed.
* @param httpClient the HttpClient instance to use for this request executor
*/
public CommonsHttpInvokerRequestExecutor(HttpClient httpClient) {
this.httpClient = httpClient;
}
/**
* Set the HttpClient instance to use for this request executor.
*/
public void setHttpClient(HttpClient httpClient) {
this.httpClient = httpClient;
}
/**
* Return the HttpClient instance that this request executor uses.
*/
public HttpClient getHttpClient() {
return this.httpClient;
}
/**
* Set the socket read timeout for the underlying HttpClient. A value
* of 0 means <emphasis>never</emphasis> timeout.
* @param timeout the timeout value in milliseconds
* @see org.apache.commons.httpclient.params.HttpConnectionManagerParams#setSoTimeout(int)
* @see #DEFAULT_READ_TIMEOUT_MILLISECONDS
*/
public void setReadTimeout(int timeout) {
if (timeout < 0) {
throw new IllegalArgumentException("timeout must be a non-negative value");
}
this.httpClient.getHttpConnectionManager().getParams().setSoTimeout(timeout);
}
/**
* Execute the given request through Commons HttpClient.
* <p>This method implements the basic processing workflow:
* The actual work happens in this class's template methods.
* @see #createPostMethod
* @see #setRequestBody
* @see #executePostMethod
* @see #validateResponse
* @see #getResponseBody
*/
protected RemoteInvocationResult doExecuteRequest(
HttpInvokerClientConfiguration config, ByteArrayOutputStream baos)
throws IOException, ClassNotFoundException {
PostMethod postMethod = createPostMethod(config);
try {
setRequestBody(config, postMethod, baos);
executePostMethod(config, getHttpClient(), postMethod);
validateResponse(config, postMethod);
InputStream responseBody = getResponseBody(config, postMethod);
return readRemoteInvocationResult(responseBody, config.getCodebaseUrl());
}
finally {
// Need to explicitly release because it might be pooled.
postMethod.releaseConnection();
}
}
/**
* Create a PostMethod for the given configuration.
* <p>The default implementation creates a standard PostMethod with
* "application/x-java-serialized-object" as "Content-Type" header.
* @param config the HTTP invoker configuration that specifies the
* target service
* @return the PostMethod instance
* @throws IOException if thrown by I/O methods
*/
protected PostMethod createPostMethod(HttpInvokerClientConfiguration config) throws IOException {
PostMethod postMethod = new PostMethod(config.getServiceUrl());
LocaleContext locale = LocaleContextHolder.getLocaleContext();
if (locale != null) {
postMethod.addRequestHeader(HTTP_HEADER_ACCEPT_LANGUAGE, StringUtils.toLanguageTag(locale.getLocale()));
}
if (isAcceptGzipEncoding()) {
postMethod.addRequestHeader(HTTP_HEADER_ACCEPT_ENCODING, ENCODING_GZIP);
}
return postMethod;
}
/**
* Set the given serialized remote invocation as request body.
* <p>The default implementation simply sets the serialized invocation
* as the PostMethod's request body. This can be overridden, for example,
* to write a specific encoding and potentially set appropriate HTTP
* request headers.
* @param config the HTTP invoker configuration that specifies the target service
* @param postMethod the PostMethod to set the request body on
* @param baos the ByteArrayOutputStream that contains the serialized
* RemoteInvocation object
* @throws IOException if thrown by I/O methods
* @see org.apache.commons.httpclient.methods.PostMethod#setRequestBody(java.io.InputStream)
* @see org.apache.commons.httpclient.methods.PostMethod#setRequestEntity
* @see org.apache.commons.httpclient.methods.InputStreamRequestEntity
*/
protected void setRequestBody(
HttpInvokerClientConfiguration config, PostMethod postMethod, ByteArrayOutputStream baos)
throws IOException {
postMethod.setRequestEntity(new ByteArrayRequestEntity(baos.toByteArray(), getContentType()));
}
/**
* Execute the given PostMethod instance.
* @param config the HTTP invoker configuration that specifies the target service
* @param httpClient the HttpClient to execute on
* @param postMethod the PostMethod to execute
* @throws IOException if thrown by I/O methods
* @see org.apache.commons.httpclient.HttpClient#executeMethod(org.apache.commons.httpclient.HttpMethod)
*/
protected void executePostMethod(
HttpInvokerClientConfiguration config, HttpClient httpClient, PostMethod postMethod)
throws IOException {
httpClient.executeMethod(postMethod);
}
/**
* Validate the given response as contained in the PostMethod object,
* throwing an exception if it does not correspond to a successful HTTP response.
* <p>Default implementation rejects any HTTP status code beyond 2xx, to avoid
* parsing the response body and trying to deserialize from a corrupted stream.
* @param config the HTTP invoker configuration that specifies the target service
* @param postMethod the executed PostMethod to validate
* @throws IOException if validation failed
* @see org.apache.commons.httpclient.methods.PostMethod#getStatusCode()
* @see org.apache.commons.httpclient.HttpException
*/
protected void validateResponse(HttpInvokerClientConfiguration config, PostMethod postMethod)
throws IOException {
if (postMethod.getStatusCode() >= 300) {
throw new HttpException(
"Did not receive successful HTTP response: status code = " + postMethod.getStatusCode() +
", status message = [" + postMethod.getStatusText() + "]");
}
}
/**
* Extract the response body from the given executed remote invocation
* request.
* <p>The default implementation simply fetches the PostMethod's response
* body stream. If the response is recognized as GZIP response, the
* InputStream will get wrapped in a GZIPInputStream.
* @param config the HTTP invoker configuration that specifies the target service
* @param postMethod the PostMethod to read the response body from
* @return an InputStream for the response body
* @throws IOException if thrown by I/O methods
* @see #isGzipResponse
* @see java.util.zip.GZIPInputStream
* @see org.apache.commons.httpclient.methods.PostMethod#getResponseBodyAsStream()
* @see org.apache.commons.httpclient.methods.PostMethod#getResponseHeader(String)
*/
protected InputStream getResponseBody(HttpInvokerClientConfiguration config, PostMethod postMethod)
throws IOException {
if (isGzipResponse(postMethod)) {
return new GZIPInputStream(postMethod.getResponseBodyAsStream());
}
else {
return postMethod.getResponseBodyAsStream();
}
}
/**
* Determine whether the given response indicates a GZIP response.
* <p>Default implementation checks whether the HTTP "Content-Encoding"
* header contains "gzip" (in any casing).
* @param postMethod the PostMethod to check
* @return whether the given response indicates a GZIP response
*/
protected boolean isGzipResponse(PostMethod postMethod) {
Header encodingHeader = postMethod.getResponseHeader(HTTP_HEADER_CONTENT_ENCODING);
return (encodingHeader != null && encodingHeader.getValue() != null &&
encodingHeader.getValue().toLowerCase().indexOf(ENCODING_GZIP) != -1);
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2002-2005 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.remoting.httpinvoker;
/**
* Configuration interface for executing HTTP invoker requests.
*
* @author Juergen Hoeller
* @since 1.1
* @see HttpInvokerRequestExecutor
* @see HttpInvokerClientInterceptor
*/
public interface HttpInvokerClientConfiguration {
/**
* Return the HTTP URL of the target service.
*/
String getServiceUrl();
/**
* Return the codebase URL to download classes from if not found locally.
* Can consist of multiple URLs, separated by spaces.
* @return the codebase URL, or <code>null</code> if none
* @see java.rmi.server.RMIClassLoader
*/
String getCodebaseUrl();
}

View File

@ -0,0 +1,216 @@
/*
* 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.remoting.httpinvoker;
import java.io.IOException;
import java.io.InvalidClassException;
import java.net.ConnectException;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.AopUtils;
import org.springframework.remoting.RemoteAccessException;
import org.springframework.remoting.RemoteConnectFailureException;
import org.springframework.remoting.RemoteInvocationFailureException;
import org.springframework.remoting.support.RemoteInvocation;
import org.springframework.remoting.support.RemoteInvocationBasedAccessor;
import org.springframework.remoting.support.RemoteInvocationResult;
/**
* {@link org.aopalliance.intercept.MethodInterceptor} for accessing an
* HTTP invoker service. The service URL must be an HTTP URL exposing
* an HTTP invoker service.
*
* <p>Serializes remote invocation objects and deserializes remote invocation
* result objects. Uses Java serialization just like RMI, but provides the
* same ease of setup as Caucho's HTTP-based Hessian and Burlap protocols.
*
* <P>HTTP invoker is a very extensible and customizable protocol.
* It supports the RemoteInvocationFactory mechanism, like RMI invoker,
* allowing to include additional invocation attributes (for example,
* a security context). Furthermore, it allows to customize request
* execution via the {@link HttpInvokerRequestExecutor} strategy.
*
* <p>Can use the JDK's {@link java.rmi.server.RMIClassLoader} to load
* classes from a given {@link #setCodebaseUrl codebase}, performing
* on-demand dynamic code download from a remote location. The codebase
* can consist of multiple URLs, separated by spaces. Note that
* RMIClassLoader requires a SecurityManager to be set, analogous to
* when using dynamic class download with standard RMI!
* (See the RMI documentation for details.)
*
* @author Juergen Hoeller
* @since 1.1
* @see #setServiceUrl
* @see #setCodebaseUrl
* @see #setRemoteInvocationFactory
* @see #setHttpInvokerRequestExecutor
* @see HttpInvokerServiceExporter
* @see HttpInvokerProxyFactoryBean
* @see java.rmi.server.RMIClassLoader
*/
public class HttpInvokerClientInterceptor extends RemoteInvocationBasedAccessor
implements MethodInterceptor, HttpInvokerClientConfiguration {
private String codebaseUrl;
private HttpInvokerRequestExecutor httpInvokerRequestExecutor;
/**
* Set the codebase URL to download classes from if not found locally.
* Can consists of multiple URLs, separated by spaces.
* <p>Follows RMI's codebase conventions for dynamic class download.
* In contrast to RMI, where the server determines the URL for class download
* (via the "java.rmi.server.codebase" system property), it's the client
* that determines the codebase URL here. The server will usually be the
* same as for the service URL, just pointing to a different path there.
* @see #setServiceUrl
* @see org.springframework.remoting.rmi.CodebaseAwareObjectInputStream
* @see java.rmi.server.RMIClassLoader
*/
public void setCodebaseUrl(String codebaseUrl) {
this.codebaseUrl = codebaseUrl;
}
/**
* Return the codebase URL to download classes from if not found locally.
*/
public String getCodebaseUrl() {
return this.codebaseUrl;
}
/**
* Set the HttpInvokerRequestExecutor implementation to use for executing
* remote invocations.
* <p>Default is {@link SimpleHttpInvokerRequestExecutor}. Alternatively,
* consider using {@link CommonsHttpInvokerRequestExecutor} for more
* sophisticated needs.
* @see SimpleHttpInvokerRequestExecutor
* @see CommonsHttpInvokerRequestExecutor
*/
public void setHttpInvokerRequestExecutor(HttpInvokerRequestExecutor httpInvokerRequestExecutor) {
this.httpInvokerRequestExecutor = httpInvokerRequestExecutor;
}
/**
* Return the HttpInvokerRequestExecutor used by this remote accessor.
* <p>Creates a default SimpleHttpInvokerRequestExecutor if no executor
* has been initialized already.
*/
public HttpInvokerRequestExecutor getHttpInvokerRequestExecutor() {
if (this.httpInvokerRequestExecutor == null) {
SimpleHttpInvokerRequestExecutor executor = new SimpleHttpInvokerRequestExecutor();
executor.setBeanClassLoader(getBeanClassLoader());
this.httpInvokerRequestExecutor = executor;
}
return this.httpInvokerRequestExecutor;
}
public void afterPropertiesSet() {
super.afterPropertiesSet();
// Eagerly initialize the default HttpInvokerRequestExecutor, if needed.
getHttpInvokerRequestExecutor();
}
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
if (AopUtils.isToStringMethod(methodInvocation.getMethod())) {
return "HTTP invoker proxy for service URL [" + getServiceUrl() + "]";
}
RemoteInvocation invocation = createRemoteInvocation(methodInvocation);
RemoteInvocationResult result = null;
try {
result = executeRequest(invocation, methodInvocation);
}
catch (Throwable ex) {
throw convertHttpInvokerAccessException(ex);
}
try {
return recreateRemoteInvocationResult(result);
}
catch (Throwable ex) {
if (result.hasInvocationTargetException()) {
throw ex;
}
else {
throw new RemoteInvocationFailureException("Invocation of method [" + methodInvocation.getMethod() +
"] failed in HTTP invoker remote service at [" + getServiceUrl() + "]", ex);
}
}
}
/**
* Execute the given remote invocation via the HttpInvokerRequestExecutor.
* <p>This implementation delegates to {@link #executeRequest(RemoteInvocation)}.
* Can be overridden to react to the specific original MethodInvocation.
* @param invocation the RemoteInvocation to execute
* @param originalInvocation the original MethodInvocation (can e.g. be cast
* to the ProxyMethodInvocation interface for accessing user attributes)
* @return the RemoteInvocationResult object
* @throws Exception in case of errors
*/
protected RemoteInvocationResult executeRequest(
RemoteInvocation invocation, MethodInvocation originalInvocation) throws Exception {
return executeRequest(invocation);
}
/**
* Execute the given remote invocation via the HttpInvokerRequestExecutor.
* <p>Can be overridden in subclasses to pass a different configuration object
* to the executor. Alternatively, add further configuration properties in a
* subclass of this accessor: By default, the accessor passed itself as
* configuration object to the executor.
* @param invocation the RemoteInvocation to execute
* @return the RemoteInvocationResult object
* @throws IOException if thrown by I/O operations
* @throws ClassNotFoundException if thrown during deserialization
* @throws Exception in case of general errors
* @see #getHttpInvokerRequestExecutor
* @see HttpInvokerClientConfiguration
*/
protected RemoteInvocationResult executeRequest(RemoteInvocation invocation) throws Exception {
return getHttpInvokerRequestExecutor().executeRequest(this, invocation);
}
/**
* Convert the given HTTP invoker access exception to an appropriate
* Spring RemoteAccessException.
* @param ex the exception to convert
* @return the RemoteAccessException to throw
*/
protected RemoteAccessException convertHttpInvokerAccessException(Throwable ex) {
if (ex instanceof ConnectException) {
throw new RemoteConnectFailureException(
"Could not connect to HTTP invoker remote service at [" + getServiceUrl() + "]", ex);
}
else if (ex instanceof ClassNotFoundException || ex instanceof NoClassDefFoundError ||
ex instanceof InvalidClassException) {
throw new RemoteAccessException(
"Could not deserialize result from HTTP invoker remote service [" + getServiceUrl() + "]", ex);
}
else {
throw new RemoteAccessException(
"Could not access HTTP invoker remote service at [" + getServiceUrl() + "]", ex);
}
}
}

View File

@ -0,0 +1,77 @@
/*
* 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.remoting.httpinvoker;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.FactoryBean;
/**
* FactoryBean for HTTP invoker proxies. Exposes the proxied service for
* use as a bean reference, using the specified service interface.
*
* <p>The service URL must be an HTTP URL exposing an HTTP invoker service.
* Optionally, a codebase URL can be specified for on-demand dynamic code download
* from a remote location. For details, see HttpInvokerClientInterceptor docs.
*
* <p>Serializes remote invocation objects and deserializes remote invocation
* result objects. Uses Java serialization just like RMI, but provides the
* same ease of setup as Caucho's HTTP-based Hessian and Burlap protocols.
*
* <p><b>HTTP invoker is the recommended protocol for Java-to-Java remoting.</b>
* It is more powerful and more extensible than Hessian and Burlap, at the
* expense of being tied to Java. Nevertheless, it is as easy to set up as
* Hessian and Burlap, which is its main advantage compared to RMI.
*
* @author Juergen Hoeller
* @since 1.1
* @see #setServiceInterface
* @see #setServiceUrl
* @see #setCodebaseUrl
* @see HttpInvokerClientInterceptor
* @see HttpInvokerServiceExporter
* @see org.springframework.remoting.rmi.RmiProxyFactoryBean
* @see org.springframework.remoting.caucho.HessianProxyFactoryBean
* @see org.springframework.remoting.caucho.BurlapProxyFactoryBean
*/
public class HttpInvokerProxyFactoryBean extends HttpInvokerClientInterceptor
implements FactoryBean {
private Object serviceProxy;
public void afterPropertiesSet() {
super.afterPropertiesSet();
if (getServiceInterface() == null) {
throw new IllegalArgumentException("Property 'serviceInterface' is required");
}
this.serviceProxy = new ProxyFactory(getServiceInterface(), this).getProxy(getBeanClassLoader());
}
public Object getObject() {
return this.serviceProxy;
}
public Class getObjectType() {
return getServiceInterface();
}
public boolean isSingleton() {
return true;
}
}

View File

@ -0,0 +1,59 @@
/*
* Copyright 2002-2005 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.remoting.httpinvoker;
import java.io.IOException;
import org.springframework.remoting.support.RemoteInvocation;
import org.springframework.remoting.support.RemoteInvocationResult;
/**
* Strategy interface for actual execution of an HTTP invoker request.
* Used by HttpInvokerClientInterceptor and its subclass
* HttpInvokerProxyFactoryBean.
*
* <p>Two implementations are provided out of the box:
* <ul>
* <li><b>SimpleHttpInvokerRequestExecutor:</b>
* Uses J2SE facilities to execute POST requests, without support
* for HTTP authentication or advanced configuration options.
* <li><b>CommonsHttpInvokerRequestExecutor:</b>
* Uses Jakarta's Commons HttpClient to execute POST requests,
* allowing to use a preconfigured HttpClient instance
* (potentially with authentication, HTTP connection pooling, etc).
* </ul>
*
* @author Juergen Hoeller
* @since 1.1
* @see HttpInvokerClientInterceptor#setHttpInvokerRequestExecutor
*/
public interface HttpInvokerRequestExecutor {
/**
* Execute a request to send the given remote invocation.
* @param config the HTTP invoker configuration that specifies the
* target service
* @param invocation the RemoteInvocation to execute
* @return the RemoteInvocationResult object
* @throws IOException if thrown by I/O operations
* @throws ClassNotFoundException if thrown during deserialization
* @throws Exception in case of general errors
*/
RemoteInvocationResult executeRequest(HttpInvokerClientConfiguration config, RemoteInvocation invocation)
throws Exception;
}

View File

@ -0,0 +1,199 @@
/*
* 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.remoting.httpinvoker;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.remoting.rmi.RemoteInvocationSerializingExporter;
import org.springframework.remoting.support.RemoteInvocation;
import org.springframework.remoting.support.RemoteInvocationResult;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.util.NestedServletException;
/**
* Servlet-API-based HTTP request handler that exports the specified service bean
* as HTTP invoker service endpoint, accessible via an HTTP invoker proxy.
*
* <p><b>Note:</b> Spring also provides an alternative version of this exporter,
* for Sun's JRE 1.6 HTTP server: {@link SimpleHttpInvokerServiceExporter}.
*
* <p>Deserializes remote invocation objects and serializes remote invocation
* result objects. Uses Java serialization just like RMI, but provides the
* same ease of setup as Caucho's HTTP-based Hessian and Burlap protocols.
*
* <p><b>HTTP invoker is the recommended protocol for Java-to-Java remoting.</b>
* It is more powerful and more extensible than Hessian and Burlap, at the
* expense of being tied to Java. Nevertheless, it is as easy to set up as
* Hessian and Burlap, which is its main advantage compared to RMI.
*
* @author Juergen Hoeller
* @since 1.1
* @see HttpInvokerClientInterceptor
* @see HttpInvokerProxyFactoryBean
* @see org.springframework.remoting.rmi.RmiServiceExporter
* @see org.springframework.remoting.caucho.HessianServiceExporter
* @see org.springframework.remoting.caucho.BurlapServiceExporter
*/
public class HttpInvokerServiceExporter extends RemoteInvocationSerializingExporter
implements HttpRequestHandler {
/**
* Reads a remote invocation from the request, executes it,
* and writes the remote invocation result to the response.
* @see #readRemoteInvocation(HttpServletRequest)
* @see #invokeAndCreateResult(org.springframework.remoting.support.RemoteInvocation, Object)
* @see #writeRemoteInvocationResult(HttpServletRequest, HttpServletResponse, RemoteInvocationResult)
*/
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
RemoteInvocation invocation = readRemoteInvocation(request);
RemoteInvocationResult result = invokeAndCreateResult(invocation, getProxy());
writeRemoteInvocationResult(request, response, result);
}
catch (ClassNotFoundException ex) {
throw new NestedServletException("Class not found during deserialization", ex);
}
}
/**
* Read a RemoteInvocation from the given HTTP request.
* <p>Delegates to
* {@link #readRemoteInvocation(javax.servlet.http.HttpServletRequest, java.io.InputStream)}
* with the
* {@link javax.servlet.ServletRequest#getInputStream() servlet request's input stream}.
* @param request current HTTP request
* @return the RemoteInvocation object
* @throws IOException in case of I/O failure
* @throws ClassNotFoundException if thrown by deserialization
*/
protected RemoteInvocation readRemoteInvocation(HttpServletRequest request)
throws IOException, ClassNotFoundException {
return readRemoteInvocation(request, request.getInputStream());
}
/**
* Deserialize a RemoteInvocation object from the given InputStream.
* <p>Gives {@link #decorateInputStream} a chance to decorate the stream
* first (for example, for custom encryption or compression). Creates a
* {@link org.springframework.remoting.rmi.CodebaseAwareObjectInputStream}
* and calls {@link #doReadRemoteInvocation} to actually read the object.
* <p>Can be overridden for custom serialization of the invocation.
* @param request current HTTP request
* @param is the InputStream to read from
* @return the RemoteInvocation object
* @throws IOException in case of I/O failure
* @throws ClassNotFoundException if thrown during deserialization
*/
protected RemoteInvocation readRemoteInvocation(HttpServletRequest request, InputStream is)
throws IOException, ClassNotFoundException {
ObjectInputStream ois = createObjectInputStream(decorateInputStream(request, is));
try {
return doReadRemoteInvocation(ois);
}
finally {
ois.close();
}
}
/**
* Return the InputStream to use for reading remote invocations,
* potentially decorating the given original InputStream.
* <p>The default implementation returns the given stream as-is.
* Can be overridden, for example, for custom encryption or compression.
* @param request current HTTP request
* @param is the original InputStream
* @return the potentially decorated InputStream
* @throws IOException in case of I/O failure
*/
protected InputStream decorateInputStream(HttpServletRequest request, InputStream is) throws IOException {
return is;
}
/**
* Write the given RemoteInvocationResult to the given HTTP response.
* @param request current HTTP request
* @param response current HTTP response
* @param result the RemoteInvocationResult object
* @throws IOException in case of I/O failure
*/
protected void writeRemoteInvocationResult(
HttpServletRequest request, HttpServletResponse response, RemoteInvocationResult result)
throws IOException {
response.setContentType(getContentType());
writeRemoteInvocationResult(request, response, result, response.getOutputStream());
}
/**
* Serialize the given RemoteInvocation to the given OutputStream.
* <p>The default implementation gives {@link #decorateOutputStream} a chance
* to decorate the stream first (for example, for custom encryption or compression).
* Creates an {@link java.io.ObjectOutputStream} for the final stream and calls
* {@link #doWriteRemoteInvocationResult} to actually write the object.
* <p>Can be overridden for custom serialization of the invocation.
* @param request current HTTP request
* @param response current HTTP response
* @param result the RemoteInvocationResult object
* @param os the OutputStream to write to
* @throws IOException in case of I/O failure
* @see #decorateOutputStream
* @see #doWriteRemoteInvocationResult
*/
protected void writeRemoteInvocationResult(
HttpServletRequest request, HttpServletResponse response, RemoteInvocationResult result, OutputStream os)
throws IOException {
ObjectOutputStream oos = createObjectOutputStream(decorateOutputStream(request, response, os));
try {
doWriteRemoteInvocationResult(result, oos);
oos.flush();
}
finally {
oos.close();
}
}
/**
* Return the OutputStream to use for writing remote invocation results,
* potentially decorating the given original OutputStream.
* <p>The default implementation returns the given stream as-is.
* Can be overridden, for example, for custom encryption or compression.
* @param request current HTTP request
* @param response current HTTP response
* @param os the original OutputStream
* @return the potentially decorated OutputStream
* @throws IOException in case of I/O failure
*/
protected OutputStream decorateOutputStream(
HttpServletRequest request, HttpServletResponse response, OutputStream os) throws IOException {
return os;
}
}

View File

@ -0,0 +1,192 @@
/*
* 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.remoting.httpinvoker;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.zip.GZIPInputStream;
import org.springframework.context.i18n.LocaleContext;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.remoting.support.RemoteInvocationResult;
import org.springframework.util.StringUtils;
/**
* HttpInvokerRequestExecutor implementation that uses standard J2SE facilities
* to execute POST requests, without support for HTTP authentication or
* advanced configuration options.
*
* <p>Designed for easy subclassing, customizing specific template methods.
* However, consider CommonsHttpInvokerRequestExecutor for more sophisticated
* needs: The J2SE HttpURLConnection is rather limited in its capabilities.
*
* @author Juergen Hoeller
* @since 1.1
* @see CommonsHttpInvokerRequestExecutor
* @see java.net.HttpURLConnection
*/
public class SimpleHttpInvokerRequestExecutor extends AbstractHttpInvokerRequestExecutor {
/**
* Execute the given request through a standard J2SE HttpURLConnection.
* <p>This method implements the basic processing workflow:
* The actual work happens in this class's template methods.
* @see #openConnection
* @see #prepareConnection
* @see #writeRequestBody
* @see #validateResponse
* @see #readResponseBody
*/
protected RemoteInvocationResult doExecuteRequest(
HttpInvokerClientConfiguration config, ByteArrayOutputStream baos)
throws IOException, ClassNotFoundException {
HttpURLConnection con = openConnection(config);
prepareConnection(con, baos.size());
writeRequestBody(config, con, baos);
validateResponse(config, con);
InputStream responseBody = readResponseBody(config, con);
return readRemoteInvocationResult(responseBody, config.getCodebaseUrl());
}
/**
* Open an HttpURLConnection for the given remote invocation request.
* @param config the HTTP invoker configuration that specifies the
* target service
* @return the HttpURLConnection for the given request
* @throws IOException if thrown by I/O methods
* @see java.net.URL#openConnection()
*/
protected HttpURLConnection openConnection(HttpInvokerClientConfiguration config) throws IOException {
URLConnection con = new URL(config.getServiceUrl()).openConnection();
if (!(con instanceof HttpURLConnection)) {
throw new IOException("Service URL [" + config.getServiceUrl() + "] is not an HTTP URL");
}
return (HttpURLConnection) con;
}
/**
* Prepare the given HTTP connection.
* <p>The default implementation specifies POST as method,
* "application/x-java-serialized-object" as "Content-Type" header,
* and the given content length as "Content-Length" header.
* @param con the HTTP connection to prepare
* @param contentLength the length of the content to send
* @throws IOException if thrown by HttpURLConnection methods
* @see java.net.HttpURLConnection#setRequestMethod
* @see java.net.HttpURLConnection#setRequestProperty
*/
protected void prepareConnection(HttpURLConnection con, int contentLength) throws IOException {
con.setDoOutput(true);
con.setRequestMethod(HTTP_METHOD_POST);
con.setRequestProperty(HTTP_HEADER_CONTENT_TYPE, getContentType());
con.setRequestProperty(HTTP_HEADER_CONTENT_LENGTH, Integer.toString(contentLength));
LocaleContext locale = LocaleContextHolder.getLocaleContext();
if (locale != null) {
con.setRequestProperty(HTTP_HEADER_ACCEPT_LANGUAGE, StringUtils.toLanguageTag(locale.getLocale()));
}
if (isAcceptGzipEncoding()) {
con.setRequestProperty(HTTP_HEADER_ACCEPT_ENCODING, ENCODING_GZIP);
}
}
/**
* Set the given serialized remote invocation as request body.
* <p>The default implementation simply write the serialized invocation to the
* HttpURLConnection's OutputStream. This can be overridden, for example, to write
* a specific encoding and potentially set appropriate HTTP request headers.
* @param config the HTTP invoker configuration that specifies the target service
* @param con the HttpURLConnection to write the request body to
* @param baos the ByteArrayOutputStream that contains the serialized
* RemoteInvocation object
* @throws IOException if thrown by I/O methods
* @see java.net.HttpURLConnection#getOutputStream()
* @see java.net.HttpURLConnection#setRequestProperty
*/
protected void writeRequestBody(
HttpInvokerClientConfiguration config, HttpURLConnection con, ByteArrayOutputStream baos)
throws IOException {
baos.writeTo(con.getOutputStream());
}
/**
* Validate the given response as contained in the HttpURLConnection object,
* throwing an exception if it does not correspond to a successful HTTP response.
* <p>Default implementation rejects any HTTP status code beyond 2xx, to avoid
* parsing the response body and trying to deserialize from a corrupted stream.
* @param config the HTTP invoker configuration that specifies the target service
* @param con the HttpURLConnection to validate
* @throws IOException if validation failed
* @see java.net.HttpURLConnection#getResponseCode()
*/
protected void validateResponse(HttpInvokerClientConfiguration config, HttpURLConnection con)
throws IOException {
if (con.getResponseCode() >= 300) {
throw new IOException(
"Did not receive successful HTTP response: status code = " + con.getResponseCode() +
", status message = [" + con.getResponseMessage() + "]");
}
}
/**
* Extract the response body from the given executed remote invocation
* request.
* <p>The default implementation simply reads the serialized invocation
* from the HttpURLConnection's InputStream. If the response is recognized
* as GZIP response, the InputStream will get wrapped in a GZIPInputStream.
* @param config the HTTP invoker configuration that specifies the target service
* @param con the HttpURLConnection to read the response body from
* @return an InputStream for the response body
* @throws IOException if thrown by I/O methods
* @see #isGzipResponse
* @see java.util.zip.GZIPInputStream
* @see java.net.HttpURLConnection#getInputStream()
* @see java.net.HttpURLConnection#getHeaderField(int)
* @see java.net.HttpURLConnection#getHeaderFieldKey(int)
*/
protected InputStream readResponseBody(HttpInvokerClientConfiguration config, HttpURLConnection con)
throws IOException {
if (isGzipResponse(con)) {
// GZIP response found - need to unzip.
return new GZIPInputStream(con.getInputStream());
}
else {
// Plain response found.
return con.getInputStream();
}
}
/**
* Determine whether the given response is a GZIP response.
* <p>Default implementation checks whether the HTTP "Content-Encoding"
* header contains "gzip" (in any casing).
* @param con the HttpURLConnection to check
*/
protected boolean isGzipResponse(HttpURLConnection con) {
String encodingHeader = con.getHeaderField(HTTP_HEADER_CONTENT_ENCODING);
return (encodingHeader != null && encodingHeader.toLowerCase().indexOf(ENCODING_GZIP) != -1);
}
}

View File

@ -0,0 +1,177 @@
/*
* 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.remoting.httpinvoker;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import org.springframework.remoting.rmi.RemoteInvocationSerializingExporter;
import org.springframework.remoting.support.RemoteInvocation;
import org.springframework.remoting.support.RemoteInvocationResult;
/**
* HTTP request handler that exports the specified service bean as
* HTTP invoker service endpoint, accessible via an HTTP invoker proxy.
* Designed for Sun's JRE 1.6 HTTP server, implementing the
* {@link com.sun.net.httpserver.HttpHandler} interface.
*
* <p>Deserializes remote invocation objects and serializes remote invocation
* result objects. Uses Java serialization just like RMI, but provides the
* same ease of setup as Caucho's HTTP-based Hessian and Burlap protocols.
*
* <p><b>HTTP invoker is the recommended protocol for Java-to-Java remoting.</b>
* It is more powerful and more extensible than Hessian and Burlap, at the
* expense of being tied to Java. Nevertheless, it is as easy to set up as
* Hessian and Burlap, which is its main advantage compared to RMI.
*
* @author Juergen Hoeller
* @since 2.5.1
* @see org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor
* @see org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean
* @see org.springframework.remoting.caucho.SimpleHessianServiceExporter
* @see org.springframework.remoting.caucho.SimpleBurlapServiceExporter
*/
public class SimpleHttpInvokerServiceExporter extends RemoteInvocationSerializingExporter
implements HttpHandler {
/**
* Reads a remote invocation from the request, executes it,
* and writes the remote invocation result to the response.
* @see #readRemoteInvocation(com.sun.net.httpserver.HttpExchange)
* @see #invokeAndCreateResult(org.springframework.remoting.support.RemoteInvocation, Object)
* @see #writeRemoteInvocationResult(com.sun.net.httpserver.HttpExchange, org.springframework.remoting.support.RemoteInvocationResult)
*/
public void handle(HttpExchange exchange) throws IOException {
try {
RemoteInvocation invocation = readRemoteInvocation(exchange);
RemoteInvocationResult result = invokeAndCreateResult(invocation, getProxy());
writeRemoteInvocationResult(exchange, result);
exchange.close();
}
catch (ClassNotFoundException ex) {
throw new IOException("Class not found during deserialization", ex);
}
}
/**
* Read a RemoteInvocation from the given HTTP request.
* <p>Delegates to
* {@link #readRemoteInvocation(com.sun.net.httpserver.HttpExchange, java.io.InputStream)}
* with the
* {@link com.sun.net.httpserver.HttpExchange#getRequestBody()} request's input stream}.
* @param exchange current HTTP request/response
* @return the RemoteInvocation object
* @throws java.io.IOException in case of I/O failure
* @throws ClassNotFoundException if thrown by deserialization
*/
protected RemoteInvocation readRemoteInvocation(HttpExchange exchange)
throws IOException, ClassNotFoundException {
return readRemoteInvocation(exchange, exchange.getRequestBody());
}
/**
* Deserialize a RemoteInvocation object from the given InputStream.
* <p>Gives {@link #decorateInputStream} a chance to decorate the stream
* first (for example, for custom encryption or compression). Creates a
* {@link org.springframework.remoting.rmi.CodebaseAwareObjectInputStream}
* and calls {@link #doReadRemoteInvocation} to actually read the object.
* <p>Can be overridden for custom serialization of the invocation.
* @param exchange current HTTP request/response
* @param is the InputStream to read from
* @return the RemoteInvocation object
* @throws java.io.IOException in case of I/O failure
* @throws ClassNotFoundException if thrown during deserialization
*/
protected RemoteInvocation readRemoteInvocation(HttpExchange exchange, InputStream is)
throws IOException, ClassNotFoundException {
ObjectInputStream ois = createObjectInputStream(decorateInputStream(exchange, is));
return doReadRemoteInvocation(ois);
}
/**
* Return the InputStream to use for reading remote invocations,
* potentially decorating the given original InputStream.
* <p>The default implementation returns the given stream as-is.
* Can be overridden, for example, for custom encryption or compression.
* @param exchange current HTTP request/response
* @param is the original InputStream
* @return the potentially decorated InputStream
* @throws java.io.IOException in case of I/O failure
*/
protected InputStream decorateInputStream(HttpExchange exchange, InputStream is) throws IOException {
return is;
}
/**
* Write the given RemoteInvocationResult to the given HTTP response.
* @param exchange current HTTP request/response
* @param result the RemoteInvocationResult object
* @throws java.io.IOException in case of I/O failure
*/
protected void writeRemoteInvocationResult(HttpExchange exchange, RemoteInvocationResult result)
throws IOException {
exchange.getResponseHeaders().set("Content-Type", getContentType());
exchange.sendResponseHeaders(200, 0);
writeRemoteInvocationResult(exchange, result, exchange.getResponseBody());
}
/**
* Serialize the given RemoteInvocation to the given OutputStream.
* <p>The default implementation gives {@link #decorateOutputStream} a chance
* to decorate the stream first (for example, for custom encryption or compression).
* Creates an {@link java.io.ObjectOutputStream} for the final stream and calls
* {@link #doWriteRemoteInvocationResult} to actually write the object.
* <p>Can be overridden for custom serialization of the invocation.
* @param exchange current HTTP request/response
* @param result the RemoteInvocationResult object
* @param os the OutputStream to write to
* @throws java.io.IOException in case of I/O failure
* @see #decorateOutputStream
* @see #doWriteRemoteInvocationResult
*/
protected void writeRemoteInvocationResult(
HttpExchange exchange, RemoteInvocationResult result, OutputStream os) throws IOException {
ObjectOutputStream oos = createObjectOutputStream(decorateOutputStream(exchange, os));
doWriteRemoteInvocationResult(result, oos);
oos.flush();
}
/**
* Return the OutputStream to use for writing remote invocation results,
* potentially decorating the given original OutputStream.
* <p>The default implementation returns the given stream as-is.
* Can be overridden, for example, for custom encryption or compression.
* @param exchange current HTTP request/response
* @param os the original OutputStream
* @return the potentially decorated OutputStream
* @throws java.io.IOException in case of I/O failure
*/
protected OutputStream decorateOutputStream(HttpExchange exchange, OutputStream os) throws IOException {
return os;
}
}

View File

@ -0,0 +1,14 @@
<html>
<body>
Remoting classes for transparent Java-to-Java remoting via HTTP invokers.
Uses Java serialization just like RMI, but provides the same ease of setup
as Caucho's HTTP-based Hessian and Burlap protocols.
<p><b>HTTP invoker is the recommended protocol for Java-to-Java remoting.</b>
It is more powerful and more extensible than Hessian and Burlap, at the
expense of being tied to Java. Neverthelesss, it is as easy to set up as
Hessian and Burlap, which is its main advantage compared to RMI.
</body>
</html>

View File

@ -0,0 +1,757 @@
/*
* 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.remoting.jaxrpc;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import javax.xml.namespace.QName;
import javax.xml.rpc.Call;
import javax.xml.rpc.JAXRPCException;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceException;
import javax.xml.rpc.Stub;
import javax.xml.rpc.soap.SOAPFaultException;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.remoting.RemoteLookupFailureException;
import org.springframework.remoting.RemoteProxyFailureException;
import org.springframework.remoting.rmi.RmiClientInterceptorUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
/**
* {@link org.aopalliance.intercept.MethodInterceptor} for accessing a specific port
* of a JAX-RPC service. Uses either {@link LocalJaxRpcServiceFactory}'s facilities
* underneath or takes an explicit reference to an existing JAX-RPC Service instance
* (e.g. obtained via a {@link org.springframework.jndi.JndiObjectFactoryBean}).
*
* <p>Allows to set JAX-RPC's standard stub properties directly, via the
* "username", "password", "endpointAddress" and "maintainSession" properties.
* For typical usage, it is not necessary to specify those.
*
* <p>In standard JAX-RPC style, this invoker is used with an RMI service interface.
* Alternatively, this invoker can also proxy a JAX-RPC service with a matching
* non-RMI business interface, that is, an interface that declares the service methods
* without RemoteExceptions. In the latter case, RemoteExceptions thrown by JAX-RPC
* will automatically get converted to Spring's unchecked RemoteAccessException.
*
* <p>Setting "serviceInterface" is usually sufficient: The invoker will automatically
* use JAX-RPC "dynamic invocations" via the Call API in this case, no matter whether
* the specified interface is an RMI or non-RMI interface. Alternatively, a corresponding
* JAX-RPC port interface can be specified as "portInterface", which will turn this
* invoker into "static invocation" mode (operating on a standard JAX-RPC port stub).
*
* @author Juergen Hoeller
* @since 15.12.2003
* @see #setPortName
* @see #setServiceInterface
* @see #setPortInterface
* @see javax.xml.rpc.Service#createCall
* @see javax.xml.rpc.Service#getPort
* @see org.springframework.remoting.RemoteAccessException
* @see org.springframework.jndi.JndiObjectFactoryBean
*/
public class JaxRpcPortClientInterceptor extends LocalJaxRpcServiceFactory
implements MethodInterceptor, InitializingBean {
private Service jaxRpcService;
private Service serviceToUse;
private String portName;
private String username;
private String password;
private String endpointAddress;
private boolean maintainSession;
/** Map of custom properties, keyed by property name (String) */
private final Map customPropertyMap = new HashMap();
private Class serviceInterface;
private Class portInterface;
private boolean lookupServiceOnStartup = true;
private boolean refreshServiceAfterConnectFailure = false;
private QName portQName;
private Remote portStub;
private final Object preparationMonitor = new Object();
/**
* Set a reference to an existing JAX-RPC Service instance,
* for example obtained via {@link org.springframework.jndi.JndiObjectFactoryBean}.
* If not set, {@link LocalJaxRpcServiceFactory}'s properties have to be specified.
* @see #setServiceFactoryClass
* @see #setWsdlDocumentUrl
* @see #setNamespaceUri
* @see #setServiceName
* @see org.springframework.jndi.JndiObjectFactoryBean
*/
public void setJaxRpcService(Service jaxRpcService) {
this.jaxRpcService = jaxRpcService;
}
/**
* Return a reference to an existing JAX-RPC Service instance, if any.
*/
public Service getJaxRpcService() {
return this.jaxRpcService;
}
/**
* Set the name of the port.
* Corresponds to the "wsdl:port" name.
*/
public void setPortName(String portName) {
this.portName = portName;
}
/**
* Return the name of the port.
*/
public String getPortName() {
return this.portName;
}
/**
* Set the username to specify on the stub or call.
* @see javax.xml.rpc.Stub#USERNAME_PROPERTY
* @see javax.xml.rpc.Call#USERNAME_PROPERTY
*/
public void setUsername(String username) {
this.username = username;
}
/**
* Return the username to specify on the stub or call.
*/
public String getUsername() {
return this.username;
}
/**
* Set the password to specify on the stub or call.
* @see javax.xml.rpc.Stub#PASSWORD_PROPERTY
* @see javax.xml.rpc.Call#PASSWORD_PROPERTY
*/
public void setPassword(String password) {
this.password = password;
}
/**
* Return the password to specify on the stub or call.
*/
public String getPassword() {
return this.password;
}
/**
* Set the endpoint address to specify on the stub or call.
* @see javax.xml.rpc.Stub#ENDPOINT_ADDRESS_PROPERTY
* @see javax.xml.rpc.Call#setTargetEndpointAddress
*/
public void setEndpointAddress(String endpointAddress) {
this.endpointAddress = endpointAddress;
}
/**
* Return the endpoint address to specify on the stub or call.
*/
public String getEndpointAddress() {
return this.endpointAddress;
}
/**
* Set the maintain session flag to specify on the stub or call.
* @see javax.xml.rpc.Stub#SESSION_MAINTAIN_PROPERTY
* @see javax.xml.rpc.Call#SESSION_MAINTAIN_PROPERTY
*/
public void setMaintainSession(boolean maintainSession) {
this.maintainSession = maintainSession;
}
/**
* Return the maintain session flag to specify on the stub or call.
*/
public boolean isMaintainSession() {
return this.maintainSession;
}
/**
* Set custom properties to be set on the stub or call.
* <p>Can be populated with a String "value" (parsed via PropertiesEditor)
* or a "props" element in XML bean definitions.
* @see javax.xml.rpc.Stub#_setProperty
* @see javax.xml.rpc.Call#setProperty
*/
public void setCustomProperties(Properties customProperties) {
CollectionUtils.mergePropertiesIntoMap(customProperties, this.customPropertyMap);
}
/**
* Set custom properties to be set on the stub or call.
* <p>Can be populated with a "map" or "props" element in XML bean definitions.
* @see javax.xml.rpc.Stub#_setProperty
* @see javax.xml.rpc.Call#setProperty
*/
public void setCustomPropertyMap(Map customProperties) {
if (customProperties != null) {
Iterator it = customProperties.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
if (!(entry.getKey() instanceof String)) {
throw new IllegalArgumentException(
"Illegal property key [" + entry.getKey() + "]: only Strings allowed");
}
addCustomProperty((String) entry.getKey(), entry.getValue());
}
}
}
/**
* Allow Map access to the custom properties to be set on the stub
* or call, with the option to add or override specific entries.
* <p>Useful for specifying entries directly, for example via
* "customPropertyMap[myKey]". This is particularly useful for
* adding or overriding entries in child bean definitions.
*/
public Map getCustomPropertyMap() {
return this.customPropertyMap;
}
/**
* Add a custom property to this JAX-RPC Stub/Call.
* @param name the name of the attribute to expose
* @param value the attribute value to expose
* @see javax.xml.rpc.Stub#_setProperty
* @see javax.xml.rpc.Call#setProperty
*/
public void addCustomProperty(String name, Object value) {
this.customPropertyMap.put(name, value);
}
/**
* Set the interface of the service that this factory should create a proxy for.
* This will typically be a non-RMI business interface, although you can also
* use an RMI port interface as recommended by JAX-RPC here.
* <p>Calls on the specified service interface will either be translated to the
* underlying RMI port interface (in case of a "portInterface" being specified)
* or to dynamic calls (using the JAX-RPC Dynamic Invocation Interface).
* <p>The dynamic call mechanism has the advantage that you don't need to
* maintain an RMI port interface in addition to an existing non-RMI business
* interface. In terms of configuration, specifying the business interface
* as "serviceInterface" will be enough; this interceptor will automatically
* use dynamic calls in such a scenario.
* @see javax.xml.rpc.Service#createCall
* @see #setPortInterface
*/
public void setServiceInterface(Class serviceInterface) {
if (serviceInterface != null && !serviceInterface.isInterface()) {
throw new IllegalArgumentException("'serviceInterface' must be an interface");
}
this.serviceInterface = serviceInterface;
}
/**
* Return the interface of the service that this factory should create a proxy for.
*/
public Class getServiceInterface() {
return this.serviceInterface;
}
/**
* Set the JAX-RPC port interface to use. Only needs to be set if a JAX-RPC
* port stub should be used instead of the dynamic call mechanism.
* See the javadoc of the "serviceInterface" property for more details.
* <p>The interface must be suitable for a JAX-RPC port, that is, it must be
* an RMI service interface (that extends <code>java.rmi.Remote</code>).
* <p><b>NOTE:</b> Check whether your JAX-RPC provider returns thread-safe
* port stubs. If not, use the dynamic call mechanism instead, which will
* always be thread-safe. In particular, do not use JAX-RPC port stubs
* with Apache Axis, whose port stubs are known to be non-thread-safe.
* @see javax.xml.rpc.Service#getPort
* @see java.rmi.Remote
* @see #setServiceInterface
*/
public void setPortInterface(Class portInterface) {
if (portInterface != null &&
(!portInterface.isInterface() || !Remote.class.isAssignableFrom(portInterface))) {
throw new IllegalArgumentException(
"'portInterface' must be an interface derived from [java.rmi.Remote]");
}
this.portInterface = portInterface;
}
/**
* Return the JAX-RPC port interface to use.
*/
public Class getPortInterface() {
return this.portInterface;
}
/**
* Set whether to look up the JAX-RPC service on startup.
* <p>Default is "true". Turn this flag off to allow for late start
* of the target server. In this case, the JAX-RPC service will be
* lazily fetched on first access.
*/
public void setLookupServiceOnStartup(boolean lookupServiceOnStartup) {
this.lookupServiceOnStartup = lookupServiceOnStartup;
}
/**
* Set whether to refresh the JAX-RPC service on connect failure,
* that is, whenever a JAX-RPC invocation throws a RemoteException.
* <p>Default is "false", keeping a reference to the JAX-RPC service
* in any case, retrying the next invocation on the same service
* even in case of failure. Turn this flag on to reinitialize the
* entire service in case of connect failures.
*/
public void setRefreshServiceAfterConnectFailure(boolean refreshServiceAfterConnectFailure) {
this.refreshServiceAfterConnectFailure = refreshServiceAfterConnectFailure;
}
/**
* Prepares the JAX-RPC service and port if the "lookupServiceOnStartup"
* is turned on (which it is by default).
*/
public void afterPropertiesSet() {
if (this.lookupServiceOnStartup) {
prepare();
}
}
/**
* Create and initialize the JAX-RPC service for the specified port.
* <p>Prepares a JAX-RPC stub if possible (if an RMI interface is available);
* falls back to JAX-RPC dynamic calls else. Using dynamic calls can be enforced
* through overriding {@link #alwaysUseJaxRpcCall} to return <code>true</code>.
* <p>{@link #postProcessJaxRpcService} and {@link #postProcessPortStub}
* hooks are available for customization in subclasses. When using dynamic calls,
* each can be post-processed via {@link #postProcessJaxRpcCall}.
* @throws RemoteLookupFailureException if service initialization or port stub creation failed
*/
public void prepare() throws RemoteLookupFailureException {
if (getPortName() == null) {
throw new IllegalArgumentException("Property 'portName' is required");
}
synchronized (this.preparationMonitor) {
this.serviceToUse = null;
// Cache the QName for the port.
this.portQName = getQName(getPortName());
try {
Service service = getJaxRpcService();
if (service == null) {
service = createJaxRpcService();
}
else {
postProcessJaxRpcService(service);
}
Class portInterface = getPortInterface();
if (portInterface != null && !alwaysUseJaxRpcCall()) {
// JAX-RPC-compliant port interface -> using JAX-RPC stub for port.
if (logger.isDebugEnabled()) {
logger.debug("Creating JAX-RPC proxy for JAX-RPC port [" + this.portQName +
"], using port interface [" + portInterface.getName() + "]");
}
Remote remoteObj = service.getPort(this.portQName, portInterface);
if (logger.isDebugEnabled()) {
Class serviceInterface = getServiceInterface();
if (serviceInterface != null) {
boolean isImpl = serviceInterface.isInstance(remoteObj);
logger.debug("Using service interface [" + serviceInterface.getName() + "] for JAX-RPC port [" +
this.portQName + "] - " + (!isImpl ? "not" : "") + " directly implemented");
}
}
if (!(remoteObj instanceof Stub)) {
throw new RemoteLookupFailureException("Port stub of class [" + remoteObj.getClass().getName() +
"] is not a valid JAX-RPC stub: it does not implement interface [javax.xml.rpc.Stub]");
}
Stub stub = (Stub) remoteObj;
// Apply properties to JAX-RPC stub.
preparePortStub(stub);
// Allow for custom post-processing in subclasses.
postProcessPortStub(stub);
this.portStub = remoteObj;
}
else {
// No JAX-RPC-compliant port interface -> using JAX-RPC dynamic calls.
if (logger.isDebugEnabled()) {
logger.debug("Using JAX-RPC dynamic calls for JAX-RPC port [" + this.portQName + "]");
}
}
this.serviceToUse = service;
}
catch (ServiceException ex) {
throw new RemoteLookupFailureException(
"Failed to initialize service for JAX-RPC port [" + this.portQName + "]", ex);
}
}
}
/**
* Return whether to always use JAX-RPC dynamic calls.
* Called by <code>afterPropertiesSet</code>.
* <p>Default is "false"; if an RMI interface is specified as "portInterface"
* or "serviceInterface", it will be used to create a JAX-RPC port stub.
* <p>Can be overridden to enforce the use of the JAX-RPC Call API,
* for example if there is a need to customize at the Call level.
* This just necessary if you you want to use an RMI interface as
* "serviceInterface", though; in case of only a non-RMI interface being
* available, this interceptor will fall back to the Call API anyway.
* @see #postProcessJaxRpcCall
*/
protected boolean alwaysUseJaxRpcCall() {
return false;
}
/**
* Reset the prepared service of this interceptor,
* allowing for reinitialization on next access.
*/
protected void reset() {
synchronized (this.preparationMonitor) {
this.serviceToUse = null;
}
}
/**
* Return whether this client interceptor has already been prepared,
* i.e. has already looked up the JAX-RPC service and port.
*/
protected boolean isPrepared() {
synchronized (this.preparationMonitor) {
return (this.serviceToUse != null);
}
}
/**
* Return the prepared QName for the port.
* @see #setPortName
* @see #getQName
*/
protected final QName getPortQName() {
return this.portQName;
}
/**
* Prepare the given JAX-RPC port stub, applying properties to it.
* Called by {@link #prepare}.
* <p>Just applied when actually creating a JAX-RPC port stub, in case of a
* compliant port interface. Else, JAX-RPC dynamic calls will be used.
* @param stub the current JAX-RPC port stub
* @see #setUsername
* @see #setPassword
* @see #setEndpointAddress
* @see #setMaintainSession
* @see #setCustomProperties
* @see #setPortInterface
* @see #prepareJaxRpcCall
*/
protected void preparePortStub(Stub stub) {
String username = getUsername();
if (username != null) {
stub._setProperty(Stub.USERNAME_PROPERTY, username);
}
String password = getPassword();
if (password != null) {
stub._setProperty(Stub.PASSWORD_PROPERTY, password);
}
String endpointAddress = getEndpointAddress();
if (endpointAddress != null) {
stub._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY, endpointAddress);
}
if (isMaintainSession()) {
stub._setProperty(Stub.SESSION_MAINTAIN_PROPERTY, Boolean.TRUE);
}
if (this.customPropertyMap != null) {
for (Iterator it = this.customPropertyMap.keySet().iterator(); it.hasNext();) {
String key = (String) it.next();
stub._setProperty(key, this.customPropertyMap.get(key));
}
}
}
/**
* Post-process the given JAX-RPC port stub. Called by {@link #prepare}.
* <p>The default implementation is empty.
* <p>Just applied when actually creating a JAX-RPC port stub, in case of a
* compliant port interface. Else, JAX-RPC dynamic calls will be used.
* @param stub the current JAX-RPC port stub
* (can be cast to an implementation-specific class if necessary)
* @see #setPortInterface
* @see #postProcessJaxRpcCall
*/
protected void postProcessPortStub(Stub stub) {
}
/**
* Return the underlying JAX-RPC port stub that this interceptor delegates to
* for each method invocation on the proxy.
*/
protected Remote getPortStub() {
return this.portStub;
}
/**
* Translates the method invocation into a JAX-RPC service invocation.
* <p>Prepares the service on the fly, if necessary, in case of lazy
* lookup or a connect failure having happened.
* @see #prepare()
* @see #doInvoke
*/
public Object invoke(MethodInvocation invocation) throws Throwable {
if (AopUtils.isToStringMethod(invocation.getMethod())) {
return "JAX-RPC proxy for port [" + getPortName() + "] of service [" + getServiceName() + "]";
}
// Lazily prepare service and stub if necessary.
synchronized (this.preparationMonitor) {
if (!isPrepared()) {
prepare();
}
}
return doInvoke(invocation);
}
/**
* Perform a JAX-RPC service invocation based on the given method invocation.
* <p>Uses traditional RMI stub invocation if a JAX-RPC port stub is available;
* falls back to JAX-RPC dynamic calls else.
* @param invocation the AOP method invocation
* @return the invocation result, if any
* @throws Throwable in case of invocation failure
* @see #getPortStub()
* @see #doInvoke(org.aopalliance.intercept.MethodInvocation, java.rmi.Remote)
* @see #performJaxRpcCall(org.aopalliance.intercept.MethodInvocation, javax.xml.rpc.Service)
*/
protected Object doInvoke(MethodInvocation invocation) throws Throwable {
Remote stub = getPortStub();
try {
if (stub != null) {
// JAX-RPC port stub available -> traditional RMI stub invocation.
if (logger.isTraceEnabled()) {
logger.trace("Invoking operation '" + invocation.getMethod().getName() + "' on JAX-RPC port stub");
}
return doInvoke(invocation, stub);
}
else {
// No JAX-RPC stub -> using JAX-RPC dynamic calls.
if (logger.isTraceEnabled()) {
logger.trace("Invoking operation '" + invocation.getMethod().getName() + "' as JAX-RPC dynamic call");
}
return performJaxRpcCall(invocation, this.serviceToUse);
}
}
catch (RemoteException ex) {
throw handleRemoteException(invocation.getMethod(), ex);
}
catch (SOAPFaultException ex) {
throw new JaxRpcSoapFaultException(ex);
}
catch (JAXRPCException ex) {
throw new RemoteProxyFailureException("Invalid JAX-RPC call configuration", ex);
}
}
/**
* Perform a JAX-RPC service invocation on the given port stub.
* @param invocation the AOP method invocation
* @param portStub the RMI port stub to invoke
* @return the invocation result, if any
* @throws Throwable in case of invocation failure
* @see #getPortStub()
* @see #doInvoke(org.aopalliance.intercept.MethodInvocation, java.rmi.Remote)
* @see #performJaxRpcCall
*/
protected Object doInvoke(MethodInvocation invocation, Remote portStub) throws Throwable {
try {
return RmiClientInterceptorUtils.invokeRemoteMethod(invocation, portStub);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
/**
* Perform a JAX-RPC dynamic call for the given AOP method invocation.
* Delegates to {@link #prepareJaxRpcCall} and
* {@link #postProcessJaxRpcCall} for setting up the call object.
* <p>The default implementation uses method name as JAX-RPC operation name
* and method arguments as arguments for the JAX-RPC call. Can be
* overridden in subclasses for custom operation names and/or arguments.
* @param invocation the current AOP MethodInvocation that should
* be converted to a JAX-RPC call
* @param service the JAX-RPC Service to use for the call
* @return the return value of the invocation, if any
* @throws Throwable the exception thrown by the invocation, if any
* @see #prepareJaxRpcCall
* @see #postProcessJaxRpcCall
*/
protected Object performJaxRpcCall(MethodInvocation invocation, Service service) throws Throwable {
Method method = invocation.getMethod();
QName portQName = this.portQName;
// Create JAX-RPC call object, using the method name as operation name.
// Synchronized because of non-thread-safe Axis implementation!
Call call = null;
synchronized (service) {
call = service.createCall(portQName, method.getName());
}
// Apply properties to JAX-RPC stub.
prepareJaxRpcCall(call);
// Allow for custom post-processing in subclasses.
postProcessJaxRpcCall(call, invocation);
// Perform actual invocation.
return call.invoke(invocation.getArguments());
}
/**
* Prepare the given JAX-RPC call, applying properties to it. Called by {@link #invoke}.
* <p>Just applied when actually using JAX-RPC dynamic calls, i.e. if no compliant
* port interface was specified. Else, a JAX-RPC port stub will be used.
* @param call the current JAX-RPC call object
* @see #setUsername
* @see #setPassword
* @see #setEndpointAddress
* @see #setMaintainSession
* @see #setCustomProperties
* @see #setPortInterface
* @see #preparePortStub
*/
protected void prepareJaxRpcCall(Call call) {
String username = getUsername();
if (username != null) {
call.setProperty(Call.USERNAME_PROPERTY, username);
}
String password = getPassword();
if (password != null) {
call.setProperty(Call.PASSWORD_PROPERTY, password);
}
String endpointAddress = getEndpointAddress();
if (endpointAddress != null) {
call.setTargetEndpointAddress(endpointAddress);
}
if (isMaintainSession()) {
call.setProperty(Call.SESSION_MAINTAIN_PROPERTY, Boolean.TRUE);
}
if (this.customPropertyMap != null) {
for (Iterator it = this.customPropertyMap.keySet().iterator(); it.hasNext();) {
String key = (String) it.next();
call.setProperty(key, this.customPropertyMap.get(key));
}
}
}
/**
* Post-process the given JAX-RPC call. Called by {@link #invoke}.
* <p>The default implementation is empty.
* <p>Just applied when actually using JAX-RPC dynamic calls, i.e. if no compliant
* port interface was specified. Else, a JAX-RPC port stub will be used.
* @param call the current JAX-RPC call object
* (can be cast to an implementation-specific class if necessary)
* @param invocation the current AOP MethodInvocation that the call was
* created for (can be used to check method name, method parameters
* and/or passed-in arguments)
* @see #setPortInterface
* @see #postProcessPortStub
*/
protected void postProcessJaxRpcCall(Call call, MethodInvocation invocation) {
}
/**
* Handle the given RemoteException that was thrown from a JAX-RPC port stub
* or JAX-RPC call invocation.
* @param method the service interface method that we invoked
* @param ex the original RemoteException
* @return the exception to rethrow (may be the original RemoteException
* or an extracted/wrapped exception, but never <code>null</code>)
*/
protected Throwable handleRemoteException(Method method, RemoteException ex) {
boolean isConnectFailure = isConnectFailure(ex);
if (isConnectFailure && this.refreshServiceAfterConnectFailure) {
reset();
}
Throwable cause = ex.getCause();
if (cause != null && ReflectionUtils.declaresException(method, cause.getClass())) {
if (logger.isDebugEnabled()) {
logger.debug("Rethrowing wrapped exception of type [" + cause.getClass().getName() + "] as-is");
}
// Declared on the service interface: probably a wrapped business exception.
return ex.getCause();
}
else {
// Throw either a RemoteAccessException or the original RemoteException,
// depending on what the service interface declares.
return RmiClientInterceptorUtils.convertRmiAccessException(
method, ex, isConnectFailure, this.portQName.toString());
}
}
/**
* Determine whether the given RMI exception indicates a connect failure.
* <p>The default implementation returns <code>true</code> unless the
* exception class name (or exception superclass name) contains the term
* "Fault" (e.g. "AxisFault"), assuming that the JAX-RPC provider only
* throws RemoteException in case of WSDL faults and connect failures.
* @param ex the RMI exception to check
* @return whether the exception should be treated as connect failure
* @see org.springframework.remoting.rmi.RmiClientInterceptorUtils#isConnectFailure
*/
protected boolean isConnectFailure(RemoteException ex) {
return (ex.getClass().getName().indexOf("Fault") == -1 &&
ex.getClass().getSuperclass().getName().indexOf("Fault") == -1);
}
}

View File

@ -0,0 +1,87 @@
/*
* 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.remoting.jaxrpc;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.util.ClassUtils;
/**
* {@link org.springframework.beans.factory.FactoryBean} for a specific port of a
* JAX-RPC service. Exposes a proxy for the port, to be used for bean references.
* Inherits configuration properties from {@link JaxRpcPortClientInterceptor}.
*
* <p>This factory is typically used with an RMI service interface. Alternatively,
* this factory can also proxy a JAX-RPC service with a matching non-RMI business
* interface, i.e. an interface that mirrors the RMI service methods but does not
* declare RemoteExceptions. In the latter case, RemoteExceptions thrown by the
* JAX-RPC stub will automatically get converted to Spring's unchecked
* RemoteAccessException.
*
* <p>If exposing the JAX-RPC port interface (i.e. an RMI interface) directly,
* setting "serviceInterface" is sufficient. If exposing a non-RMI business
* interface, the business interface needs to be set as "serviceInterface",
* and the JAX-RPC port interface as "portInterface".
*
* @author Juergen Hoeller
* @since 15.12.2003
* @see #setServiceInterface
* @see #setPortInterface
* @see LocalJaxRpcServiceFactoryBean
*/
public class JaxRpcPortProxyFactoryBean extends JaxRpcPortClientInterceptor
implements FactoryBean, BeanClassLoaderAware {
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
private Object serviceProxy;
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
public void afterPropertiesSet() {
if (getServiceInterface() == null) {
// Use JAX-RPC port interface (a traditional RMI interface)
// as service interface if none explicitly specified.
if (getPortInterface() != null) {
setServiceInterface(getPortInterface());
}
else {
throw new IllegalArgumentException("Property 'serviceInterface' is required");
}
}
super.afterPropertiesSet();
this.serviceProxy = new ProxyFactory(getServiceInterface(), this).getProxy(this.beanClassLoader);
}
public Object getObject() {
return this.serviceProxy;
}
public Class getObjectType() {
return getServiceInterface();
}
public boolean isSingleton() {
return true;
}
}

View File

@ -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.remoting.jaxrpc;
import javax.xml.rpc.Service;
/**
* Callback interface for post-processing a JAX-RPC Service.
*
* <p>Implementations can be registered with {@link LocalJaxRpcServiceFactory}
* or one of its subclasses: {@link LocalJaxRpcServiceFactoryBean},
* {@link JaxRpcPortClientInterceptor}, or {@link JaxRpcPortProxyFactoryBean}.
*
* <p>Useful, for example, to register custom type mappings. See the
* {@link org.springframework.remoting.jaxrpc.support.AxisBeanMappingServicePostProcessor}
* class that registers Axis-specific bean mappings for specified bean classes.
* This is defined for the domain objects in the JPetStore same application,
* for example.
*
* @author Juergen Hoeller
* @since 1.1.4
* @see LocalJaxRpcServiceFactory#setServicePostProcessors
* @see LocalJaxRpcServiceFactoryBean#setServicePostProcessors
* @see JaxRpcPortClientInterceptor#setServicePostProcessors
* @see JaxRpcPortProxyFactoryBean#setServicePostProcessors
* @see javax.xml.rpc.Service#getTypeMappingRegistry
*/
public interface JaxRpcServicePostProcessor {
/**
* Post-process the given JAX-RPC {@link Service}.
* @param service the current JAX-RPC <code>Service</code>
* (can be cast to an implementation-specific class if necessary)
*/
void postProcessJaxRpcService(Service service);
}

View File

@ -0,0 +1,65 @@
/*
* 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.remoting.jaxrpc;
import javax.xml.namespace.QName;
import javax.xml.rpc.soap.SOAPFaultException;
import org.springframework.remoting.soap.SoapFaultException;
/**
* Spring SoapFaultException adapter for the JAX-RPC
* {@link javax.xml.rpc.soap.SOAPFaultException} class.
*
* @author Juergen Hoeller
* @since 2.5
*/
public class JaxRpcSoapFaultException extends SoapFaultException {
/**
* Constructor for JaxRpcSoapFaultException.
* @param original the original JAX-RPC SOAPFaultException to wrap
*/
public JaxRpcSoapFaultException(SOAPFaultException original) {
super(original.getMessage(), original);
}
/**
* Return the wrapped JAX-RPC SOAPFaultException.
*/
public final SOAPFaultException getOriginalException() {
return (SOAPFaultException) getCause();
}
public String getFaultCode() {
return getOriginalException().getFaultCode().toString();
}
public QName getFaultCodeAsQName() {
return getOriginalException().getFaultCode();
}
public String getFaultString() {
return getOriginalException().getFaultString();
}
public String getFaultActor() {
return getOriginalException().getFaultActor();
}
}

View File

@ -0,0 +1,321 @@
/*
* 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.remoting.jaxrpc;
import java.net.URL;
import java.util.Properties;
import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceException;
import javax.xml.rpc.ServiceFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
/**
* Factory for locally defined JAX-RPC {@link javax.xml.rpc.Service} references.
* Uses a JAX-RPC {@link javax.xml.rpc.ServiceFactory} underneath.
*
* <p>Serves as base class for {@link LocalJaxRpcServiceFactoryBean} as well as
* {@link JaxRpcPortClientInterceptor} and {@link JaxRpcPortProxyFactoryBean}.
*
* @author Juergen Hoeller
* @since 15.12.2003
* @see javax.xml.rpc.ServiceFactory
* @see javax.xml.rpc.Service
* @see LocalJaxRpcServiceFactoryBean
* @see JaxRpcPortClientInterceptor
* @see JaxRpcPortProxyFactoryBean
*/
public class LocalJaxRpcServiceFactory {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
private ServiceFactory serviceFactory;
private Class serviceFactoryClass;
private URL wsdlDocumentUrl;
private String namespaceUri;
private String serviceName;
private Class jaxRpcServiceInterface;
private Properties jaxRpcServiceProperties;
private JaxRpcServicePostProcessor[] servicePostProcessors;
/**
* Set the ServiceFactory instance to use.
* <p>This is an alternative to the common "serviceFactoryClass" property,
* allowing for a pre-initialized ServiceFactory instance to be specified.
* @see #setServiceFactoryClass
*/
public void setServiceFactory(ServiceFactory serviceFactory) {
this.serviceFactory = serviceFactory;
}
/**
* Return the specified ServiceFactory instance, if any.
*/
public ServiceFactory getServiceFactory() {
return this.serviceFactory;
}
/**
* Set the ServiceFactory class to use, for example
* "org.apache.axis.client.ServiceFactory".
* <p>Does not need to be set if the JAX-RPC implementation has registered
* itself with the JAX-RPC system property "SERVICEFACTORY_PROPERTY".
* @see javax.xml.rpc.ServiceFactory
*/
public void setServiceFactoryClass(Class serviceFactoryClass) {
if (serviceFactoryClass != null && !ServiceFactory.class.isAssignableFrom(serviceFactoryClass)) {
throw new IllegalArgumentException("'serviceFactoryClass' must implement [javax.xml.rpc.ServiceFactory]");
}
this.serviceFactoryClass = serviceFactoryClass;
}
/**
* Return the ServiceFactory class to use, or <code>null</code> if default.
*/
public Class getServiceFactoryClass() {
return this.serviceFactoryClass;
}
/**
* Set the URL of the WSDL document that describes the service.
*/
public void setWsdlDocumentUrl(URL wsdlDocumentUrl) {
this.wsdlDocumentUrl = wsdlDocumentUrl;
}
/**
* Return the URL of the WSDL document that describes the service.
*/
public URL getWsdlDocumentUrl() {
return this.wsdlDocumentUrl;
}
/**
* Set the namespace URI of the service.
* Corresponds to the WSDL "targetNamespace".
*/
public void setNamespaceUri(String namespaceUri) {
this.namespaceUri = (namespaceUri != null ? namespaceUri.trim() : null);
}
/**
* Return the namespace URI of the service.
*/
public String getNamespaceUri() {
return this.namespaceUri;
}
/**
* Set the name of the service to look up.
* Corresponds to the "wsdl:service" name.
* @see javax.xml.rpc.ServiceFactory#createService(javax.xml.namespace.QName)
* @see javax.xml.rpc.ServiceFactory#createService(java.net.URL, javax.xml.namespace.QName)
* @see javax.xml.rpc.ServiceFactory#loadService(java.net.URL, javax.xml.namespace.QName, java.util.Properties)
*/
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
/**
* Return the name of the service.
*/
public String getServiceName() {
return this.serviceName;
}
/**
* Set the JAX-RPC service interface to use for looking up the service.
* If specified, this will override a "serviceName" setting.
* <p>The specified interface will usually be a generated JAX-RPC service
* interface that directly corresponds to the WSDL service declaration.
* Note that this is not a port interface or the application-level service
* interface to be exposed by a port proxy!
* <p>Only supported by JAX-RPC 1.1 providers.
* @see #setServiceName
* @see javax.xml.rpc.ServiceFactory#loadService(Class)
* @see javax.xml.rpc.ServiceFactory#loadService(java.net.URL, Class, java.util.Properties)
*/
public void setJaxRpcServiceInterface(Class jaxRpcServiceInterface) {
this.jaxRpcServiceInterface = jaxRpcServiceInterface;
}
/**
* Return the JAX-RPC service interface to use for looking up the service.
*/
public Class getJaxRpcServiceInterface() {
return this.jaxRpcServiceInterface;
}
/**
* Set JAX-RPC service properties to be passed to the ServiceFactory, if any.
* <p>Only supported by JAX-RPC 1.1 providers.
* @see javax.xml.rpc.ServiceFactory#loadService(java.net.URL, javax.xml.namespace.QName, java.util.Properties)
* @see javax.xml.rpc.ServiceFactory#loadService(java.net.URL, Class, java.util.Properties)
*/
public void setJaxRpcServiceProperties(Properties jaxRpcServiceProperties) {
this.jaxRpcServiceProperties = jaxRpcServiceProperties;
}
/**
* Return JAX-RPC service properties to be passed to the ServiceFactory, if any.
*/
public Properties getJaxRpcServiceProperties() {
return this.jaxRpcServiceProperties;
}
/**
* Set the JaxRpcServicePostProcessors to be applied to JAX-RPC Service
* instances created by this factory.
* <p>Such post-processors can, for example, register custom type mappings.
* They are reusable across all pre-built subclasses of this factory:
* LocalJaxRpcServiceFactoryBean, JaxRpcPortClientInterceptor,
* JaxRpcPortProxyFactoryBean.
* @see LocalJaxRpcServiceFactoryBean
* @see JaxRpcPortClientInterceptor
* @see JaxRpcPortProxyFactoryBean
*/
public void setServicePostProcessors(JaxRpcServicePostProcessor[] servicePostProcessors) {
this.servicePostProcessors = servicePostProcessors;
}
/**
* Return the JaxRpcServicePostProcessors to be applied to JAX-RPC Service
* instances created by this factory.
*/
public JaxRpcServicePostProcessor[] getServicePostProcessors() {
return this.servicePostProcessors;
}
/**
* Create a JAX-RPC Service according to the parameters of this factory.
* @see #setServiceName
* @see #setWsdlDocumentUrl
* @see #postProcessJaxRpcService
*/
public Service createJaxRpcService() throws ServiceException {
ServiceFactory serviceFactory = getServiceFactory();
if (serviceFactory == null) {
serviceFactory = createServiceFactory();
}
// Create service based on this factory's settings.
Service service = createService(serviceFactory);
// Allow for custom post-processing in subclasses.
postProcessJaxRpcService(service);
return service;
}
/**
* Return a QName for the given name, relative to the namespace URI
* of this factory, if given.
* @see #setNamespaceUri
*/
protected QName getQName(String name) {
return (getNamespaceUri() != null ? new QName(getNamespaceUri(), name) : new QName(name));
}
/**
* Create a JAX-RPC ServiceFactory, either of the specified class
* or the default.
* @throws ServiceException if thrown by JAX-RPC methods
* @see #setServiceFactoryClass
* @see javax.xml.rpc.ServiceFactory#newInstance()
*/
protected ServiceFactory createServiceFactory() throws ServiceException {
if (getServiceFactoryClass() != null) {
return (ServiceFactory) BeanUtils.instantiateClass(getServiceFactoryClass());
}
else {
return ServiceFactory.newInstance();
}
}
/**
* Actually create the JAX-RPC Service instance,
* based on this factory's settings.
* @param serviceFactory the JAX-RPC ServiceFactory to use
* @return the newly created JAX-RPC Service
* @throws ServiceException if thrown by JAX-RPC methods
* @see javax.xml.rpc.ServiceFactory#createService
* @see javax.xml.rpc.ServiceFactory#loadService
*/
protected Service createService(ServiceFactory serviceFactory) throws ServiceException {
if (getServiceName() == null && getJaxRpcServiceInterface() == null) {
throw new IllegalArgumentException("Either 'serviceName' or 'jaxRpcServiceInterface' is required");
}
if (getJaxRpcServiceInterface() != null) {
// Create service via generated JAX-RPC service interface.
// Only supported on JAX-RPC 1.1
if (getWsdlDocumentUrl() != null || getJaxRpcServiceProperties() != null) {
return serviceFactory.loadService(
getWsdlDocumentUrl(), getJaxRpcServiceInterface(), getJaxRpcServiceProperties());
}
return serviceFactory.loadService(getJaxRpcServiceInterface());
}
// Create service via specified JAX-RPC service name.
QName serviceQName = getQName(getServiceName());
if (getJaxRpcServiceProperties() != null) {
// Only supported on JAX-RPC 1.1
return serviceFactory.loadService(getWsdlDocumentUrl(), serviceQName, getJaxRpcServiceProperties());
}
if (getWsdlDocumentUrl() != null) {
return serviceFactory.createService(getWsdlDocumentUrl(), serviceQName);
}
return serviceFactory.createService(serviceQName);
}
/**
* Post-process the given JAX-RPC Service. Called by {@link #createJaxRpcService}.
* Useful, for example, to register custom type mappings.
* <p>The default implementation delegates to all registered
* {@link JaxRpcServicePostProcessor JaxRpcServicePostProcessors}.
* It is usually preferable to implement custom type mappings etc there rather
* than in a subclass of this factory, to allow for reuse of the post-processors.
* @param service the current JAX-RPC Service
* (can be cast to an implementation-specific class if necessary)
* @see #setServicePostProcessors
* @see javax.xml.rpc.Service#getTypeMappingRegistry()
*/
protected void postProcessJaxRpcService(Service service) {
JaxRpcServicePostProcessor[] postProcessors = getServicePostProcessors();
if (postProcessors != null) {
for (int i = 0; i < postProcessors.length; i++) {
postProcessors[i].postProcessJaxRpcService(service);
}
}
}
}

View File

@ -0,0 +1,62 @@
/*
* 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.remoting.jaxrpc;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
/**
* {@link org.springframework.beans.factory.FactoryBean} for locally
* defined JAX-RPC Service references.
* Uses {@link LocalJaxRpcServiceFactory}'s facilities underneath.
*
* <p>Alternatively, JAX-RPC Service references can be looked up
* in the JNDI environment of the J2EE container.
*
* @author Juergen Hoeller
* @since 15.12.2003
* @see javax.xml.rpc.Service
* @see org.springframework.jndi.JndiObjectFactoryBean
* @see JaxRpcPortProxyFactoryBean
*/
public class LocalJaxRpcServiceFactoryBean extends LocalJaxRpcServiceFactory
implements FactoryBean, InitializingBean {
private Service service;
public void afterPropertiesSet() throws ServiceException {
this.service = createJaxRpcService();
}
public Object getObject() throws Exception {
return this.service;
}
public Class getObjectType() {
return (this.service != null ? this.service.getClass() : Service.class);
}
public boolean isSingleton() {
return true;
}
}

View File

@ -0,0 +1,150 @@
/*
* 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.remoting.jaxrpc;
import java.io.File;
import javax.servlet.ServletContext;
import javax.xml.rpc.ServiceException;
import javax.xml.rpc.server.ServiceLifecycle;
import javax.xml.rpc.server.ServletEndpointContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.util.WebUtils;
/**
* Convenience base class for JAX-RPC servlet endpoint implementations.
* Provides a reference to the current Spring application context,
* e.g. for bean lookup or resource loading.
*
* <p>The Web Service servlet needs to run in the same web application
* as the Spring context to allow for access to Spring's facilities.
* In case of Axis, copy the AxisServlet definition into your web.xml,
* and set up the endpoint in "server-config.wsdd" (or use the deploy tool).
*
* <p>This class does not extend
* {@link org.springframework.web.context.support.WebApplicationObjectSupport}
* to not expose any public setters. For some reason, Axis tries to
* resolve public setters in a special way...
*
* <p>JAX-RPC service endpoints are usually required to implement an
* RMI port interface. However, many JAX-RPC implementations accept plain
* service endpoint classes too, avoiding the need to maintain an RMI port
* interface in addition to an existing non-RMI business interface.
* Therefore, implementing the business interface will usually be sufficient.
*
* @author Juergen Hoeller
* @since 16.12.2003
* @see #init
* @see #getWebApplicationContext
*/
public abstract class ServletEndpointSupport implements ServiceLifecycle {
protected final Log logger = LogFactory.getLog(getClass());
private ServletEndpointContext servletEndpointContext;
private WebApplicationContext webApplicationContext;
private MessageSourceAccessor messageSourceAccessor;
/**
* Initialize this JAX-RPC servlet endpoint.
* Calls onInit after successful context initialization.
* @param context ServletEndpointContext
* @throws ServiceException if the context is not a ServletEndpointContext
* @see #onInit
*/
public final void init(Object context) throws ServiceException {
if (!(context instanceof ServletEndpointContext)) {
throw new ServiceException("ServletEndpointSupport needs ServletEndpointContext, not [" + context + "]");
}
this.servletEndpointContext = (ServletEndpointContext) context;
ServletContext servletContext = this.servletEndpointContext.getServletContext();
this.webApplicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
this.messageSourceAccessor = new MessageSourceAccessor(this.webApplicationContext);
onInit();
}
/**
* Return the current JAX-RPC ServletEndpointContext.
*/
protected final ServletEndpointContext getServletEndpointContext() {
return this.servletEndpointContext;
}
/**
* Return the current Spring ApplicationContext.
*/
protected final ApplicationContext getApplicationContext() {
return this.webApplicationContext;
}
/**
* Return the current Spring WebApplicationContext.
*/
protected final WebApplicationContext getWebApplicationContext() {
return this.webApplicationContext;
}
/**
* Return a MessageSourceAccessor for the application context
* used by this object, for easy message access.
*/
protected final MessageSourceAccessor getMessageSourceAccessor() {
return this.messageSourceAccessor;
}
/**
* Return the current ServletContext.
*/
protected final ServletContext getServletContext() {
return this.webApplicationContext.getServletContext();
}
/**
* Return the temporary directory for the current web application,
* as provided by the servlet container.
* @return the File representing the temporary directory
*/
protected final File getTempDir() {
return WebUtils.getTempDir(getServletContext());
}
/**
* Callback for custom initialization after the context has been set up.
* @throws ServiceException if initialization failed
*/
protected void onInit() throws ServiceException {
}
/**
* This implementation of destroy is empty.
* Can be overridden in subclasses.
*/
public void destroy() {
}
}

View File

@ -0,0 +1,10 @@
<html>
<body>
Remoting classes for Web Services via JAX-RPC.
This package provides proxy factories for accessing JAX-RPC
services and ports, and a support class for implementing
JAX-RPC Servlet endpoints.
</body>
</html>

View File

@ -0,0 +1,210 @@
/*
* 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.remoting.jaxrpc.support;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.encoding.TypeMapping;
import javax.xml.rpc.encoding.TypeMappingRegistry;
import org.apache.axis.encoding.ser.BeanDeserializerFactory;
import org.apache.axis.encoding.ser.BeanSerializerFactory;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.remoting.jaxrpc.JaxRpcServicePostProcessor;
import org.springframework.util.ClassUtils;
/**
* Axis-specific {@link JaxRpcServicePostProcessor} that registers bean
* mappings for domain objects that follow the JavaBean pattern.
*
* <p>The same mappings are usually also registered at the server in
* Axis' "server-config.wsdd" file.
*
* <p>To be registered as a service post-processor on a
* {@link org.springframework.remoting.jaxrpc.LocalJaxRpcServiceFactoryBean} or
* {@link org.springframework.remoting.jaxrpc.JaxRpcPortProxyFactoryBean},
* carrying appropriate configuration.
*
* <p>Note: Without such explicit bean mappings, a complex type like a
* domain object cannot be transferred via SOAP.
*
* @author Juergen Hoeller
* @since 2.0
* @see org.apache.axis.encoding.ser.BeanSerializerFactory
* @see org.apache.axis.encoding.ser.BeanDeserializerFactory
* @see org.springframework.remoting.jaxrpc.LocalJaxRpcServiceFactoryBean#setServicePostProcessors
* @see org.springframework.remoting.jaxrpc.JaxRpcPortProxyFactoryBean#setServicePostProcessors
*/
public class AxisBeanMappingServicePostProcessor implements JaxRpcServicePostProcessor, BeanClassLoaderAware {
private String encodingStyleUri;
private String typeNamespaceUri;
private Map beanMappings;
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
/**
* Set the encoding style URI to use for the type mapping.
* <p>A typical value is "http://schemas.xmlsoap.org/soap/encoding/",
* as suggested by the JAX-RPC javadoc. However, note that the default
* behavior of this post-processor is to register the type mapping
* as JAX-RPC default if no explicit encoding style URI is given.
* @see javax.xml.rpc.encoding.TypeMappingRegistry#register
* @see javax.xml.rpc.encoding.TypeMappingRegistry#registerDefault
*/
public void setEncodingStyleUri(String encodingStyleUri) {
this.encodingStyleUri = encodingStyleUri;
}
/**
* Set the application-specific namespace to use for XML types,
* for example "urn:JPetStore".
* @see javax.xml.rpc.encoding.TypeMapping#register
*/
public void setTypeNamespaceUri(String typeNamespaceUri) {
this.typeNamespaceUri = typeNamespaceUri;
}
/**
* Specify the bean mappings to register as String-String pairs,
* with the Java type name as key and the WSDL type name as value.
*/
public void setBeanMappings(Properties beanMappingProps) {
if (beanMappingProps != null) {
this.beanMappings = new HashMap(beanMappingProps.size());
Enumeration propertyNames = beanMappingProps.propertyNames();
while (propertyNames.hasMoreElements()) {
String javaTypeName = (String) propertyNames.nextElement();
String wsdlTypeName = beanMappingProps.getProperty(javaTypeName);
this.beanMappings.put(javaTypeName, wsdlTypeName);
}
}
else {
this.beanMappings = null;
}
}
/**
* Specify the bean mappings to register as Java types,
* with the WSDL type names inferred from the Java type names
* (using the short, that is, non-fully-qualified class name).
*/
public void setBeanClasses(Class[] beanClasses) {
if (beanClasses != null) {
this.beanMappings = new HashMap(beanClasses.length);
for (int i = 0; i < beanClasses.length; i++) {
Class beanClass = beanClasses[i];
String wsdlTypeName = ClassUtils.getShortName(beanClass);
this.beanMappings.put(beanClass, wsdlTypeName);
}
}
else {
this.beanMappings = null;
}
}
public void setBeanClassLoader(ClassLoader beanClassLoader) {
this.beanClassLoader = beanClassLoader;
}
/**
* Register the specified bean mappings on the given Service's
* {@link TypeMappingRegistry}.
* @see javax.xml.rpc.Service#getTypeMappingRegistry()
* @see #setBeanMappings
* @see #registerBeanMappings(javax.xml.rpc.encoding.TypeMapping)
*/
public void postProcessJaxRpcService(Service service) {
TypeMappingRegistry registry = service.getTypeMappingRegistry();
TypeMapping mapping = registry.createTypeMapping();
registerBeanMappings(mapping);
if (this.encodingStyleUri != null) {
registry.register(this.encodingStyleUri, mapping);
}
else {
registry.registerDefault(mapping);
}
}
/**
* Perform the actual bean mapping registration.
* @param mapping the JAX-RPC {@link TypeMapping} to operate on
* @see #setBeanMappings
* @see #registerBeanMapping(javax.xml.rpc.encoding.TypeMapping, Class, String)
*/
protected void registerBeanMappings(TypeMapping mapping) {
if (this.beanMappings != null) {
for (Iterator it = this.beanMappings.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
Object key = entry.getKey();
Class javaType = null;
if (key instanceof Class) {
javaType = (Class) key;
}
else {
javaType = ClassUtils.resolveClassName((String) key, this.beanClassLoader);
}
String wsdlTypeName = (String) entry.getValue();
registerBeanMapping(mapping, javaType, wsdlTypeName);
}
}
}
/**
* Register a bean mapping for the given Java type and WSDL type name.
* @param mapping the JAX-RPC {@link TypeMapping} to operate on
* @param javaType the Java type
* @param wsdlTypeName the WSDL type name (as a {@link String})
*/
protected void registerBeanMapping(TypeMapping mapping, Class javaType, String wsdlTypeName) {
registerBeanMapping(mapping, javaType, getTypeQName(wsdlTypeName));
}
/**
* Register a bean mapping for the given Java type and WSDL type.
* @param mapping the JAX-RPC {@link TypeMapping} to operate on
* @param javaType the Java type
* @param wsdlType the WSDL type (as XML {@link QName})
*/
protected void registerBeanMapping(TypeMapping mapping, Class javaType, QName wsdlType) {
mapping.register(javaType, wsdlType,
new BeanSerializerFactory(javaType, wsdlType),
new BeanDeserializerFactory(javaType, wsdlType));
}
/**
* Return a {@link QName} for the given name, relative to the
* {@link #setTypeNamespaceUri namespace URI} of this post-processor, if given.
*/
protected final QName getTypeQName(String name) {
return (this.typeNamespaceUri != null ? new QName(this.typeNamespaceUri, name) : new QName(name));
}
}

View File

@ -0,0 +1,8 @@
<html>
<body>
Support for specific JAX-RPC providers. Contains an Axis-specific
JaxRpcServicePostProcessor for declaratively registering bean mappings.
</body>
</html>

View File

@ -0,0 +1,148 @@
/*
* 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.remoting.jaxws;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import javax.jws.WebService;
import javax.xml.ws.Endpoint;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.core.task.TaskExecutor;
import org.springframework.core.task.support.ConcurrentExecutorAdapter;
/**
* Abstract exporter for JAX-WS services, autodetecting annotated service beans
* (through the JAX-WS {@link javax.jws.WebService} annotation). Subclasses
* need to implement the {@link #publishEndpoint} template method for actual
* endpoint exposure.
*
* @author Juergen Hoeller
* @since 2.5.5
* @see javax.jws.WebService
* @see javax.xml.ws.Endpoint
* @see SimpleJaxWsServiceExporter
* @see SimpleHttpServerJaxWsServiceExporter
*/
public abstract class AbstractJaxWsServiceExporter implements BeanFactoryAware, InitializingBean, DisposableBean {
private Map<String, Object> endpointProperties;
private Executor executor;
private ListableBeanFactory beanFactory;
private final Set<Endpoint> publishedEndpoints = new LinkedHashSet<Endpoint>();
/**
* Set the property bag for the endpoint, including properties such as
* "javax.xml.ws.wsdl.service" or "javax.xml.ws.wsdl.port".
* @see javax.xml.ws.Endpoint#setProperties
* @see javax.xml.ws.Endpoint#WSDL_SERVICE
* @see javax.xml.ws.Endpoint#WSDL_PORT
*/
public void setEndpointProperties(Map<String, Object> endpointProperties) {
this.endpointProperties = endpointProperties;
}
/**
* Set the JDK concurrent executor to use for dispatching incoming requests
* to exported service instances.
* @see javax.xml.ws.Endpoint#setExecutor
*/
public void setExecutor(Executor executor) {
this.executor = executor;
}
/**
* Set the Spring TaskExecutor to use for dispatching incoming requests
* to exported service instances.
* @see javax.xml.ws.Endpoint#setExecutor
*/
public void setTaskExecutor(TaskExecutor executor) {
this.executor = new ConcurrentExecutorAdapter(executor);
}
/**
* Obtains all web service beans and publishes them as JAX-WS endpoints.
*/
public void setBeanFactory(BeanFactory beanFactory) {
if (!(beanFactory instanceof ListableBeanFactory)) {
throw new IllegalStateException(getClass().getSimpleName() + " requires a ListableBeanFactory");
}
this.beanFactory = (ListableBeanFactory) beanFactory;
}
/**
* Immediately publish all endpoints when fully configured.
* @see #publishEndpoints()
*/
public void afterPropertiesSet() throws Exception {
publishEndpoints();
}
/**
* Publish all {@link javax.jws.WebService} annotated beans in the
* containing BeanFactory.
* @see #publishEndpoint
*/
public void publishEndpoints() {
String[] beanNames = this.beanFactory.getBeanNamesForType(Object.class, false, false);
for (String beanName : beanNames) {
Class<?> type = this.beanFactory.getType(beanName);
WebService annotation = type.getAnnotation(WebService.class);
if (annotation != null) {
Endpoint endpoint = Endpoint.create(this.beanFactory.getBean(beanName));
if (this.endpointProperties != null) {
endpoint.setProperties(this.endpointProperties);
}
if (this.executor != null) {
endpoint.setExecutor(this.executor);
}
publishEndpoint(endpoint, annotation);
this.publishedEndpoints.add(endpoint);
}
}
}
/**
* Actually publish the given endpoint. To be implemented by subclasses.
* @param endpoint the JAX-WS Endpoint object
* @param annotation the service bean's WebService annotation
*/
protected abstract void publishEndpoint(Endpoint endpoint, WebService annotation);
/**
* Stops all published endpoints, taking the web services offline.
*/
public void destroy() {
for (Endpoint endpoint : this.publishedEndpoints) {
endpoint.stop();
}
}
}

View File

@ -0,0 +1,426 @@
/*
* 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.remoting.jaxws;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.ProtocolException;
import javax.xml.ws.Service;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.soap.SOAPFaultException;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.remoting.RemoteAccessException;
import org.springframework.remoting.RemoteConnectFailureException;
import org.springframework.remoting.RemoteLookupFailureException;
import org.springframework.remoting.RemoteProxyFailureException;
/**
* {@link org.aopalliance.intercept.MethodInterceptor} for accessing a
* specific port of a JAX-WS service.
*
* <p>Uses either {@link LocalJaxWsServiceFactory}'s facilities underneath,
* or takes an explicit reference to an existing JAX-WS Service instance
* (e.g. obtained via {@link org.springframework.jndi.JndiObjectFactoryBean}).
*
* @author Juergen Hoeller
* @since 2.5
* @see #setPortName
* @see #setServiceInterface
* @see javax.xml.ws.Service#getPort
* @see org.springframework.remoting.RemoteAccessException
* @see org.springframework.jndi.JndiObjectFactoryBean
*/
public class JaxWsPortClientInterceptor extends LocalJaxWsServiceFactory
implements MethodInterceptor, InitializingBean {
private Service jaxWsService;
private String portName;
private String username;
private String password;
private String endpointAddress;
private boolean maintainSession;
private boolean useSoapAction;
private String soapActionUri;
private Map<String, Object> customProperties;
private Class<?> serviceInterface;
private boolean lookupServiceOnStartup = true;
private QName portQName;
private Object portStub;
private final Object preparationMonitor = new Object();
/**
* Set a reference to an existing JAX-WS Service instance,
* for example obtained via {@link org.springframework.jndi.JndiObjectFactoryBean}.
* If not set, {@link LocalJaxWsServiceFactory}'s properties have to be specified.
* @see #setWsdlDocumentUrl
* @see #setNamespaceUri
* @see #setServiceName
* @see org.springframework.jndi.JndiObjectFactoryBean
*/
public void setJaxWsService(Service jaxWsService) {
this.jaxWsService = jaxWsService;
}
/**
* Return a reference to an existing JAX-WS Service instance, if any.
*/
public Service getJaxWsService() {
return this.jaxWsService;
}
/**
* Set the name of the port.
* Corresponds to the "wsdl:port" name.
*/
public void setPortName(String portName) {
this.portName = portName;
}
/**
* Return the name of the port.
*/
public String getPortName() {
return this.portName;
}
/**
* Set the username to specify on the stub.
* @see javax.xml.ws.BindingProvider#USERNAME_PROPERTY
*/
public void setUsername(String username) {
this.username = username;
}
/**
* Return the username to specify on the stub.
*/
public String getUsername() {
return this.username;
}
/**
* Set the password to specify on the stub.
* @see javax.xml.ws.BindingProvider#PASSWORD_PROPERTY
*/
public void setPassword(String password) {
this.password = password;
}
/**
* Return the password to specify on the stub.
*/
public String getPassword() {
return this.password;
}
/**
* Set the endpoint address to specify on the stub.
* @see javax.xml.ws.BindingProvider#ENDPOINT_ADDRESS_PROPERTY
*/
public void setEndpointAddress(String endpointAddress) {
this.endpointAddress = endpointAddress;
}
/**
* Return the endpoint address to specify on the stub.
*/
public String getEndpointAddress() {
return this.endpointAddress;
}
/**
* Set the "session.maintain" flag to specify on the stub.
* @see javax.xml.ws.BindingProvider#SESSION_MAINTAIN_PROPERTY
*/
public void setMaintainSession(boolean maintainSession) {
this.maintainSession = maintainSession;
}
/**
* Return the "session.maintain" flag to specify on the stub.
*/
public boolean isMaintainSession() {
return this.maintainSession;
}
/**
* Set the "soapaction.use" flag to specify on the stub.
* @see javax.xml.ws.BindingProvider#SOAPACTION_USE_PROPERTY
*/
public void setUseSoapAction(boolean useSoapAction) {
this.useSoapAction = useSoapAction;
}
/**
* Return the "soapaction.use" flag to specify on the stub.
*/
public boolean isUseSoapAction() {
return this.useSoapAction;
}
/**
* Set the SOAP action URI to specify on the stub.
* @see javax.xml.ws.BindingProvider#SOAPACTION_URI_PROPERTY
*/
public void setSoapActionUri(String soapActionUri) {
this.soapActionUri = soapActionUri;
}
/**
* Return the SOAP action URI to specify on the stub.
*/
public String getSoapActionUri() {
return this.soapActionUri;
}
/**
* Set custom properties to be set on the stub.
* <p>Can be populated with a String "value" (parsed via PropertiesEditor)
* or a "props" element in XML bean definitions.
* @see javax.xml.ws.BindingProvider#getRequestContext()
*/
public void setCustomProperties(Map<String, Object> customProperties) {
this.customProperties = customProperties;
}
/**
* Allow Map access to the custom properties to be set on the stub,
* with the option to add or override specific entries.
* <p>Useful for specifying entries directly, for example via
* "customProperties[myKey]". This is particularly useful for
* adding or overriding entries in child bean definitions.
*/
public Map<String, Object> getCustomProperties() {
if (this.customProperties == null) {
this.customProperties = new HashMap<String, Object>();
}
return this.customProperties;
}
/**
* Add a custom property to this JAX-WS BindingProvider.
* @param name the name of the attribute to expose
* @param value the attribute value to expose
* @see javax.xml.ws.BindingProvider#getRequestContext()
*/
public void addCustomProperty(String name, Object value) {
getCustomProperties().put(name, value);
}
/**
* Set the interface of the service that this factory should create a proxy for.
*/
public void setServiceInterface(Class serviceInterface) {
if (serviceInterface != null && !serviceInterface.isInterface()) {
throw new IllegalArgumentException("'serviceInterface' must be an interface");
}
this.serviceInterface = serviceInterface;
}
/**
* Return the interface of the service that this factory should create a proxy for.
*/
public Class getServiceInterface() {
return this.serviceInterface;
}
/**
* Set whether to look up the JAX-WS service on startup.
* <p>Default is "true". Turn this flag off to allow for late start
* of the target server. In this case, the JAX-WS service will be
* lazily fetched on first access.
*/
public void setLookupServiceOnStartup(boolean lookupServiceOnStartup) {
this.lookupServiceOnStartup = lookupServiceOnStartup;
}
public void afterPropertiesSet() {
if (this.lookupServiceOnStartup) {
prepare();
}
}
public void prepare() {
if (getServiceInterface() == null) {
throw new IllegalArgumentException("Property 'serviceInterface' is required");
}
Service serviceToUse = getJaxWsService();
if (serviceToUse == null) {
serviceToUse = createJaxWsService();
}
this.portQName = getQName(getPortName() != null ? getPortName() : getServiceInterface().getName());
Object stub = (getPortName() != null ?
serviceToUse.getPort(this.portQName, getServiceInterface()) : serviceToUse.getPort(getServiceInterface()));
preparePortStub(stub);
this.portStub = stub;
}
/**
* Return whether this client interceptor has already been prepared,
* i.e. has already looked up the JAX-WS service and port.
*/
protected boolean isPrepared() {
synchronized (this.preparationMonitor) {
return (this.portStub != null);
}
}
/**
* Return the prepared QName for the port.
* @see #setPortName
* @see #getQName
*/
protected final QName getPortQName() {
return this.portQName;
}
/**
* Prepare the given JAX-WS port stub, applying properties to it.
* Called by {@link #prepare}.
* @param stub the current JAX-WS port stub
* @see #setUsername
* @see #setPassword
* @see #setEndpointAddress
* @see #setMaintainSession
* @see #setCustomProperties
*/
protected void preparePortStub(Object stub) {
Map<String, Object> stubProperties = new HashMap<String, Object>();
String username = getUsername();
if (username != null) {
stubProperties.put(BindingProvider.USERNAME_PROPERTY, username);
}
String password = getPassword();
if (password != null) {
stubProperties.put(BindingProvider.PASSWORD_PROPERTY, password);
}
String endpointAddress = getEndpointAddress();
if (endpointAddress != null) {
stubProperties.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpointAddress);
}
if (isMaintainSession()) {
stubProperties.put(BindingProvider.SESSION_MAINTAIN_PROPERTY, Boolean.TRUE);
}
if (isUseSoapAction()) {
stubProperties.put(BindingProvider.SOAPACTION_USE_PROPERTY, Boolean.TRUE);
}
String soapActionUri = getSoapActionUri();
if (soapActionUri != null) {
stubProperties.put(BindingProvider.SOAPACTION_URI_PROPERTY, soapActionUri);
}
stubProperties.putAll(getCustomProperties());
if (!stubProperties.isEmpty()) {
if (!(stub instanceof BindingProvider)) {
throw new RemoteLookupFailureException("Port stub of class [" + stub.getClass().getName() +
"] is not a customizable JAX-WS stub: it does not implement interface [javax.xml.ws.BindingProvider]");
}
((BindingProvider) stub).getRequestContext().putAll(stubProperties);
}
}
/**
* Return the underlying JAX-WS port stub that this interceptor delegates to
* for each method invocation on the proxy.
*/
protected Object getPortStub() {
return this.portStub;
}
public Object invoke(MethodInvocation invocation) throws Throwable {
if (AopUtils.isToStringMethod(invocation.getMethod())) {
return "JAX-WS proxy for port [" + getPortName() + "] of service [" + getServiceName() + "]";
}
// Lazily prepare service and stub if necessary.
synchronized (this.preparationMonitor) {
if (!isPrepared()) {
prepare();
}
}
return doInvoke(invocation);
}
/**
* Perform a JAX-WS service invocation based on the given method invocation.
* @param invocation the AOP method invocation
* @return the invocation result, if any
* @throws Throwable in case of invocation failure
* @see #getPortStub()
* @see #doInvoke(org.aopalliance.intercept.MethodInvocation, Object)
*/
protected Object doInvoke(MethodInvocation invocation) throws Throwable {
try {
return doInvoke(invocation, getPortStub());
}
catch (SOAPFaultException ex) {
throw new JaxWsSoapFaultException(ex);
}
catch (ProtocolException ex) {
throw new RemoteConnectFailureException("Could not connect to remote service [" + this.portQName + "]", ex);
}
catch (WebServiceException ex) {
throw new RemoteAccessException("Could not access remote service at [" + this.portQName + "]", ex);
}
}
/**
* Perform a JAX-WS service invocation on the given port stub.
* @param invocation the AOP method invocation
* @param portStub the RMI port stub to invoke
* @return the invocation result, if any
* @throws Throwable in case of invocation failure
* @see #getPortStub()
*/
protected Object doInvoke(MethodInvocation invocation, Object portStub) throws Throwable {
Method method = invocation.getMethod();
try {
return method.invoke(portStub, invocation.getArguments());
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
catch (Throwable ex) {
throw new RemoteProxyFailureException("Invocation of stub method failed: " + method, ex);
}
}
}

View File

@ -0,0 +1,72 @@
/*
* 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.remoting.jaxws;
import javax.xml.ws.BindingProvider;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.util.ClassUtils;
/**
* {@link org.springframework.beans.factory.FactoryBean} for a specific port of a
* JAX-WS service. Exposes a proxy for the port, to be used for bean references.
* Inherits configuration properties from {@link JaxWsPortClientInterceptor}.
*
* @author Juergen Hoeller
* @since 2.5
* @see #setServiceInterface
* @see LocalJaxWsServiceFactoryBean
*/
public class JaxWsPortProxyFactoryBean extends JaxWsPortClientInterceptor
implements FactoryBean, BeanClassLoaderAware {
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
private Object serviceProxy;
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
public void afterPropertiesSet() {
super.afterPropertiesSet();
// Build a proxy that also exposes the JAX-WS BindingProvider interface.
ProxyFactory pf = new ProxyFactory();
pf.addInterface(getServiceInterface());
pf.addInterface(BindingProvider.class);
pf.addAdvice(this);
this.serviceProxy = pf.getProxy(this.beanClassLoader);
}
public Object getObject() {
return this.serviceProxy;
}
public Class getObjectType() {
return getServiceInterface();
}
public boolean isSingleton() {
return true;
}
}

View File

@ -0,0 +1,66 @@
/*
* 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.remoting.jaxws;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPFault;
import javax.xml.ws.soap.SOAPFaultException;
import org.springframework.remoting.soap.SoapFaultException;
/**
* Spring SoapFaultException adapter for the JAX-WS
* {@link javax.xml.ws.soap.SOAPFaultException} class.
*
* @author Juergen Hoeller
* @since 2.5
*/
public class JaxWsSoapFaultException extends SoapFaultException {
/**
* Constructor for JaxWsSoapFaultException.
* @param original the original JAX-WS SOAPFaultException to wrap
*/
public JaxWsSoapFaultException(SOAPFaultException original) {
super(original.getMessage(), original);
}
/**
* Return the wrapped JAX-WS SOAPFault.
*/
public final SOAPFault getFault() {
return ((SOAPFaultException) getCause()).getFault();
}
public String getFaultCode() {
return getFault().getFaultCode();
}
public QName getFaultCodeAsQName() {
return getFault().getFaultCodeAsQName();
}
public String getFaultString() {
return getFault().getFaultString();
}
public String getFaultActor() {
return getFault().getFaultActor();
}
}

View File

@ -0,0 +1,156 @@
/*
* 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.remoting.jaxws;
import java.net.URL;
import java.util.concurrent.Executor;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.handler.HandlerResolver;
import org.springframework.core.task.TaskExecutor;
import org.springframework.core.task.support.ConcurrentExecutorAdapter;
/**
* Factory for locally defined JAX-WS {@link javax.xml.ws.Service} references.
* Uses the JAX-WS {@link javax.xml.ws.Service#create} factory API underneath.
*
* <p>Serves as base class for {@link LocalJaxWsServiceFactoryBean} as well as
* {@link JaxWsPortClientInterceptor} and {@link JaxWsPortProxyFactoryBean}.
*
* @author Juergen Hoeller
* @since 2.5
* @see javax.xml.ws.Service
* @see LocalJaxWsServiceFactoryBean
* @see JaxWsPortClientInterceptor
* @see JaxWsPortProxyFactoryBean
*/
public class LocalJaxWsServiceFactory {
private URL wsdlDocumentUrl;
private String namespaceUri;
private String serviceName;
private Executor executor;
private HandlerResolver handlerResolver;
/**
* Set the URL of the WSDL document that describes the service.
*/
public void setWsdlDocumentUrl(URL wsdlDocumentUrl) {
this.wsdlDocumentUrl = wsdlDocumentUrl;
}
/**
* Return the URL of the WSDL document that describes the service.
*/
public URL getWsdlDocumentUrl() {
return this.wsdlDocumentUrl;
}
/**
* Set the namespace URI of the service.
* Corresponds to the WSDL "targetNamespace".
*/
public void setNamespaceUri(String namespaceUri) {
this.namespaceUri = (namespaceUri != null ? namespaceUri.trim() : null);
}
/**
* Return the namespace URI of the service.
*/
public String getNamespaceUri() {
return this.namespaceUri;
}
/**
* Set the name of the service to look up.
* Corresponds to the "wsdl:service" name.
*/
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
/**
* Return the name of the service.
*/
public String getServiceName() {
return this.serviceName;
}
/**
* Set the JDK concurrent executor to use for asynchronous executions
* that require callbacks.
* @see javax.xml.ws.Service#setExecutor
*/
public void setExecutor(Executor executor) {
this.executor = executor;
}
/**
* Set the Spring TaskExecutor to use for asynchronous executions
* that require callbacks.
* @see javax.xml.ws.Service#setExecutor
*/
public void setTaskExecutor(TaskExecutor executor) {
this.executor = new ConcurrentExecutorAdapter(executor);
}
/**
* Set the JAX-WS HandlerResolver to use for all proxies and dispatchers
* created through this factory.
* @see javax.xml.ws.Service#setHandlerResolver
*/
public void setHandlerResolver(HandlerResolver handlerResolver) {
this.handlerResolver = handlerResolver;
}
/**
* Create a JAX-WS Service according to the parameters of this factory.
* @see #setServiceName
* @see #setWsdlDocumentUrl
*/
public Service createJaxWsService() {
Service service = (this.wsdlDocumentUrl != null ?
Service.create(this.wsdlDocumentUrl, getQName(this.serviceName)) :
Service.create(getQName(this.serviceName)));
if (this.executor != null) {
service.setExecutor(this.executor);
}
if (this.handlerResolver != null) {
service.setHandlerResolver(this.handlerResolver);
}
return service;
}
/**
* Return a QName for the given name, relative to the namespace URI
* of this factory, if given.
* @see #setNamespaceUri
*/
protected QName getQName(String name) {
return (getNamespaceUri() != null ? new QName(getNamespaceUri(), name) : new QName(name));
}
}

View File

@ -0,0 +1,59 @@
/*
* 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.remoting.jaxws;
import javax.xml.ws.Service;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
/**
* {@link org.springframework.beans.factory.FactoryBean} for locally
* defined JAX-WS Service references.
* Uses {@link LocalJaxWsServiceFactory}'s facilities underneath.
*
* <p>Alternatively, JAX-WS Service references can be looked up
* in the JNDI environment of the J2EE container.
*
* @author Juergen Hoeller
* @since 2.5
* @see javax.xml.ws.Service
* @see org.springframework.jndi.JndiObjectFactoryBean
* @see JaxWsPortProxyFactoryBean
*/
public class LocalJaxWsServiceFactoryBean extends LocalJaxWsServiceFactory implements FactoryBean, InitializingBean {
private Service service;
public void afterPropertiesSet() {
this.service = createJaxWsService();
}
public Object getObject() throws Exception {
return this.service;
}
public Class getObjectType() {
return (this.service != null ? this.service.getClass() : Service.class);
}
public boolean isSingleton() {
return true;
}
}

View File

@ -0,0 +1,186 @@
/*
* 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.remoting.jaxws;
import java.net.InetSocketAddress;
import java.util.List;
import javax.jws.WebService;
import javax.xml.ws.Endpoint;
import com.sun.net.httpserver.Authenticator;
import com.sun.net.httpserver.Filter;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpServer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Simple exporter for JAX-WS services, autodetecting annotated service beans
* (through the JAX-WS {@link javax.jws.WebService} annotation) and exporting
* them through the HTTP server included in Sun's JDK 1.6. The full address
* for each service will consist of the server's base address with the
* service name appended (e.g. "http://localhost:8080/OrderService").
*
* <p>Note that this exporter will only work on Sun's JDK 1.6 or higher, as well
* as on JDKs that ship Sun's entire class library as included in the Sun JDK.
* For a portable JAX-WS exporter, have a look at {@link SimpleJaxWsServiceExporter}.
*
* @author Juergen Hoeller
* @since 2.5.5
* @see javax.jws.WebService
* @see javax.xml.ws.Endpoint#publish(Object)
* @see SimpleJaxWsServiceExporter
*/
public class SimpleHttpServerJaxWsServiceExporter extends AbstractJaxWsServiceExporter {
protected final Log logger = LogFactory.getLog(getClass());
private HttpServer server;
private int port = 8080;
private String hostname;
private int backlog = -1;
private int shutdownDelay = 0;
private String basePath = "/";
private List<Filter> filters;
private Authenticator authenticator;
private boolean localServer = false;
/**
* Specify an existing HTTP server to register the web service contexts
* with. This will typically be a server managed by the general Spring
* {@link org.springframework.remoting.support.SimpleHttpServerFactoryBean}.
* <p>Alternatively, configure a local HTTP server through the
* {@link #setPort "port"}, {@link #setHostname "hostname"} and
* {@link #setBacklog "backlog"} properties (or rely on the defaults there).
*/
public void setServer(HttpServer server) {
this.server = server;
}
/**
* Specify the HTTP server's port. Default is 8080.
* <p>Only applicable for a locally configured HTTP server.
* Ignored when the {@link #setServer "server"} property has been specified.
*/
public void setPort(int port) {
this.port = port;
}
/**
* Specify the HTTP server's hostname to bind to. Default is localhost;
* can be overridden with a specific network address to bind to.
* <p>Only applicable for a locally configured HTTP server.
* Ignored when the {@link #setServer "server"} property has been specified.
*/
public void setHostname(String hostname) {
this.hostname = hostname;
}
/**
* Specify the HTTP server's TCP backlog. Default is -1,
* indicating the system's default value.
* <p>Only applicable for a locally configured HTTP server.
* Ignored when the {@link #setServer "server"} property has been specified.
*/
public void setBacklog(int backlog) {
this.backlog = backlog;
}
/**
* Specify the number of seconds to wait until HTTP exchanges have
* completed when shutting down the HTTP server. Default is 0.
* <p>Only applicable for a locally configured HTTP server.
* Ignored when the {@link #setServer "server"} property has been specified.
*/
public void setShutdownDelay(int shutdownDelay) {
this.shutdownDelay = shutdownDelay;
}
/**
* Set the base path for context publication. Default is "/".
* <p>For each context publication path, the service name will be
* appended to this base address. E.g. service name "OrderService"
* -> "/OrderService".
* @see javax.xml.ws.Endpoint#publish(Object)
* @see javax.jws.WebService#serviceName()
*/
public void setBasePath(String basePath) {
this.basePath = basePath;
}
/**
* Register common {@link com.sun.net.httpserver.Filter Filters} to be
* applied to all detected {@link javax.jws.WebService} annotated beans.
*/
public void setFilters(List<Filter> filters) {
this.filters = filters;
}
/**
* Register a common {@link com.sun.net.httpserver.Authenticator} to be
* applied to all detected {@link javax.jws.WebService} annotated beans.
*/
public void setAuthenticator(Authenticator authenticator) {
this.authenticator = authenticator;
}
public void afterPropertiesSet() throws Exception {
if (this.server == null) {
InetSocketAddress address = (this.hostname != null ?
new InetSocketAddress(this.hostname, this.port) : new InetSocketAddress(this.port));
this.server = HttpServer.create(address, this.backlog);
if (this.logger.isInfoEnabled()) {
this.logger.info("Starting HttpServer at address " + address);
}
this.server.start();
this.localServer = true;
}
super.afterPropertiesSet();
}
protected void publishEndpoint(Endpoint endpoint, WebService annotation) {
String fullPath = this.basePath + annotation.serviceName();
HttpContext httpContext = this.server.createContext(fullPath);
if (this.filters != null) {
httpContext.getFilters().addAll(this.filters);
}
if (this.authenticator != null) {
httpContext.setAuthenticator(this.authenticator);
}
endpoint.publish(httpContext);
}
public void destroy() {
super.destroy();
if (this.localServer) {
logger.info("Stopping HttpServer");
this.server.stop(this.shutdownDelay);
}
}
}

View File

@ -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.remoting.jaxws;
import javax.jws.WebService;
import javax.xml.ws.Endpoint;
/**
* Simple exporter for JAX-WS services, autodetecting annotated service beans
* (through the JAX-WS {@link javax.jws.WebService} annotation) and exporting
* them with a configured base address (by default "http://localhost:8080/")
* using the JAX-WS provider's built-in publication support. The full address
* for each service will consist of the base address with the service name
* appended (e.g. "http://localhost:8080/OrderService").
*
* <p>Note that this exporter will only work if the JAX-WS runtime actually
* supports publishing with an address argument, i.e. if the JAX-WS runtime
* ships an internal HTTP server. This is the case with the JAX-WS runtime
* that's inclued in Sun's JDK 1.6 but not with the standalone JAX-WS 2.1 RI.
*
* <p>For explicit configuration of JAX-WS endpoints with Sun's JDK 1.6
* HTTP server, consider using {@link SimpleHttpServerJaxWsServiceExporter}!
*
* @author Juergen Hoeller
* @since 2.5
* @see javax.jws.WebService
* @see javax.xml.ws.Endpoint#publish(String)
* @see SimpleHttpServerJaxWsServiceExporter
*/
public class SimpleJaxWsServiceExporter extends AbstractJaxWsServiceExporter {
public static final String DEFAULT_BASE_ADDRESS = "http://localhost:8080/";
private String baseAddress = DEFAULT_BASE_ADDRESS;
/**
* Set the base address for exported services.
* Default is "http://localhost:8080/".
* <p>For each actual publication address, the service name will be
* appended to this base address. E.g. service name "OrderService"
* -> "http://localhost:8080/OrderService".
* @see javax.xml.ws.Endpoint#publish(String)
* @see javax.jws.WebService#serviceName()
*/
public void setBaseAddress(String baseAddress) {
this.baseAddress = baseAddress;
}
protected void publishEndpoint(Endpoint endpoint, WebService annotation) {
String fullAddress = this.baseAddress + annotation.serviceName();
endpoint.publish(fullAddress);
}
}

View File

@ -0,0 +1,9 @@
<html>
<body>
Remoting classes for Web Services via JAX-WS (the successor of JAX-RPC),
as included in Java 6 and Java EE 5. This package provides proxy
factories for accessing JAX-WS services and ports.
</body>
</html>

View File

@ -0,0 +1,90 @@
/*
* 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.web;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Plain handler interface for components that process HTTP requests,
* analogous to a Servlet. Only declares {@link javax.servlet.ServletException}
* and {@link java.io.IOException}, to allow for usage within any
* {@link javax.servlet.http.HttpServlet}}. This interface is ssentially the
* direct equivalent of an HttpServlet, reduced to a central handle method.
*
* <p>The easiest way to expose an HttpRequestHandler bean in Spring style
* is to define it in Spring's root web application context and define
* an {@link org.springframework.web.context.support.HttpRequestHandlerServlet}
* in <code>web.xml</code>, pointing at the target HttpRequestHandler bean
* through its <code>servlet-name</code> which needs to match the target bean name.
*
* <p>Supported as a handler type within Spring's
* {@link org.springframework.web.servlet.DispatcherServlet}, being able
* to interact with the dispatcher's advanced mapping and interception
* facilities. This is the recommended way of exposing an HttpRequestHandler,
* while keeping the handler implementations free of direct dependencies
* on a DispatcherServlet environment.
*
* <p>Typically implemented to generate binary responses directly,
* with no separate view resource involved. This differentiates it from a
* {@link org.springframework.web.servlet.mvc.Controller} within Spring's Web MVC
* framework. The lack of a {@link org.springframework.web.servlet.ModelAndView}
* return value gives a clearer signature to callers other than the
* DispatcherServlet, indicating that there will never be a view to render.
*
* <p>As of Spring 2.0, Spring's HTTP-based remote exporters, such as
* {@link org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter}
* and {@link org.springframework.remoting.caucho.HessianServiceExporter},
* implement this interface rather than the more extensive Controller interface,
* for minimal dependencies on Spring-specific web infrastructure.
*
* <p>Note that HttpRequestHandlers may optionally implement the
* {@link org.springframework.web.servlet.mvc.LastModified} interface,
* just like Controllers can, <i>provided that they run within Spring's
* DispatcherServlet</i>. However, this is usually not necessary, since
* HttpRequestHandlers typically only support POST requests to begin with.
* Alternatively, a handler may implement the "If-Modified-Since" HTTP
* header processing manually within its <code>handle</code> method.
*
* @author Juergen Hoeller
* @since 2.0
* @see org.springframework.web.context.support.HttpRequestHandlerServlet
* @see org.springframework.web.servlet.DispatcherServlet
* @see org.springframework.web.servlet.ModelAndView
* @see org.springframework.web.servlet.mvc.Controller
* @see org.springframework.web.servlet.mvc.LastModified
* @see org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter
* @see org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter
* @see org.springframework.remoting.caucho.HessianServiceExporter
* @see org.springframework.remoting.caucho.BurlapServiceExporter
*/
public interface HttpRequestHandler {
/**
* Process the given request, generating a response.
* @param request current HTTP request
* @param response current HTTP response
* @throws ServletException in case of general errors
* @throws IOException in case of I/O errors
*/
void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException;
}

View File

@ -0,0 +1,88 @@
/*
* 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.web;
import javax.servlet.ServletException;
/**
* Exception thrown when a request handler does not support a
* specific request method.
*
* @author Juergen Hoeller
* @since 2.0
*/
public class HttpRequestMethodNotSupportedException extends ServletException {
private String method;
private String[] supportedMethods;
/**
* Create a new HttpRequestMethodNotSupportedException.
* @param method the unsupported HTTP request method
*/
public HttpRequestMethodNotSupportedException(String method) {
this(method, (String[]) null);
}
/**
* Create a new HttpRequestMethodNotSupportedException.
* @param method the unsupported HTTP request method
* @param supportedMethods the actually supported HTTP methods
*/
public HttpRequestMethodNotSupportedException(String method, String[] supportedMethods) {
this(method, supportedMethods, "Request method '" + method + "' not supported");
}
/**
* Create a new HttpRequestMethodNotSupportedException.
* @param method the unsupported HTTP request method
* @param msg the detail message
*/
public HttpRequestMethodNotSupportedException(String method, String msg) {
this(method, null, msg);
}
/**
* Create a new HttpRequestMethodNotSupportedException.
* @param method the unsupported HTTP request method
* @param supportedMethods the actually supported HTTP methods
* @param msg the detail message
*/
public HttpRequestMethodNotSupportedException(String method, String[] supportedMethods, String msg) {
super(msg);
this.method = method;
this.supportedMethods = supportedMethods;
}
/**
* Return the HTTP request method that caused the failure.
*/
public String getMethod() {
return this.method;
}
/**
* Return the actually supported HTTP methods, if known.
*/
public String[] getSupportedMethods() {
return this.supportedMethods;
}
}

View File

@ -0,0 +1,37 @@
/*
* 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.web;
import javax.servlet.ServletException;
/**
* Exception thrown when an HTTP request handler requires a pre-existing session.
*
* @author Juergen Hoeller
* @since 2.0
*/
public class HttpSessionRequiredException extends ServletException {
/**
* Create a new HttpSessionRequiredException.
* @param msg the detail message
*/
public HttpSessionRequiredException(String msg) {
super(msg);
}
}

View File

@ -0,0 +1,95 @@
/*
* 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.web.context;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import org.springframework.context.ConfigurableApplicationContext;
/**
* Interface to be implemented by configurable web application contexts.
* Supported by {@link ContextLoader} and
* {@link org.springframework.web.servlet.FrameworkServlet}.
*
* <p>Note: The setters of this interface need to be called before an
* invocation of the {@link #refresh} method inherited from
* {@link org.springframework.context.ConfigurableApplicationContext}.
* They do not cause an initialization of the context on their own.
*
* @author Juergen Hoeller
* @since 05.12.2003
* @see #refresh
* @see ContextLoader#createWebApplicationContext
* @see org.springframework.web.servlet.FrameworkServlet#createWebApplicationContext
*/
public interface ConfigurableWebApplicationContext extends WebApplicationContext, ConfigurableApplicationContext {
/**
* Set the ServletContext for this web application context.
* <p>Does not cause an initialization of the context: refresh needs to be
* called after the setting of all configuration properties.
* @see #refresh()
*/
void setServletContext(ServletContext servletContext);
/**
* Set the ServletConfig for this web application context.
* Only called for a WebApplicationContext that belongs to a specific Servlet.
* @see #refresh()
*/
void setServletConfig(ServletConfig servletConfig);
/**
* Return the ServletConfig for this web application context, if any.
*/
ServletConfig getServletConfig();
/**
* Set the namespace for this web application context,
* to be used for building a default context config location.
* The root web application context does not have a namespace.
*/
void setNamespace(String namespace);
/**
* Return the namespace for this web application context, if any.
*/
String getNamespace();
/**
* Set the config locations for this web application context in init-param style,
* i.e. with distinct locations separated by commas, semicolons or whitespace.
* <p>If not set, the implementation is supposed to use a default for the
* given namespace or the root web application context, as appropriate.
*/
void setConfigLocation(String configLocation);
/**
* Set the config locations for this web application context.
* <p>If not set, the implementation is supposed to use a default for the
* given namespace or the root web application context, as appropriate.
*/
void setConfigLocations(String[] configLocations);
/**
* Return the config locations for this web application context,
* or <code>null</code> if none specified.
*/
String[] getConfigLocations();
}

View File

@ -0,0 +1,386 @@
/*
* 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.web.context;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import javax.servlet.ServletContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.access.BeanFactoryLocator;
import org.springframework.beans.factory.access.BeanFactoryReference;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
import org.springframework.core.CollectionFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.util.ClassUtils;
/**
* Performs the actual initialization work for the root application context.
* Called by {@link ContextLoaderListener} and {@link ContextLoaderServlet}.
*
* <p>Looks for a {@link #CONTEXT_CLASS_PARAM "contextClass"} parameter
* at the <code>web.xml</code> context-param level to specify the context
* class type, falling back to the default of
* {@link org.springframework.web.context.support.XmlWebApplicationContext}
* if not found. With the default ContextLoader implementation, any context class
* specified needs to implement the ConfigurableWebApplicationContext interface.
*
* <p>Processes a {@link #CONFIG_LOCATION_PARAM "contextConfigLocation"}
* context-param and passes its value to the context instance, parsing it into
* potentially multiple file paths which can be separated by any number of
* commas and spaces, e.g. "WEB-INF/applicationContext1.xml,
* WEB-INF/applicationContext2.xml". Ant-style path patterns are supported as well,
* e.g. "WEB-INF/*Context.xml,WEB-INF/spring*.xml" or "WEB-INF/&#42;&#42;/*Context.xml".
* If not explicitly specified, the context implementation is supposed to use a
* default location (with XmlWebApplicationContext: "/WEB-INF/applicationContext.xml").
*
* <p>Note: In case of multiple config locations, later bean definitions will
* override ones defined in previously loaded files, at least when using one of
* Spring's default ApplicationContext implementations. This can be leveraged
* to deliberately override certain bean definitions via an extra XML file.
*
* <p>Above and beyond loading the root application context, this class
* can optionally load or obtain and hook up a shared parent context to
* the root application context. See the
* {@link #loadParentContext(ServletContext)} method for more information.
*
* @author Juergen Hoeller
* @author Colin Sampaleanu
* @author Sam Brannen
* @since 17.02.2003
* @see ContextLoaderListener
* @see ContextLoaderServlet
* @see ConfigurableWebApplicationContext
* @see org.springframework.web.context.support.XmlWebApplicationContext
*/
public class ContextLoader {
/**
* Config param for the root WebApplicationContext implementation class to
* use: "<code>contextClass</code>"
*/
public static final String CONTEXT_CLASS_PARAM = "contextClass";
/**
* Name of servlet context parameter (i.e., "<code>contextConfigLocation</code>")
* that can specify the config location for the root context, falling back
* to the implementation's default otherwise.
* @see org.springframework.web.context.support.XmlWebApplicationContext#DEFAULT_CONFIG_LOCATION
*/
public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
/**
* Optional servlet context parameter (i.e., "<code>locatorFactorySelector</code>")
* used only when obtaining a parent context using the default implementation
* of {@link #loadParentContext(ServletContext servletContext)}.
* Specifies the 'selector' used in the
* {@link ContextSingletonBeanFactoryLocator#getInstance(String selector)}
* method call, which is used to obtain the BeanFactoryLocator instance from
* which the parent context is obtained.
* <p>The default is <code>classpath*:beanRefContext.xml</code>,
* matching the default applied for the
* {@link ContextSingletonBeanFactoryLocator#getInstance()} method.
* Supplying the "parentContextKey" parameter is sufficient in this case.
*/
public static final String LOCATOR_FACTORY_SELECTOR_PARAM = "locatorFactorySelector";
/**
* Optional servlet context parameter (i.e., "<code>parentContextKey</code>")
* used only when obtaining a parent context using the default implementation
* of {@link #loadParentContext(ServletContext servletContext)}.
* Specifies the 'factoryKey' used in the
* {@link BeanFactoryLocator#useBeanFactory(String factoryKey)} method call,
* obtaining the parent application context from the BeanFactoryLocator instance.
* <p>Supplying this "parentContextKey" parameter is sufficient when relying
* on the default <code>classpath*:beanRefContext.xml</code> selector for
* candidate factory references.
*/
public static final String LOCATOR_FACTORY_KEY_PARAM = "parentContextKey";
/**
* Name of the class path resource (relative to the ContextLoader class)
* that defines ContextLoader's default strategy names.
*/
private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
private static final Properties defaultStrategies;
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
}
}
private static final Log logger = LogFactory.getLog(ContextLoader.class);
/**
* Map from (thread context) ClassLoader to WebApplicationContext.
* Often just holding one reference - if the ContextLoader class is
* deployed in the web app ClassLoader itself!
*/
private static final Map currentContextPerThread = CollectionFactory.createConcurrentMapIfPossible(1);
/**
* The root WebApplicationContext instance that this loader manages.
*/
private WebApplicationContext context;
/**
* Holds BeanFactoryReference when loading parent factory via
* ContextSingletonBeanFactoryLocator.
*/
private BeanFactoryReference parentContextRef;
/**
* Initialize Spring's web application context for the given servlet context,
* according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and
* "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.
* @param servletContext current servlet context
* @return the new WebApplicationContext
* @throws IllegalStateException if there is already a root application context present
* @throws BeansException if the context failed to initialize
* @see #CONTEXT_CLASS_PARAM
* @see #CONFIG_LOCATION_PARAM
*/
public WebApplicationContext initWebApplicationContext(ServletContext servletContext)
throws IllegalStateException, BeansException {
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!");
}
servletContext.log("Initializing Spring root WebApplicationContext");
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis();
try {
// Determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
this.context = createWebApplicationContext(servletContext, parent);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
currentContextPerThread.put(Thread.currentThread().getContextClassLoader(), this.context);
if (logger.isDebugEnabled()) {
logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
}
if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
}
return this.context;
}
catch (RuntimeException ex) {
logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
catch (Error err) {
logger.error("Context initialization failed", err);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
throw err;
}
}
/**
* Instantiate the root WebApplicationContext for this loader, either the
* default context class or a custom context class if specified.
* <p>This implementation expects custom contexts to implement the
* {@link ConfigurableWebApplicationContext} interface.
* Can be overridden in subclasses.
* <p>In addition, {@link #customizeContext} gets called prior to refreshing the
* context, allowing subclasses to perform custom modifications to the context.
* @param servletContext current servlet context
* @param parent the parent ApplicationContext to use, or <code>null</code> if none
* @return the root WebApplicationContext
* @throws BeansException if the context couldn't be initialized
* @see ConfigurableWebApplicationContext
*/
protected WebApplicationContext createWebApplicationContext(
ServletContext servletContext, ApplicationContext parent) throws BeansException {
Class contextClass = determineContextClass(servletContext);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setParent(parent);
wac.setServletContext(servletContext);
wac.setConfigLocation(servletContext.getInitParameter(CONFIG_LOCATION_PARAM));
customizeContext(servletContext, wac);
wac.refresh();
return wac;
}
/**
* Return the WebApplicationContext implementation class to use, either the
* default XmlWebApplicationContext or a custom context class if specified.
* @param servletContext current servlet context
* @return the WebApplicationContext implementation class to use
* @throws ApplicationContextException if the context class couldn't be loaded
* @see #CONTEXT_CLASS_PARAM
* @see org.springframework.web.context.support.XmlWebApplicationContext
*/
protected Class determineContextClass(ServletContext servletContext) throws ApplicationContextException {
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
try {
return ClassUtils.forName(contextClassName);
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load custom context class [" + contextClassName + "]", ex);
}
}
else {
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load default context class [" + contextClassName + "]", ex);
}
}
}
/**
* Customize the {@link ConfigurableWebApplicationContext} created by this
* ContextLoader after config locations have been supplied to the context
* but before the context is <em>refreshed</em>.
* <p>The default implementation is empty but can be overridden in subclasses
* to customize the application context.
* @param servletContext the current servlet context
* @param applicationContext the newly created application context
* @see #createWebApplicationContext(ServletContext, ApplicationContext)
*/
protected void customizeContext(
ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) {
}
/**
* Template method with default implementation (which may be overridden by a
* subclass), to load or obtain an ApplicationContext instance which will be
* used as the parent context of the root WebApplicationContext. If the
* return value from the method is null, no parent context is set.
* <p>The main reason to load a parent context here is to allow multiple root
* web application contexts to all be children of a shared EAR context, or
* alternately to also share the same parent context that is visible to
* EJBs. For pure web applications, there is usually no need to worry about
* having a parent context to the root web application context.
* <p>The default implementation uses
* {@link org.springframework.context.access.ContextSingletonBeanFactoryLocator},
* configured via {@link #LOCATOR_FACTORY_SELECTOR_PARAM} and
* {@link #LOCATOR_FACTORY_KEY_PARAM}, to load a parent context
* which will be shared by all other users of ContextsingletonBeanFactoryLocator
* which also use the same configuration parameters.
* @param servletContext current servlet context
* @return the parent application context, or <code>null</code> if none
* @throws BeansException if the context couldn't be initialized
* @see org.springframework.context.access.ContextSingletonBeanFactoryLocator
*/
protected ApplicationContext loadParentContext(ServletContext servletContext)
throws BeansException {
ApplicationContext parentContext = null;
String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);
String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);
if (parentContextKey != null) {
// locatorFactorySelector may be null, indicating the default "classpath*:beanRefContext.xml"
BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);
if (logger.isDebugEnabled()) {
logger.debug("Getting parent context definition: using parent context key of '" +
parentContextKey + "' with BeanFactoryLocator");
}
this.parentContextRef = locator.useBeanFactory(parentContextKey);
parentContext = (ApplicationContext) this.parentContextRef.getFactory();
}
return parentContext;
}
/**
* Close Spring's web application context for the given servlet context. If
* the default {@link #loadParentContext(ServletContext)} implementation,
* which uses ContextSingletonBeanFactoryLocator, has loaded any shared
* parent context, release one reference to that shared parent context.
* <p>If overriding {@link #loadParentContext(ServletContext)}, you may have
* to override this method as well.
* @param servletContext the ServletContext that the WebApplicationContext runs in
*/
public void closeWebApplicationContext(ServletContext servletContext) {
servletContext.log("Closing Spring root WebApplicationContext");
try {
if (this.context instanceof ConfigurableWebApplicationContext) {
((ConfigurableWebApplicationContext) this.context).close();
}
}
finally {
currentContextPerThread.remove(Thread.currentThread().getContextClassLoader());
servletContext.removeAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
if (this.parentContextRef != null) {
this.parentContextRef.release();
}
}
}
/**
* Obtain the Spring root web application context for the current thread
* (i.e. for the current thread's context ClassLoader, which needs to be
* the web application's ClassLoader).
* @return the current root web application context, or <code>null</code>
* if none found
* @see org.springframework.web.context.support.SpringBeanAutowiringSupport
*/
public static WebApplicationContext getCurrentWebApplicationContext() {
return (WebApplicationContext) currentContextPerThread.get(Thread.currentThread().getContextClassLoader());
}
}

View File

@ -0,0 +1,5 @@
# Default WebApplicationContext implementation class for ContextLoader.
# Used as fallback when no explicit context implementation has been specified as context-param.
# Not meant to be customized by application developers.
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

View File

@ -0,0 +1,74 @@
/*
* 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.web.context;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/**
* Bootstrap listener to start up Spring's root {@link WebApplicationContext}.
* Simply delegates to {@link ContextLoader}.
*
* <p>This listener should be registered after
* {@link org.springframework.web.util.Log4jConfigListener}
* in <code>web.xml</code>, if the latter is used.
*
* @author Juergen Hoeller
* @since 17.02.2003
* @see ContextLoaderServlet
* @see org.springframework.web.util.Log4jConfigListener
*/
public class ContextLoaderListener implements ServletContextListener {
private ContextLoader contextLoader;
/**
* Initialize the root web application context.
*/
public void contextInitialized(ServletContextEvent event) {
this.contextLoader = createContextLoader();
this.contextLoader.initWebApplicationContext(event.getServletContext());
}
/**
* Create the ContextLoader to use. Can be overridden in subclasses.
* @return the new ContextLoader
*/
protected ContextLoader createContextLoader() {
return new ContextLoader();
}
/**
* Return the ContextLoader used by this listener.
* @return the current ContextLoader
*/
public ContextLoader getContextLoader() {
return this.contextLoader;
}
/**
* Close the root web application context.
*/
public void contextDestroyed(ServletContextEvent event) {
if (this.contextLoader != null) {
this.contextLoader.closeWebApplicationContext(event.getServletContext());
}
}
}

View File

@ -0,0 +1,129 @@
/*
* 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.web.context;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Bootstrap servlet to start up Spring's root {@link WebApplicationContext}.
* Simply delegates to {@link ContextLoader}.
*
* <p>This servlet should have a lower <code>load-on-startup</code> value
* in <code>web.xml</code> than any servlets that access the root web
* application context.
*
* <p><i>Note that this class has been deprecated for containers implementing
* Servlet API 2.4 or higher, in favor of {@link ContextLoaderListener}.</i><br>
* According to Servlet 2.4, listeners must be initialized before load-on-startup
* servlets. Many Servlet 2.3 containers already enforce this behavior. If you
* use such a container, this servlet can be replaced with ContextLoaderListener.
*
* <p>Servlet 2.3 containers known to work with bootstrap listeners are:
* <ul>
* <li>Apache Tomcat 4.x+
* <li>Jetty 4.x+
* <li>Resin 2.1.8+
* <li>Orion 2.0.2+
* <li>BEA WebLogic 8.1 SP3
* </ul>
* For working with any of them, ContextLoaderListener is recommended.
*
* <p>Servlet 2.3 containers known <i>not</i> to work with bootstrap listeners are:
* <ul>
* <li>BEA WebLogic up to 8.1 SP2
* <li>IBM WebSphere 5.x
* <li>Oracle OC4J 9.0.3
* </ul>
* If you happen to work with such a server, this servlet has to be used.
*
* <p>So unfortunately, the only context initialization option that is compatible
* with <i>all</i> Servlet 2.3 containers is this servlet.
*
* <p>Note that a startup failure of this servlet will not stop the rest of the
* web application from starting, in contrast to a listener failure. This can
* lead to peculiar side effects if other servlets get started that depend on
* initialization of the root web application context.
*
* @author Juergen Hoeller
* @author Darren Davison
* @see ContextLoaderListener
* @see org.springframework.web.util.Log4jConfigServlet
*/
public class ContextLoaderServlet extends HttpServlet {
private ContextLoader contextLoader;
/**
* Initialize the root web application context.
*/
public void init() throws ServletException {
this.contextLoader = createContextLoader();
this.contextLoader.initWebApplicationContext(getServletContext());
}
/**
* Create the ContextLoader to use. Can be overridden in subclasses.
* @return the new ContextLoader
*/
protected ContextLoader createContextLoader() {
return new ContextLoader();
}
/**
* Return the ContextLoader used by this servlet.
* @return the current ContextLoader
*/
public ContextLoader getContextLoader() {
return this.contextLoader;
}
/**
* Close the root web application context.
*/
public void destroy() {
if (this.contextLoader != null) {
this.contextLoader.closeWebApplicationContext(getServletContext());
}
}
/**
* This should never even be called since no mapping to this servlet should
* ever be created in web.xml. That's why a correctly invoked Servlet 2.3
* listener is much more appropriate for initialization work ;-)
*/
public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
getServletContext().log(
"Attempt to call service method on ContextLoaderServlet as [" +
request.getRequestURI() + "] was ignored");
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
}
public String getServletInfo() {
return "ContextLoaderServlet for Servlet API 2.3 " +
"(deprecated in favor of ContextLoaderListener for Servlet API 2.4)";
}
}

View File

@ -0,0 +1,48 @@
/*
* 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.web.context;
import javax.servlet.ServletConfig;
/**
* Interface to be implemented by any object that wishes to be notified
* of the ServletConfig (typically determined by the WebApplicationContext)
* that it runs in.
*
* <p>Only satisfied if actually running within a Servlet-specific
* WebApplicationContext. If this callback interface is encountered
* elsewhere, an exception will be thrown on bean creation.
*
* @author Juergen Hoeller
* @since 2.0
* @see ServletContextAware
*/
public interface ServletConfigAware {
/**
* Set the ServletConfig that this object runs in.
* <p>Invoked after population of normal bean properties but before an init
* callback like InitializingBean's <code>afterPropertiesSet</code> or a
* custom init-method. Invoked after ApplicationContextAware's
* <code>setApplicationContext</code>.
* @param servletConfig ServletConfig object to be used by this object
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
* @see org.springframework.context.ApplicationContextAware#setApplicationContext
*/
void setServletConfig(ServletConfig servletConfig);
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2002-2005 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.web.context;
import javax.servlet.ServletContext;
/**
* Interface to be implemented by any object that wishes to be notified
* of the ServletContext (typically determined by the WebApplicationContext)
* that it runs in.
*
* @author Juergen Hoeller
* @since 12.03.2004
* @see ServletConfigAware
*/
public interface ServletContextAware {
/**
* Set the ServletContext that this object runs in.
* <p>Invoked after population of normal bean properties but before an init
* callback like InitializingBean's <code>afterPropertiesSet</code> or a
* custom init-method. Invoked after ApplicationContextAware's
* <code>setApplicationContext</code>.
* @param servletContext ServletContext object to be used by this object
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
* @see org.springframework.context.ApplicationContextAware#setApplicationContext
*/
void setServletContext(ServletContext servletContext);
}

View File

@ -0,0 +1,81 @@
/*
* 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.web.context;
import javax.servlet.ServletContext;
import org.springframework.context.ApplicationContext;
/**
* Interface to provide configuration for a web application. This is read-only while
* the application is running, but may be reloaded if the implementation supports this.
*
* <p>This interface adds a getServletContext method to the generic ApplicationContext
* interface, and defines a well-known application attribute name that the root
* context must be bound to in the bootstrap process.
*
* <p>Like generic application contexts, web application contexts are hierarchical.
* There is a single root context per application, while each servlet in the application
* (including a dispatcher servlet in the MVC framework) has its own child context.
*
* <p>In addition to standard application context lifecycle capabilities,
* WebApplicationContext implementations need to detect ServletContextAware
* beans and invoke the setServletContext method accordingly.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since January 19, 2001
* @see ServletContextAware#setServletContext
*/
public interface WebApplicationContext extends ApplicationContext {
/**
* Context attribute to bind root WebApplicationContext to on successful startup.
* <p>Note: If the startup of the root context fails, this attribute can contain
* an exception or error as value. Use WebApplicationContextUtils for convenient
* lookup of the root WebApplicationContext.
* @see org.springframework.web.context.support.WebApplicationContextUtils#getWebApplicationContext
* @see org.springframework.web.context.support.WebApplicationContextUtils#getRequiredWebApplicationContext
*/
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
/**
* Scope identifier for request scope: "request".
* Supported in addition to the standard scopes "singleton" and "prototype".
*/
String SCOPE_REQUEST = "request";
/**
* Scope identifier for session scope: "session".
* Supported in addition to the standard scopes "singleton" and "prototype".
*/
String SCOPE_SESSION = "session";
/**
* Scope identifier for global session scope: "globalSession".
* Supported in addition to the standard scopes "singleton" and "prototype".
*/
String SCOPE_GLOBAL_SESSION = "globalSession";
/**
* Return the standard Servlet API ServletContext for this application.
*/
ServletContext getServletContext();
}

View File

@ -0,0 +1,8 @@
<html>
<body>
Contains a variant of the application context interface for web applications,
and the ContextLoaderListener that bootstraps a root web application context.
</body>
</html>

View File

@ -0,0 +1,104 @@
/*
* 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.web.context.request;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.util.Assert;
/**
* Abstract support class for RequestAttributes implementations,
* offering a request completion mechanism for request-specific destruction
* callbacks and for updating accessed session attributes.
*
* @author Juergen Hoeller
* @since 2.0
* @see #requestCompleted()
*/
public abstract class AbstractRequestAttributes implements RequestAttributes {
/** Map from attribute name String to destruction callback Runnable */
protected final Map requestDestructionCallbacks = new LinkedHashMap(8);
private volatile boolean requestActive = true;
/**
* Signal that the request has been completed.
* <p>Executes all request destruction callbacks and updates the
* session attributes that have been accessed during request processing.
*/
public void requestCompleted() {
executeRequestDestructionCallbacks();
updateAccessedSessionAttributes();
this.requestActive = false;
}
/**
* Determine whether the original request is still active.
* @see #requestCompleted()
*/
protected final boolean isRequestActive() {
return this.requestActive;
}
/**
* Register the given callback as to be executed after request completion.
* @param name the name of the attribute to register the callback for
* @param callback the callback to be executed for destruction
*/
protected final void registerRequestDestructionCallback(String name, Runnable callback) {
Assert.notNull(name, "Name must not be null");
Assert.notNull(callback, "Callback must not be null");
synchronized (this.requestDestructionCallbacks) {
this.requestDestructionCallbacks.put(name, callback);
}
}
/**
* Remove the request destruction callback for the specified attribute, if any.
* @param name the name of the attribute to remove the callback for
*/
protected final void removeRequestDestructionCallback(String name) {
Assert.notNull(name, "Name must not be null");
synchronized (this.requestDestructionCallbacks) {
this.requestDestructionCallbacks.remove(name);
}
}
/**
* Execute all callbacks that have been registered for execution
* after request completion.
*/
private void executeRequestDestructionCallbacks() {
synchronized (this.requestDestructionCallbacks) {
for (Iterator it = this.requestDestructionCallbacks.values().iterator(); it.hasNext();) {
((Runnable) it.next()).run();
}
this.requestDestructionCallbacks.clear();
}
}
/**
* Update all session attributes that have been accessed during request processing,
* to expose their potentially updated state to the underlying session manager.
*/
protected abstract void updateAccessedSessionAttributes();
}

View File

@ -0,0 +1,77 @@
/*
* 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.web.context.request;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
/**
* Abstract {@link Scope} implementation that reads from a particular scope
* in the current thread-bound {@link RequestAttributes} object.
*
* <p>Subclasses simply need to implement {@link #getScope()} to instruct
* this class which {@link RequestAttributes} scope to read attributes from.
*
* <p>Subclasses may wish to override the {@link #get} and {@link #remove}
* methods to add synchronization around the call back into this super class.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Rob Harrop
* @since 2.0
*/
public abstract class AbstractRequestAttributesScope implements Scope {
public Object get(String name, ObjectFactory objectFactory) {
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
Object scopedObject = attributes.getAttribute(name, getScope());
if (scopedObject == null) {
scopedObject = objectFactory.getObject();
attributes.setAttribute(name, scopedObject, getScope());
}
return scopedObject;
}
public Object remove(String name) {
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
Object scopedObject = attributes.getAttribute(name, getScope());
if (scopedObject != null) {
attributes.removeAttribute(name, getScope());
return scopedObject;
}
else {
return null;
}
}
public void registerDestructionCallback(String name, Runnable callback) {
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
attributes.registerDestructionCallback(name, callback, getScope());
}
/**
* Template method that determines the actual target scope.
* @return the target scope, in the form of an appropriate
* {@link RequestAttributes} constant
* @see RequestAttributes#SCOPE_REQUEST
* @see RequestAttributes#SCOPE_SESSION
* @see RequestAttributes#SCOPE_GLOBAL_SESSION
*/
protected abstract int getScope();
}

View File

@ -0,0 +1,149 @@
/*
* 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.web.context.request;
import java.lang.reflect.Method;
import java.util.Map;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.util.WebUtils;
/**
* {@link RequestAttributes} adapter for a JSF {@link javax.faces.context.FacesContext}.
* Used as default in a JSF environment, wrapping the current FacesContext.
*
* <p><b>NOTE:</b> In contrast to {@link ServletRequestAttributes}, this variant does
* <i>not</i> support destruction callbacks for scoped attributes, neither for the
* request scope nor for the session scope. If you rely on such implicit destruction
* callbacks, consider defining a Spring {@link RequestContextListener} in your
* <code>web.xml</code>.
*
* @author Juergen Hoeller
* @since 2.5.2
* @see javax.faces.context.FacesContext#getExternalContext()
* @see javax.faces.context.ExternalContext#getRequestMap()
* @see javax.faces.context.ExternalContext#getSessionMap()
* @see RequestContextHolder#currentRequestAttributes()
*/
public class FacesRequestAttributes implements RequestAttributes {
/**
* We'll create a lot of these objects, so we don't want a new logger every time.
*/
private static final Log logger = LogFactory.getLog(FacesRequestAttributes.class);
private final FacesContext facesContext;
/**
* Create a new FacesRequestAttributes adapter for the given FacesContext.
* @param facesContext the current FacesContext
* @see javax.faces.context.FacesContext#getCurrentInstance()
*/
public FacesRequestAttributes(FacesContext facesContext) {
Assert.notNull(facesContext, "FacesContext must not be null");
this.facesContext = facesContext;
}
/**
* Return the JSF FacesContext that this adapter operates on.
*/
protected FacesContext getFacesContext() {
return this.facesContext;
}
/**
* Return the JSF ExternalContext that this adapter operates on.
* @see javax.faces.context.FacesContext#getExternalContext()
*/
protected ExternalContext getExternalContext() {
return getFacesContext().getExternalContext();
}
/**
* Return the JSF attribute Map for the specified scope
* @param scope constant indicating request or session scope
* @return the Map representation of the attributes in the specified scope
* @see #SCOPE_REQUEST
* @see #SCOPE_SESSION
*/
protected Map getAttributeMap(int scope) {
if (scope == SCOPE_REQUEST) {
return getExternalContext().getRequestMap();
}
else {
return getExternalContext().getSessionMap();
}
}
public Object getAttribute(String name, int scope) {
return getAttributeMap(scope).get(name);
}
public void setAttribute(String name, Object value, int scope) {
getAttributeMap(scope).put(name, value);
}
public void removeAttribute(String name, int scope) {
getAttributeMap(scope).remove(name);
}
public String[] getAttributeNames(int scope) {
return StringUtils.toStringArray(getAttributeMap(scope).entrySet());
}
public void registerDestructionCallback(String name, Runnable callback, int scope) {
if (logger.isWarnEnabled()) {
logger.warn("Could not register destruction callback [" + callback + "] for attribute '" + name +
"' because FacesRequestAttributes does not support such callbacks");
}
}
public String getSessionId() {
Object session = getExternalContext().getSession(true);
try {
// Both HttpSession and PortletSession have a getId() method.
Method getIdMethod = session.getClass().getMethod("getId", new Class[0]);
return ReflectionUtils.invokeMethod(getIdMethod, session).toString();
}
catch (NoSuchMethodException ex) {
throw new IllegalStateException("Session object [" + session + "] does not have a getId() method");
}
}
public Object getSessionMutex() {
// Enforce presence of a session first to allow listeners
// to create the mutex attribute, if any.
Object session = getExternalContext().getSession(true);
Object mutex = getExternalContext().getSessionMap().get(WebUtils.SESSION_MUTEX_ATTRIBUTE);
if (mutex == null) {
mutex = session;
}
return mutex;
}
}

View File

@ -0,0 +1,117 @@
/*
* 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.web.context.request;
import java.security.Principal;
import java.util.Locale;
import java.util.Map;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import org.springframework.util.StringUtils;
/**
* {@link WebRequest} adapter for a JSF {@link javax.faces.context.FacesContext}.
*
* @author Juergen Hoeller
* @since 2.5.2
*/
public class FacesWebRequest extends FacesRequestAttributes implements NativeWebRequest {
/**
* Create a new FacesWebRequest adapter for the given FacesContext.
* @param facesContext the current FacesContext
* @see javax.faces.context.FacesContext#getCurrentInstance()
*/
public FacesWebRequest(FacesContext facesContext) {
super(facesContext);
}
public Object getNativeRequest() {
return getExternalContext().getRequest();
}
public Object getNativeResponse() {
return getExternalContext().getResponse();
}
public String getParameter(String paramName) {
return (String) getExternalContext().getRequestParameterMap().get(paramName);
}
public String[] getParameterValues(String paramName) {
return (String[]) getExternalContext().getRequestParameterMap().get(paramName);
}
public Map getParameterMap() {
return getExternalContext().getRequestParameterMap();
}
public Locale getLocale() {
return getFacesContext().getExternalContext().getRequestLocale();
}
public String getContextPath() {
return getFacesContext().getExternalContext().getRequestContextPath();
}
public String getRemoteUser() {
return getFacesContext().getExternalContext().getRemoteUser();
}
public Principal getUserPrincipal() {
return getFacesContext().getExternalContext().getUserPrincipal();
}
public boolean isUserInRole(String role) {
return getFacesContext().getExternalContext().isUserInRole(role);
}
public boolean isSecure() {
return false;
}
public boolean checkNotModified(long lastModifiedTimestamp) {
return false;
}
public String getDescription(boolean includeClientInfo) {
ExternalContext externalContext = getExternalContext();
StringBuffer buffer = new StringBuffer();
buffer.append("context=").append(externalContext.getRequestContextPath());
if (includeClientInfo) {
Object session = externalContext.getSession(false);
if (session != null) {
buffer.append(";session=").append(getSessionId());
}
String user = externalContext.getRemoteUser();
if (StringUtils.hasLength(user)) {
buffer.append(";user=").append(user);
}
}
return buffer.toString();
}
public String toString() {
return "FacesWebRequest: " + getDescription(true);
}
}

View File

@ -0,0 +1,91 @@
/*
* 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.web.context.request;
import org.apache.log4j.Logger;
import org.apache.log4j.NDC;
import org.springframework.ui.ModelMap;
/**
* Request logging interceptor that adds a request context message to the
* Log4J nested diagnostic context (NDC) before the request is processed,
* removing it again after the request is processed.
*
* @author Juergen Hoeller
* @since 2.5
* @see org.apache.log4j.NDC#push(String)
* @see org.apache.log4j.NDC#pop()
*/
public class Log4jNestedDiagnosticContextInterceptor implements WebRequestInterceptor {
/** Logger available to subclasses */
protected final Logger log4jLogger = Logger.getLogger(getClass());
private boolean includeClientInfo = false;
/**
* Set whether or not the session id and user name should be included
* in the log message.
*/
public void setIncludeClientInfo(boolean includeClientInfo) {
this.includeClientInfo = includeClientInfo;
}
/**
* Return whether or not the session id and user name should be included
* in the log message.
*/
protected boolean isIncludeClientInfo() {
return this.includeClientInfo;
}
/**
* Adds a message the Log4J NDC before the request is processed.
*/
public void preHandle(WebRequest request) throws Exception {
NDC.push(getNestedDiagnosticContextMessage(request));
}
/**
* Determine the message to be pushed onto the Log4J nested diagnostic context.
* <p>Default is the request object's <code>getDescription</code> result.
* @param request current HTTP request
* @return the message to be pushed onto the Log4J NDC
* @see WebRequest#getDescription
* @see #isIncludeClientInfo()
*/
protected String getNestedDiagnosticContextMessage(WebRequest request) {
return request.getDescription(isIncludeClientInfo());
}
public void postHandle(WebRequest request, ModelMap model) throws Exception {
}
/**
* Removes the log message from the Log4J NDC after the request is processed.
*/
public void afterCompletion(WebRequest request, Exception ex) throws Exception {
NDC.pop();
if (NDC.getDepth() == 0) {
NDC.remove();
}
}
}

View File

@ -0,0 +1,47 @@
/*
* 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.web.context.request;
/**
* Extension of the {@link WebRequest} interface, exposing the
* native request and response objects in a generic fashion.
*
* <p>Mainly intended for framework-internal usage,
* in particular for generic argument resolution code.
*
* @author Juergen Hoeller
* @since 2.5.2
*/
public interface NativeWebRequest extends WebRequest {
/**
* Return the underlying native request object, if available.
* @see javax.servlet.http.HttpServletRequest
* @see javax.portlet.ActionRequest
* @see javax.portlet.RenderRequest
*/
Object getNativeRequest();
/**
* Return the underlying native response object, if available.
* @see javax.servlet.http.HttpServletResponse
* @see javax.portlet.ActionResponse
* @see javax.portlet.RenderResponse
*/
Object getNativeResponse();
}

View File

@ -0,0 +1,124 @@
/*
* 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.web.context.request;
/**
* Abstraction for accessing attribute objects associated with a request.
* Supports access to request-scoped attributes as well as to session-scoped
* attributes, with the optional notion of a "global session".
*
* <p>Can be implemented for any kind of request/session mechanism,
* in particular for servlet requests and portlet requests.
*
* @author Juergen Hoeller
* @since 2.0
* @see ServletRequestAttributes
* @see org.springframework.web.portlet.context.PortletRequestAttributes
*/
public interface RequestAttributes {
/**
* Constant that indicates request scope.
*/
int SCOPE_REQUEST = 0;
/**
* Constant that indicates session scope.
* <p>This preferably refers to a locally isolated session, if such
* a distinction is available (for example, in a Portlet environment).
* Else, it simply refers to the common session.
*/
int SCOPE_SESSION = 1;
/**
* Constant that indicates global session scope.
* <p>This explicitly refers to a globally shared session, if such
* a distinction is available (for example, in a Portlet environment).
* Else, it simply refers to the common session.
*/
int SCOPE_GLOBAL_SESSION = 2;
/**
* Return the value for the scoped attribute of the given name, if any.
* @param name the name of the attribute
* @param scope the scope identifier
* @return the current attribute value, or <code>null</code> if not found
*/
Object getAttribute(String name, int scope);
/**
* Set the value for the scoped attribute of the given name,
* replacing an existing value (if any).
* @param name the name of the attribute
* @param scope the scope identifier
* @param value the value for the attribute
*/
void setAttribute(String name, Object value, int scope);
/**
* Remove the scoped attribute of the given name, if it exists.
* <p>Note that an implementation should also remove a registered destruction
* callback for the specified attribute, if any. It does, however, <i>not</i>
* need to <i>execute</i> a registered destruction callback in this case,
* since the object will be destroyed by the caller (if appropriate).
* @param name the name of the attribute
* @param scope the scope identifier
*/
void removeAttribute(String name, int scope);
/**
* Retrieve the names of all attributes in the scope.
* @param scope the scope identifier
* @return the attribute names as String array
*/
String[] getAttributeNames(int scope);
/**
* Register a callback to be executed on destruction of the
* specified attribute in the given scope.
* <p>Implementations should do their best to execute the callback
* at the appropriate time: that is, at request completion or session
* termination, respectively. If such a callback is not supported by the
* underlying runtime environment, the callback <i>must be ignored</i>
* and a corresponding warning should be logged.
* <p>Note that 'destruction' usually corresponds to destruction of the
* entire scope, not to the individual attribute having been explicitly
* removed by the application. If an attribute gets removed via this
* facade's {@link #removeAttribute(String, int)} method, any registered
* destruction callback should be disabled as well, assuming that the
* removed object will be reused or manually destroyed.
* @param name the name of the attribute to register the callback for
* @param callback the destruction callback to be executed
* @param scope the scope identifier
*/
void registerDestructionCallback(String name, Runnable callback, int scope);
/**
* Return an id for the current underlying session.
* @return the session id as String (never <code>null</code>
*/
String getSessionId();
/**
* Expose the best available mutex for the underlying session:
* that is, an object to synchronize on for the underlying session.
* @return the session mutex to use (never <code>null</code>
*/
Object getSessionMutex();
}

View File

@ -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.web.context.request;
import javax.faces.context.FacesContext;
import org.springframework.core.NamedInheritableThreadLocal;
import org.springframework.core.NamedThreadLocal;
import org.springframework.util.ClassUtils;
/**
* Holder class to expose the web request in the form of a thread-bound
* {@link RequestAttributes} object.
*
* <p>Use {@link RequestContextListener} or
* {@link org.springframework.web.filter.RequestContextFilter} to expose
* the current web request. Note that
* {@link org.springframework.web.servlet.DispatcherServlet} and
* {@link org.springframework.web.portlet.DispatcherPortlet} already
* expose the current request by default.
*
* @author Juergen Hoeller
* @author Rod Johnson
* @since 2.0
* @see RequestContextListener
* @see org.springframework.web.filter.RequestContextFilter
* @see org.springframework.web.servlet.DispatcherServlet
* @see org.springframework.web.portlet.DispatcherPortlet
*/
public abstract class RequestContextHolder {
private static final boolean jsfPresent =
ClassUtils.isPresent("javax.faces.context.FacesContext", RequestContextHolder.class.getClassLoader());
private static final ThreadLocal requestAttributesHolder = new NamedThreadLocal("Request attributes");
private static final ThreadLocal inheritableRequestAttributesHolder =
new NamedInheritableThreadLocal("Request context");
/**
* Reset the RequestAttributes for the current thread.
*/
public static void resetRequestAttributes() {
requestAttributesHolder.set(null);
inheritableRequestAttributesHolder.set(null);
}
/**
* Bind the given RequestAttributes to the current thread,
* <i>not</i> exposing it as inheritable for child threads.
* @param attributes the RequestAttributes to expose
* @see #setRequestAttributes(RequestAttributes, boolean)
*/
public static void setRequestAttributes(RequestAttributes attributes) {
setRequestAttributes(attributes, false);
}
/**
* Bind the given RequestAttributes to the current thread.
* @param attributes the RequestAttributes to expose
* @param inheritable whether to expose the RequestAttributes as inheritable
* for child threads (using an {@link java.lang.InheritableThreadLocal})
*/
public static void setRequestAttributes(RequestAttributes attributes, boolean inheritable) {
if (inheritable) {
inheritableRequestAttributesHolder.set(attributes);
requestAttributesHolder.set(null);
}
else {
requestAttributesHolder.set(attributes);
inheritableRequestAttributesHolder.set(null);
}
}
/**
* Return the RequestAttributes currently bound to the thread.
* @return the RequestAttributes currently bound to the thread,
* or <code>null</code> if none bound
*/
public static RequestAttributes getRequestAttributes() {
RequestAttributes attributes = (RequestAttributes) requestAttributesHolder.get();
if (attributes == null) {
attributes = (RequestAttributes) inheritableRequestAttributesHolder.get();
}
return attributes;
}
/**
* Return the RequestAttributes currently bound to the thread.
* <p>Exposes the previously bound RequestAttributes instance, if any.
* Falls back to the current JSF FacesContext, if any.
* @return the RequestAttributes currently bound to the thread
* @throws IllegalStateException if no RequestAttributes object
* is bound to the current thread
* @see #setRequestAttributes
* @see ServletRequestAttributes
* @see FacesRequestAttributes
* @see javax.faces.context.FacesContext#getCurrentInstance()
*/
public static RequestAttributes currentRequestAttributes() throws IllegalStateException {
RequestAttributes attributes = getRequestAttributes();
if (attributes == null) {
if (jsfPresent) {
attributes = FacesRequestAttributesFactory.getFacesRequestAttributes();
}
if (attributes == null) {
throw new IllegalStateException("No thread-bound request found: " +
"Are you referring to request attributes outside of an actual web request, " +
"or processing a request outside of the originally receiving thread? " +
"If you are actually operating within a web request and still receive this message, " +
"your code is probably running outside of DispatcherServlet/DispatcherPortlet: " +
"In this case, use RequestContextListener or RequestContextFilter to expose the current request.");
}
}
return attributes;
}
/**
* Inner class to avoid hard-coded JSF dependency.
*/
private static class FacesRequestAttributesFactory {
public static RequestAttributes getFacesRequestAttributes() {
FacesContext facesContext = FacesContext.getCurrentInstance();
return (facesContext != null ? new FacesRequestAttributes(facesContext) : null);
}
}
}

View File

@ -0,0 +1,94 @@
/*
* 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.web.context.request;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.i18n.LocaleContextHolder;
/**
* Servlet 2.4+ listener that exposes the request to the current thread,
* through both {@link org.springframework.context.i18n.LocaleContextHolder} and
* {@link RequestContextHolder}. To be registered as listener in <code>web.xml</code>.
*
* <p>Alternatively, Spring's {@link org.springframework.web.filter.RequestContextFilter}
* and Spring's {@link org.springframework.web.servlet.DispatcherServlet} also expose
* the same request context to the current thread. In contrast to this listener,
* advanced options are available there (e.g. "threadContextInheritable").
*
* <p>This listener is mainly for use with third-party servlets, e.g. the JSF FacesServlet.
* Within Spring's own web support, DispatcherServlet's processing is perfectly sufficient.
*
* @author Juergen Hoeller
* @since 2.0
* @see javax.servlet.ServletRequestListener
* @see org.springframework.context.i18n.LocaleContextHolder
* @see org.springframework.web.context.request.RequestContextHolder
* @see org.springframework.web.filter.RequestContextFilter
* @see org.springframework.web.servlet.DispatcherServlet
*/
public class RequestContextListener implements ServletRequestListener {
private static final String REQUEST_ATTRIBUTES_ATTRIBUTE =
RequestContextListener.class.getName() + ".REQUEST_ATTRIBUTES";
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
public void requestInitialized(ServletRequestEvent requestEvent) {
if (!(requestEvent.getServletRequest() instanceof HttpServletRequest)) {
throw new IllegalArgumentException(
"Request is not an HttpServletRequest: " + requestEvent.getServletRequest());
}
HttpServletRequest request = (HttpServletRequest) requestEvent.getServletRequest();
ServletRequestAttributes attributes = new ServletRequestAttributes(request);
request.setAttribute(REQUEST_ATTRIBUTES_ATTRIBUTE, attributes);
LocaleContextHolder.setLocale(request.getLocale());
RequestContextHolder.setRequestAttributes(attributes);
if (logger.isDebugEnabled()) {
logger.debug("Bound request context to thread: " + request);
}
}
public void requestDestroyed(ServletRequestEvent requestEvent) {
ServletRequestAttributes attributes =
(ServletRequestAttributes) requestEvent.getServletRequest().getAttribute(REQUEST_ATTRIBUTES_ATTRIBUTE);
ServletRequestAttributes threadAttributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (threadAttributes != null) {
// We're assumably within the original request thread...
if (attributes == null) {
attributes = threadAttributes;
}
RequestContextHolder.resetRequestAttributes();
LocaleContextHolder.resetLocaleContext();
}
if (attributes != null) {
attributes.requestCompleted();
if (logger.isDebugEnabled()) {
logger.debug("Cleared thread-bound request context: " + requestEvent.getServletRequest());
}
}
}
}

View File

@ -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.web.context.request;
/**
* Request-backed {@link org.springframework.beans.factory.config.Scope}
* implementation.
*
* <p>Relies on a thread-bound {@link RequestAttributes} instance, which
* can be exported through {@link RequestContextListener},
* {@link org.springframework.web.filter.RequestContextFilter} or
* {@link org.springframework.web.servlet.DispatcherServlet}.
*
* <p>This <code>Scope</code> will also work for Portlet environments,
* through an alternate <code>RequestAttributes</code> implementation
* (as exposed out-of-the-box by Spring's
* {@link org.springframework.web.portlet.DispatcherPortlet}.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Rob Harrop
* @since 2.0
* @see RequestContextHolder#currentRequestAttributes()
* @see RequestAttributes#SCOPE_REQUEST
* @see RequestContextListener
* @see org.springframework.web.filter.RequestContextFilter
* @see org.springframework.web.servlet.DispatcherServlet
* @see org.springframework.web.portlet.DispatcherPortlet
*/
public class RequestScope extends AbstractRequestAttributesScope {
protected int getScope() {
return RequestAttributes.SCOPE_REQUEST;
}
/**
* There is no conversation id concept for a request, so this method
* returns <code>null</code>.
*/
public String getConversationId() {
return null;
}
}

View File

@ -0,0 +1,273 @@
/*
* 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.web.context.request;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.util.WebUtils;
/**
* Servlet-based implementation of the {@link RequestAttributes} interface.
*
* <p>Accesses objects from servlet request and HTTP session scope,
* with no distinction between "session" and "global session".
*
* @author Juergen Hoeller
* @since 2.0
* @see javax.servlet.ServletRequest#getAttribute
* @see javax.servlet.http.HttpSession#getAttribute
*/
public class ServletRequestAttributes extends AbstractRequestAttributes {
/**
* Constant identifying the {@link String} prefixed to the name of a
* destruction callback when it is stored in a {@link HttpSession}.
*/
public static final String DESTRUCTION_CALLBACK_NAME_PREFIX =
ServletRequestAttributes.class.getName() + ".DESTRUCTION_CALLBACK.";
private final HttpServletRequest request;
private volatile HttpSession session;
private final Map sessionAttributesToUpdate = new HashMap();
/**
* Create a new ServletRequestAttributes instance for the given request.
* @param request current HTTP request
*/
public ServletRequestAttributes(HttpServletRequest request) {
Assert.notNull(request, "Request must not be null");
this.request = request;
}
/**
* Exposes the native {@link HttpServletRequest} that we're wrapping.
*/
public final HttpServletRequest getRequest() {
return this.request;
}
/**
* Exposes the {@link HttpSession} that we're wrapping.
* @param allowCreate whether to allow creation of a new session if none exists yet
*/
protected final HttpSession getSession(boolean allowCreate) {
if (isRequestActive()) {
return this.request.getSession(allowCreate);
}
else {
// Access through stored session reference, if any...
if (this.session == null && allowCreate) {
throw new IllegalStateException(
"No session found and request already completed - cannot create new session!");
}
return this.session;
}
}
public Object getAttribute(String name, int scope) {
if (scope == SCOPE_REQUEST) {
if (!isRequestActive()) {
throw new IllegalStateException(
"Cannot ask for request attribute - request is not active anymore!");
}
return this.request.getAttribute(name);
}
else {
HttpSession session = getSession(false);
if (session != null) {
try {
Object value = session.getAttribute(name);
if (value != null) {
synchronized (this.sessionAttributesToUpdate) {
this.sessionAttributesToUpdate.put(name, value);
}
}
return value;
}
catch (IllegalStateException ex) {
// Session invalidated - shouldn't usually happen.
}
}
return null;
}
}
public void setAttribute(String name, Object value, int scope) {
if (scope == SCOPE_REQUEST) {
if (!isRequestActive()) {
throw new IllegalStateException(
"Cannot set request attribute - request is not active anymore!");
}
this.request.setAttribute(name, value);
}
else {
HttpSession session = getSession(true);
synchronized (this.sessionAttributesToUpdate) {
this.sessionAttributesToUpdate.remove(name);
}
session.setAttribute(name, value);
}
}
public void removeAttribute(String name, int scope) {
if (scope == SCOPE_REQUEST) {
if (isRequestActive()) {
this.request.removeAttribute(name);
removeRequestDestructionCallback(name);
}
}
else {
HttpSession session = getSession(false);
if (session != null) {
synchronized (this.sessionAttributesToUpdate) {
this.sessionAttributesToUpdate.remove(name);
}
try {
session.removeAttribute(name);
// Remove any registered destruction callback as well.
session.removeAttribute(DESTRUCTION_CALLBACK_NAME_PREFIX + name);
}
catch (IllegalStateException ex) {
// Session invalidated - shouldn't usually happen.
}
}
}
}
public String[] getAttributeNames(int scope) {
if (scope == SCOPE_REQUEST) {
if (!isRequestActive()) {
throw new IllegalStateException(
"Cannot ask for request attributes - request is not active anymore!");
}
return StringUtils.toStringArray(this.request.getAttributeNames());
}
else {
HttpSession session = getSession(false);
if (session != null) {
try {
return StringUtils.toStringArray(session.getAttributeNames());
}
catch (IllegalStateException ex) {
// Session invalidated - shouldn't usually happen.
}
}
return new String[0];
}
}
public void registerDestructionCallback(String name, Runnable callback, int scope) {
if (scope == SCOPE_REQUEST) {
registerRequestDestructionCallback(name, callback);
}
else {
registerSessionDestructionCallback(name, callback);
}
}
public String getSessionId() {
return getSession(true).getId();
}
public Object getSessionMutex() {
return WebUtils.getSessionMutex(getSession(true));
}
/**
* Update all accessed session attributes through <code>session.setAttribute</code>
* calls, explicitly indicating to the container that they might have been modified.
*/
protected void updateAccessedSessionAttributes() {
// Store session reference for access after request completion.
this.session = this.request.getSession(false);
// Update all affected session attributes.
synchronized (this.sessionAttributesToUpdate) {
if (this.session != null) {
try {
for (Iterator it = this.sessionAttributesToUpdate.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
String name = (String) entry.getKey();
Object newValue = entry.getValue();
Object oldValue = this.session.getAttribute(name);
if (oldValue == newValue) {
this.session.setAttribute(name, newValue);
}
}
}
catch (IllegalStateException ex) {
// Session invalidated - shouldn't usually happen.
}
}
this.sessionAttributesToUpdate.clear();
}
}
/**
* Register the given callback as to be executed after session termination.
* @param name the name of the attribute to register the callback for
* @param callback the callback to be executed for destruction
*/
private void registerSessionDestructionCallback(String name, Runnable callback) {
HttpSession session = getSession(true);
session.setAttribute(DESTRUCTION_CALLBACK_NAME_PREFIX + name,
new DestructionCallbackBindingListener(callback));
}
public String toString() {
return this.request.toString();
}
/**
* Adapter that implements the Servlet 2.3 HttpSessionBindingListener
* interface, wrapping a session destruction callback.
*/
private static class DestructionCallbackBindingListener implements HttpSessionBindingListener, Serializable {
private final Runnable destructionCallback;
public DestructionCallbackBindingListener(Runnable destructionCallback) {
this.destructionCallback = destructionCallback;
}
public void valueBound(HttpSessionBindingEvent event) {
}
public void valueUnbound(HttpSessionBindingEvent event) {
this.destructionCallback.run();
}
}
}

View File

@ -0,0 +1,166 @@
/*
* 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.web.context.request;
import java.security.Principal;
import java.util.Locale;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.util.StringUtils;
/**
* {@link WebRequest} adapter for an {@link javax.servlet.http.HttpServletRequest}.
*
* @author Juergen Hoeller
* @since 2.0
*/
public class ServletWebRequest extends ServletRequestAttributes implements NativeWebRequest {
private static final String HEADER_IF_MODIFIED_SINCE = "If-Modified-Since";
private static final String HEADER_LAST_MODIFIED = "Last-Modified";
private HttpServletResponse response;
private boolean notModified = false;
/**
* Create a new ServletWebRequest instance for the given request.
* @param request current HTTP request
*/
public ServletWebRequest(HttpServletRequest request) {
super(request);
}
/**
* Create a new ServletWebRequest instance for the given request/response pair.
* @param request current HTTP request
* @param response current HTTP response (for automatic last-modified handling)
*/
public ServletWebRequest(HttpServletRequest request, HttpServletResponse response) {
super(request);
this.response = response;
}
/**
* Exposes the native {@link HttpServletRequest} that we're wrapping (if any).
*/
public final HttpServletResponse getResponse() {
return this.response;
}
public Object getNativeRequest() {
return getRequest();
}
public Object getNativeResponse() {
return getResponse();
}
public String getParameter(String paramName) {
return getRequest().getParameter(paramName);
}
public String[] getParameterValues(String paramName) {
return getRequest().getParameterValues(paramName);
}
public Map getParameterMap() {
return getRequest().getParameterMap();
}
public Locale getLocale() {
return getRequest().getLocale();
}
public String getContextPath() {
return getRequest().getContextPath();
}
public String getRemoteUser() {
return getRequest().getRemoteUser();
}
public Principal getUserPrincipal() {
return getRequest().getUserPrincipal();
}
public boolean isUserInRole(String role) {
return getRequest().isUserInRole(role);
}
public boolean isSecure() {
return getRequest().isSecure();
}
public boolean checkNotModified(long lastModifiedTimestamp) {
if (lastModifiedTimestamp >= 0 && !this.notModified &&
(this.response == null || !this.response.containsHeader(HEADER_LAST_MODIFIED))) {
long ifModifiedSince = getRequest().getDateHeader(HEADER_IF_MODIFIED_SINCE);
this.notModified = (ifModifiedSince >= (lastModifiedTimestamp / 1000 * 1000));
if (this.response != null) {
if (this.notModified) {
this.response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
else {
this.response.setDateHeader(HEADER_LAST_MODIFIED, lastModifiedTimestamp);
}
}
}
return this.notModified;
}
public boolean isNotModified() {
return this.notModified;
}
public String getDescription(boolean includeClientInfo) {
HttpServletRequest request = getRequest();
StringBuffer buffer = new StringBuffer();
buffer.append("uri=").append(request.getRequestURI());
if (includeClientInfo) {
String client = request.getRemoteAddr();
if (StringUtils.hasLength(client)) {
buffer.append(";client=").append(client);
}
HttpSession session = request.getSession(false);
if (session != null) {
buffer.append(";session=").append(session.getId());
}
String user = request.getRemoteUser();
if (StringUtils.hasLength(user)) {
buffer.append(";user=").append(user);
}
}
return buffer.toString();
}
public String toString() {
return "ServletWebRequest: " + getDescription(true);
}
}

View File

@ -0,0 +1,101 @@
/*
* 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.web.context.request;
import org.springframework.beans.factory.ObjectFactory;
/**
* Session-backed {@link org.springframework.beans.factory.config.Scope}
* implementation.
*
* <p>Relies on a thread-bound {@link RequestAttributes} instance, which
* can be exported through {@link RequestContextListener},
* {@link org.springframework.web.filter.RequestContextFilter} or
* {@link org.springframework.web.servlet.DispatcherServlet}.
*
* <p>This <code>Scope</code> will also work for Portlet environments,
* through an alternate <code>RequestAttributes</code> implementation
* (as exposed out-of-the-box by Spring's
* {@link org.springframework.web.portlet.DispatcherPortlet}.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Rob Harrop
* @since 2.0
* @see RequestContextHolder#currentRequestAttributes()
* @see RequestAttributes#SCOPE_SESSION
* @see RequestAttributes#SCOPE_GLOBAL_SESSION
* @see RequestContextListener
* @see org.springframework.web.filter.RequestContextFilter
* @see org.springframework.web.servlet.DispatcherServlet
* @see org.springframework.web.portlet.DispatcherPortlet
*/
public class SessionScope extends AbstractRequestAttributesScope {
private final int scope;
/**
* Create a new SessionScope, storing attributes in a locally
* isolated session (or default session, if there is no distinction
* between a global session and a component-specific session).
*/
public SessionScope() {
this.scope = RequestAttributes.SCOPE_SESSION;
}
/**
* Create a new SessionScope, specifying whether to store attributes
* in the global session, provided that such a distinction is available.
* <p>This distinction is important for Portlet environments, where there
* are two notions of a session: "portlet scope" and "application scope".
* If this flag is on, objects will be put into the "application scope" session;
* else they will end up in the "portlet scope" session (the typical default).
* <p>In a Servlet environment, this flag is effectively ignored.
* @param globalSession <code>true</code> in case of the global session as target;
* <code>false</code> in case of a component-specific session as target
* @see org.springframework.web.portlet.context.PortletRequestAttributes
* @see org.springframework.web.context.request.ServletRequestAttributes
*/
public SessionScope(boolean globalSession) {
this.scope = (globalSession ? RequestAttributes.SCOPE_GLOBAL_SESSION : RequestAttributes.SCOPE_SESSION);
}
protected int getScope() {
return this.scope;
}
public String getConversationId() {
return RequestContextHolder.currentRequestAttributes().getSessionId();
}
public Object get(String name, ObjectFactory objectFactory) {
Object mutex = RequestContextHolder.currentRequestAttributes().getSessionMutex();
synchronized (mutex) {
return super.get(name, objectFactory);
}
}
public Object remove(String name) {
Object mutex = RequestContextHolder.currentRequestAttributes().getSessionMutex();
synchronized (mutex) {
return super.remove(name);
}
}
}

View File

@ -0,0 +1,129 @@
/*
* 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.web.context.request;
import java.security.Principal;
import java.util.Locale;
import java.util.Map;
/**
* Generic interface for a web request. Mainly intended for generic web
* request interceptors, giving them access to general request metadata,
* not for actual handling of the request.
*
* @author Juergen Hoeller
* @since 2.0
* @see WebRequestInterceptor
*/
public interface WebRequest extends RequestAttributes {
/**
* Return the request parameter of the given name, or <code>null</code> if none.
* <p>Retrieves the first parameter value in case of a multi-value parameter.
* @see javax.servlet.http.HttpServletRequest#getParameter(String)
*/
String getParameter(String paramName);
/**
* Return the request parameter values for the given parameter name,
* or <code>null</code> if none.
* <p>A single-value parameter will be exposed as an array with a single element.
* @see javax.servlet.http.HttpServletRequest#getParameterValues(String)
*/
String[] getParameterValues(String paramName);
/**
* Return a immutable Map of the request parameters, with parameter names as map keys
* and parameter values as map values. The map values will be of type String array.
* <p>A single-value parameter will be exposed as an array with a single element.
* @see javax.servlet.http.HttpServletRequest#getParameterMap()
*/
Map getParameterMap();
/**
* Return the primary Locale for this request.
* @see javax.servlet.http.HttpServletRequest#getLocale()
*/
Locale getLocale();
/**
* Return the context path for this request
* (usually the base path that the current web application is mapped to).
* @see javax.servlet.http.HttpServletRequest#getContextPath()
*/
String getContextPath();
/**
* Return the remote user for this request, if any.
* @see javax.servlet.http.HttpServletRequest#getRemoteUser()
*/
String getRemoteUser();
/**
* Return the user principal for this request, if any.
* @see javax.servlet.http.HttpServletRequest#getUserPrincipal()
*/
Principal getUserPrincipal();
/**
* Determine whether the user is in the given role for this request.
* @see javax.servlet.http.HttpServletRequest#isUserInRole(String)
*/
boolean isUserInRole(String role);
/**
* Return whether this request has been sent over a secure transport
* mechanism (such as SSL).
* @see javax.servlet.http.HttpServletRequest#isSecure()
*/
boolean isSecure();
/**
* Check whether the request qualifies as not modified given the
* supplied last-modified timestamp (as determined by the application).
* <p>This will also transparently set the appropriate response headers,
* for both the modified case and the not-modified case.
* <p>Typical usage:
* <pre class="code">
* public String myHandleMethod(WebRequest webRequest, Model model) {
* long lastModified = // application-specific calculation
* if (request.checkNotModified(lastModified)) {
* // shortcut exit - no further processing necessary
* return null;
* }
* // further request processing, actually building content
* model.addAttribute(...);
* return "myViewName";
* }</pre>
* @param lastModifiedTimestamp the last-modified timestamp that
* the application determined for the underlying resource
* @return whether the request qualifies as not modified,
* allowing to abort request processing and relying on the response
* telling the client that the content has not been modified
*/
boolean checkNotModified(long lastModifiedTimestamp);
/**
* Get a short description of this request,
* typically containing request URI and session id.
* @param includeClientInfo whether to include client-specific
* information such as session id and user name
* @return the requested description as String
*/
String getDescription(boolean includeClientInfo);
}

View File

@ -0,0 +1,89 @@
/*
* 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.web.context.request;
import org.springframework.ui.ModelMap;
/**
* Interface for general web request interception. Allows for being applied
* to Servlet request as well as Portlet request environments, through
* building on the {@link WebRequest} abstraction.
*
* <p>This interface assumes MVC-style request processing: A handler gets executed,
* exposes a set of model objects, then a view gets rendered based on that model.
* Alternatively, a handler may also process the request completely, with no
* view to be rendered.
*
* <p>This interface is deliberatly minimalistic to keep the dependencies of
* generic request interceptors as minimal as feasible.
*
* <p><b>NOTE:</b> While this interceptor is applied to the entire request processing
* in a Servlet environment, it is by default only applied to the <i>render</i> phase
* in a Portlet environment, preparing and rendering a Portlet view. To apply
* WebRequestInterceptors to the <i>action</i> phase as well, set the HandlerMapping's
* "applyWebRequestInterceptorsToRenderPhaseOnly" flag to "false". Alternatively,
* consider using the Portlet-specific HandlerInterceptor mechanism for such needs.
*
* @author Juergen Hoeller
* @since 2.0
* @see ServletWebRequest
* @see org.springframework.web.servlet.DispatcherServlet
* @see org.springframework.web.servlet.handler.AbstractHandlerMapping#setInterceptors
* @see org.springframework.web.servlet.HandlerInterceptor
* @see org.springframework.web.portlet.context.PortletWebRequest
* @see org.springframework.web.portlet.DispatcherPortlet
* @see org.springframework.web.portlet.handler.AbstractHandlerMapping#setInterceptors
* @see org.springframework.web.portlet.handler.AbstractHandlerMapping#setApplyWebRequestInterceptorsToRenderPhaseOnly
* @see org.springframework.web.portlet.HandlerInterceptor
*/
public interface WebRequestInterceptor {
/**
* Intercept the execution of a request handler <i>before</i> its invocation.
* <p>Allows for preparing context resources (such as a Hibernate Session)
* and expose them as request attributes or as thread-local objects.
* @param request the current web request
* @throws Exception in case of errors
*/
void preHandle(WebRequest request) throws Exception;
/**
* Intercept the execution of a request handler <i>after</i> its successful
* invocation, right before view rendering (if any).
* <p>Allows for modifying context resources after successful handler
* execution (for example, flushing a Hibernate Session).
* @param request the current web request
* @param model the map of model objects that will be exposed to the view
* (may be <code>null</code>). Can be used to analyze the exposed model
* and/or to add further model attributes, if desired.
* @throws Exception in case of errors
*/
void postHandle(WebRequest request, ModelMap model) throws Exception;
/**
* Callback after completion of request processing, that is, after rendering
* the view. Will be called on any outcome of handler execution, thus allows
* for proper resource cleanup.
* <p>Note: Will only be called if this interceptor's <code>preHandle</code>
* method has successfully completed!
* @param request the current web request
* @param ex exception thrown on handler execution, if any
* @throws Exception in case of errors
*/
void afterCompletion(WebRequest request, Exception ex) throws Exception;
}

View File

@ -0,0 +1,8 @@
<html>
<body>
Support for generic request context holding, in particular for
scoping of application objects per HTTP request or HTTP session.
</body>
</html>

View File

@ -0,0 +1,173 @@
/*
* 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.web.context.support;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.support.AbstractRefreshableConfigApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.ui.context.Theme;
import org.springframework.ui.context.ThemeSource;
import org.springframework.ui.context.support.UiApplicationContextUtils;
import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.ServletConfigAware;
import org.springframework.web.context.ServletContextAware;
/**
* {@link org.springframework.context.support.AbstractRefreshableApplicationContext}
* subclass which implements the
* {@link org.springframework.web.context.ConfigurableWebApplicationContext}
* interface for web environments. Provides a "configLocations" property,
* to be populated through the ConfigurableWebApplicationContext interface
* on web application startup.
*
* <p>This class is as easy to subclass as AbstractRefreshableApplicationContext:
* All you need to implements is the {@link #loadBeanDefinitions} method;
* see the superclass javadoc for details. Note that implementations are supposed
* to load bean definitions from the files specified by the locations returned
* by the {@link #getConfigLocations} method.
*
* <p>Interprets resource paths as servlet context resources, i.e. as paths beneath
* the web application root. Absolute paths, e.g. for files outside the web app root,
* can be accessed via "file:" URLs, as implemented by
* {@link org.springframework.core.io.DefaultResourceLoader}.
*
* <p>In addition to the special beans detected by
* {@link org.springframework.context.support.AbstractApplicationContext},
* this class detects a bean of type {@link org.springframework.ui.context.ThemeSource}
* in the context, under the special bean name "themeSource".
*
* <p><b>This is the web context to be subclassed for a different bean definition format.</b>
* Such a context implementation can be specified as "contextClass" context-param
* for {@link org.springframework.web.context.ContextLoader} or as "contextClass"
* init-param for {@link org.springframework.web.servlet.FrameworkServlet},
* replacing the default {@link XmlWebApplicationContext}. It will then automatically
* receive the "contextConfigLocation" context-param or init-param, respectively.
*
* <p>Note that WebApplicationContext implementations are generally supposed
* to configure themselves based on the configuration received through the
* {@link ConfigurableWebApplicationContext} interface. In contrast, a standalone
* application context might allow for configuration in custom startup code
* (for example, {@link org.springframework.context.support.GenericApplicationContext}).
*
* @author Juergen Hoeller
* @since 1.1.3
* @see #loadBeanDefinitions
* @see org.springframework.web.context.ConfigurableWebApplicationContext#setConfigLocations
* @see org.springframework.ui.context.ThemeSource
* @see XmlWebApplicationContext
*/
public abstract class AbstractRefreshableWebApplicationContext extends AbstractRefreshableConfigApplicationContext
implements ConfigurableWebApplicationContext, ThemeSource {
/** Servlet context that this context runs in */
private ServletContext servletContext;
/** Servlet config that this context runs in, if any */
private ServletConfig servletConfig;
/** Namespace of this context, or <code>null</code> if root */
private String namespace;
/** the ThemeSource for this ApplicationContext */
private ThemeSource themeSource;
public AbstractRefreshableWebApplicationContext() {
setDisplayName("Root WebApplicationContext");
}
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
public ServletContext getServletContext() {
return this.servletContext;
}
public void setServletConfig(ServletConfig servletConfig) {
this.servletConfig = servletConfig;
if (servletConfig != null && this.servletContext == null) {
this.servletContext = servletConfig.getServletContext();
}
}
public ServletConfig getServletConfig() {
return this.servletConfig;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
if (namespace != null) {
setDisplayName("WebApplicationContext for namespace '" + namespace + "'");
}
}
public String getNamespace() {
return this.namespace;
}
public String[] getConfigLocations() {
return super.getConfigLocations();
}
/**
* Register request/session scopes, a {@link ServletContextAwareProcessor}, etc.
*/
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
beanFactory.registerResolvableDependency(ServletContext.class, this.servletContext);
beanFactory.registerResolvableDependency(ServletConfig.class, this.servletConfig);
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory);
}
/**
* This implementation supports file paths beneath the root of the ServletContext.
* @see ServletContextResource
*/
protected Resource getResourceByPath(String path) {
return new ServletContextResource(this.servletContext, path);
}
/**
* This implementation supports pattern matching in unexpanded WARs too.
* @see ServletContextResourcePatternResolver
*/
protected ResourcePatternResolver getResourcePatternResolver() {
return new ServletContextResourcePatternResolver(this);
}
/**
* Initialize the theme capability.
*/
protected void onRefresh() {
this.themeSource = UiApplicationContextUtils.initThemeSource(this);
}
public Theme getTheme(String themeName) {
return this.themeSource.getTheme(themeName);
}
}

View File

@ -0,0 +1,99 @@
/*
* 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.web.context.support;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.springframework.util.Assert;
import org.springframework.web.context.WebApplicationContext;
/**
* HttpServletRequest decorator that makes all Spring beans in a
* given WebApplicationContext accessible as request attributes,
* through lazy checking once an attribute gets accessed.
*
* @author Juergen Hoeller
* @since 2.5
*/
public class ContextExposingHttpServletRequest extends HttpServletRequestWrapper {
private final WebApplicationContext webApplicationContext;
private final Set exposedContextBeanNames;
private Set explicitAttributes;
/**
* Create a new ContextExposingHttpServletRequest for the given request.
* @param originalRequest the original HttpServletRequest
* @param context the WebApplicationContext that this request runs in
*/
public ContextExposingHttpServletRequest(HttpServletRequest originalRequest, WebApplicationContext context) {
this(originalRequest, context, null);
}
/**
* Create a new ContextExposingHttpServletRequest for the given request.
* @param originalRequest the original HttpServletRequest
* @param context the WebApplicationContext that this request runs in
* @param exposedContextBeanNames the names of beans in the context which
* are supposed to be exposed (if this is non-null, only the beans in this
* Set are eligible for exposure as attributes)
*/
public ContextExposingHttpServletRequest(
HttpServletRequest originalRequest, WebApplicationContext context, Set exposedContextBeanNames) {
super(originalRequest);
Assert.notNull(context, "WebApplicationContext must not be null");
this.webApplicationContext = context;
this.exposedContextBeanNames = exposedContextBeanNames;
}
/**
* Return the WebApplicationContext that this request runs in.
*/
public final WebApplicationContext getWebApplicationContext() {
return this.webApplicationContext;
}
public Object getAttribute(String name) {
if ((this.explicitAttributes == null || !this.explicitAttributes.contains(name)) &&
(this.exposedContextBeanNames == null || this.exposedContextBeanNames.contains(name)) &&
this.webApplicationContext.containsBean(name)) {
return this.webApplicationContext.getBean(name);
}
else {
return super.getAttribute(name);
}
}
public void setAttribute(String name, Object value) {
super.setAttribute(name, value);
if (this.explicitAttributes == null) {
this.explicitAttributes = new HashSet(8);
}
this.explicitAttributes.add(name);
}
}

View File

@ -0,0 +1,137 @@
/*
* 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.web.context.support;
import javax.servlet.ServletContext;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.ui.context.Theme;
import org.springframework.ui.context.ThemeSource;
import org.springframework.ui.context.support.UiApplicationContextUtils;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.context.WebApplicationContext;
/**
* Subclass of {@link GenericApplicationContext}, suitable for web environments.
*
* <p>Implements the {@link WebApplicationContext} interface, but not
* {@link org.springframework.web.context.ConfigurableWebApplicationContext},
* as it is not intended for declarative setup in <code>web.xml</code>. Instead,
* it is designed for programmatic setup, for example for building nested contexts.
*
* <p><b>If you intend to implement a WebApplicationContext that reads bean definitions
* from configuration files, consider deriving from AbstractRefreshableWebApplicationContext,
* reading the bean definitions in an implementation of the <code>loadBeanDefinitions</code>
* method.</b>
*
* <p>Interprets resource paths as servlet context resources, i.e. as paths beneath
* the web application root. Absolute paths, e.g. for files outside the web app root,
* can be accessed via "file:" URLs, as implemented by AbstractApplicationContext.
*
* <p>In addition to the special beans detected by
* {@link org.springframework.context.support.AbstractApplicationContext},
* this class detects a ThemeSource bean in the context, with the name "themeSource".
*
* @author Juergen Hoeller
* @since 1.2
*/
public class GenericWebApplicationContext extends GenericApplicationContext
implements WebApplicationContext, ThemeSource {
private ServletContext servletContext;
private ThemeSource themeSource;
/**
* Create a new GenericWebApplicationContext.
* @see #setServletContext
* @see #registerBeanDefinition
* @see #refresh
*/
public GenericWebApplicationContext() {
super();
}
/**
* Create a new GenericWebApplicationContext with the given DefaultListableBeanFactory.
* @param beanFactory the DefaultListableBeanFactory instance to use for this context
* @see #setServletContext
* @see #registerBeanDefinition
* @see #refresh
*/
public GenericWebApplicationContext(DefaultListableBeanFactory beanFactory) {
super(beanFactory);
}
/**
* Set the ServletContext that this WebApplicationContext runs in.
*/
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
public ServletContext getServletContext() {
return this.servletContext;
}
/**
* Register ServletContextAwareProcessor.
* @see ServletContextAwareProcessor
*/
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext));
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
beanFactory.registerResolvableDependency(ServletContext.class, this.servletContext);
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory);
}
/**
* This implementation supports file paths beneath the root of the ServletContext.
* @see ServletContextResource
*/
protected Resource getResourceByPath(String path) {
return new ServletContextResource(this.servletContext, path);
}
/**
* This implementation supports pattern matching in unexpanded WARs too.
* @see ServletContextResourcePatternResolver
*/
protected ResourcePatternResolver getResourcePatternResolver() {
return new ServletContextResourcePatternResolver(this);
}
/**
* Initialize the theme capability.
*/
protected void onRefresh() {
this.themeSource = UiApplicationContextUtils.initThemeSource(this);
}
public Theme getTheme(String themeName) {
return this.themeSource.getTheme(themeName);
}
}

View File

@ -0,0 +1,79 @@
/*
* 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.web.context.support;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.context.WebApplicationContext;
/**
* Simple HttpServlet that delegates to an {@link HttpRequestHandler} bean defined
* in Spring's root web application context. The target bean name must match the
* HttpRequestHandlerServlet servlet-name as defined in <code>web.xml</code>.
*
* <p>This can for example be used to expose a single Spring remote exporter,
* such as {@link org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter}
* or {@link org.springframework.remoting.caucho.HessianServiceExporter},
* per HttpRequestHandlerServlet definition. This is a minimal alternative
* to defining remote exporters as beans in a DispatcherServlet context
* (with advanced mapping and interception facilities being available there).
*
* @author Juergen Hoeller
* @since 2.0
* @see org.springframework.web.HttpRequestHandler
* @see org.springframework.web.servlet.DispatcherServlet
*/
public class HttpRequestHandlerServlet extends HttpServlet {
private HttpRequestHandler target;
public void init() throws ServletException {
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
this.target = (HttpRequestHandler) wac.getBean(getServletName(), HttpRequestHandler.class);
}
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
LocaleContextHolder.setLocale(request.getLocale());
try {
this.target.handleRequest(request, response);
}
catch (HttpRequestMethodNotSupportedException ex) {
String[] supportedMethods = ((HttpRequestMethodNotSupportedException) ex).getSupportedMethods();
if (supportedMethods != null) {
response.setHeader("Allow", StringUtils.arrayToDelimitedString(supportedMethods, ", "));
}
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, ex.getMessage());
}
finally {
LocaleContextHolder.resetLocaleContext();
}
}
}

View File

@ -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.web.context.support;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.util.ResponseTimeMonitorImpl;
/**
* Listener that logs the response times of web requests.
* To be registered as bean in a WebApplicationContext.
*
* <p>Logs performance statistics using Commons Logging at "trace" level.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since January 21, 2001
* @see RequestHandledEvent
* @deprecated as of Spring 2.5, to be removed in Spring 3.0.
* Use a custom ApplicationListener specific to your needs instead.
*/
public class PerformanceMonitorListener implements ApplicationListener {
protected final Log logger = LogFactory.getLog(getClass());
protected final ResponseTimeMonitorImpl responseTimeMonitor = new ResponseTimeMonitorImpl();
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof RequestHandledEvent) {
RequestHandledEvent rhe = (RequestHandledEvent) event;
this.responseTimeMonitor.recordResponseTime(rhe.getProcessingTimeMillis());
if (logger.isTraceEnabled()) {
logger.trace("PerformanceMonitorListener: last=[" + rhe.getProcessingTimeMillis() + "ms]; " +
this.responseTimeMonitor + "; " + rhe.getShortDescription());
}
}
}
}

View File

@ -0,0 +1,157 @@
/*
* 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.web.context.support;
import org.springframework.context.ApplicationEvent;
/**
* Event raised when a request is handled within an ApplicationContext.
*
* <p>Supported by Spring's own FrameworkServlet (through a specific
* ServletRequestHandledEvent subclass), but can also be raised by any
* other web component. Used, for example, by Spring's out-of-the-box
* PerformanceMonitorListener.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since January 17, 2001
* @see ServletRequestHandledEvent
* @see PerformanceMonitorListener
* @see org.springframework.web.servlet.FrameworkServlet
* @see org.springframework.context.ApplicationContext#publishEvent
*/
public class RequestHandledEvent extends ApplicationEvent {
/** Session id that applied to the request, if any */
private String sessionId;
/** Usually the UserPrincipal */
private String userName;
/** Request processing time */
private final long processingTimeMillis;
/** Cause of failure, if any */
private Throwable failureCause;
/**
* Create a new RequestHandledEvent with session information.
* @param source the component that published the event
* @param sessionId the id of the HTTP session, if any
* @param userName the name of the user that was associated with the
* request, if any (usually the UserPrincipal)
* @param processingTimeMillis the processing time of the request in milliseconds
*/
public RequestHandledEvent(Object source, String sessionId, String userName, long processingTimeMillis) {
super(source);
this.sessionId = sessionId;
this.userName = userName;
this.processingTimeMillis = processingTimeMillis;
}
/**
* Create a new RequestHandledEvent with session information.
* @param source the component that published the event
* @param sessionId the id of the HTTP session, if any
* @param userName the name of the user that was associated with the
* request, if any (usually the UserPrincipal)
* @param processingTimeMillis the processing time of the request in milliseconds
* @param failureCause the cause of failure, if any
*/
public RequestHandledEvent(
Object source, String sessionId, String userName, long processingTimeMillis, Throwable failureCause) {
this(source, sessionId, userName, processingTimeMillis);
this.failureCause = failureCause;
}
/**
* Return the processing time of the request in milliseconds.
*/
public long getProcessingTimeMillis() {
return this.processingTimeMillis;
}
/**
* Return the id of the HTTP session, if any.
*/
public String getSessionId() {
return this.sessionId;
}
/**
* Return the name of the user that was associated with the request
* (usually the UserPrincipal).
* @see javax.servlet.http.HttpServletRequest#getUserPrincipal()
*/
public String getUserName() {
return this.userName;
}
/**
* Return whether the request failed.
*/
public boolean wasFailure() {
return (this.failureCause != null);
}
/**
* Return the cause of failure, if any.
*/
public Throwable getFailureCause() {
return this.failureCause;
}
/**
* Return a short description of this event, only involving
* the most important context data.
*/
public String getShortDescription() {
StringBuffer sb = new StringBuffer();
sb.append("session=[").append(this.sessionId).append("]; ");
sb.append("user=[").append(this.userName).append("]; ");
return sb.toString();
}
/**
* Return a full description of this event, involving
* all available context data.
*/
public String getDescription() {
StringBuffer sb = new StringBuffer();
sb.append("session=[").append(this.sessionId).append("]; ");
sb.append("user=[").append(this.userName).append("]; ");
sb.append("time=[").append(this.processingTimeMillis).append("ms]; ");
sb.append("status=[");
if (!wasFailure()) {
sb.append("OK");
}
else {
sb.append("failed: ").append(this.failureCause);
}
sb.append(']');
return sb.toString();
}
public String toString() {
return ("RequestHandledEvent: " + getDescription());
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright 2002-2005 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.web.context.support;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.ServletContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.context.ServletContextAware;
/**
* Exporter that takes Spring-defined objects and exposes them as
* ServletContext attributes. Usually, bean references will be used
* to export Spring-defined beans as ServletContext attributes.
*
* <p>Useful to make Spring-defined beans available to code that is
* not aware of Spring at all, but rather just of the Servlet API.
* Client code can then use plain ServletContext attribute lookups
* to access those objects, despite them being defined in a Spring
* application context.
*
* <p>Alternatively, consider using the WebApplicationContextUtils
* class to access Spring-defined beans via the WebApplicationContext
* interface. This makes client code aware of Spring API, of course.
*
* @author Juergen Hoeller
* @since 1.1.4
* @see javax.servlet.ServletContext#getAttribute
* @see WebApplicationContextUtils#getWebApplicationContext
*/
public class ServletContextAttributeExporter implements ServletContextAware {
protected final Log logger = LogFactory.getLog(getClass());
private Map attributes;
/**
* Set the ServletContext attributes to expose as key-value pairs.
* Each key will be considered a ServletContext attributes key,
* and each value will be used as corresponding attribute value.
* <p>Usually, you will use bean references for the values,
* to export Spring-defined beans as ServletContext attributes.
* Of course, it is also possible to define plain values to export.
* @param attributes Map with String keys and Object values
*/
public void setAttributes(Map attributes) {
this.attributes = attributes;
}
public void setServletContext(ServletContext servletContext) {
for (Iterator it = this.attributes.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
String attributeName = (String) entry.getKey();
if (logger.isWarnEnabled()) {
if (servletContext.getAttribute(attributeName) != null) {
logger.warn("Overwriting existing ServletContext attribute with name '" + attributeName + "'");
}
}
servletContext.setAttribute(attributeName, entry.getValue());
if (logger.isInfoEnabled()) {
logger.info("Exported ServletContext attribute with name '" + attributeName + "'");
}
}
}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright 2002-2005 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.web.context.support;
import javax.servlet.ServletContext;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.web.context.ServletContextAware;
/**
* FactoryBean that fetches a specific, existing ServletContext attribute.
* Exposes that ServletContext attribute when used as bean reference,
* effectively making it available as named Spring bean instance.
*
* <p>Intended to link in ServletContext attributes that exist before
* the startup of the Spring application context. Typically, such
* attributes will have been put there by third-party web frameworks.
* In a purely Spring-based web application, no such linking in of
* ServletContext attrutes will be necessary.
*
* @author Juergen Hoeller
* @since 1.1.4
* @see ServletContextParameterFactoryBean
*/
public class ServletContextAttributeFactoryBean implements FactoryBean, ServletContextAware {
private String attributeName;
private Object attribute;
/**
* Set the name of the ServletContext attribute to expose.
*/
public void setAttributeName(String attributeName) {
this.attributeName = attributeName;
}
public void setServletContext(ServletContext servletContext) {
if (this.attributeName == null) {
throw new IllegalArgumentException("attributeName is required");
}
this.attribute = servletContext.getAttribute(this.attributeName);
if (this.attribute == null) {
throw new IllegalStateException("No ServletContext attribute '" + this.attributeName + "' found");
}
}
public Object getObject() throws Exception {
return this.attribute;
}
public Class getObjectType() {
return (this.attribute != null ? this.attribute.getClass() : null);
}
public boolean isSingleton() {
return true;
}
}

View File

@ -0,0 +1,87 @@
/*
* 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.web.context.support;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.web.context.ServletConfigAware;
import org.springframework.web.context.ServletContextAware;
/**
* {@link org.springframework.beans.factory.config.BeanPostProcessor}
* implementation that passes the ServletContext to beans that implement
* the {@link ServletContextAware} interface.
*
* <p>Web application contexts will automatically register this with their
* underlying bean factory. Applications do not use this directly.
*
* @author Juergen Hoeller
* @since 12.03.2004
* @see org.springframework.web.context.ServletContextAware
* @see org.springframework.web.context.support.XmlWebApplicationContext#postProcessBeanFactory
*/
public class ServletContextAwareProcessor implements BeanPostProcessor {
private ServletContext servletContext;
private ServletConfig servletConfig;
/**
* Create a new ServletContextAwareProcessor for the given context.
*/
public ServletContextAwareProcessor(ServletContext servletContext) {
this(servletContext, null);
}
/**
* Create a new ServletContextAwareProcessor for the given config.
*/
public ServletContextAwareProcessor(ServletConfig servletConfig) {
this(null, servletConfig);
}
/**
* Create a new ServletContextAwareProcessor for the given context and config.
*/
public ServletContextAwareProcessor(ServletContext servletContext, ServletConfig servletConfig) {
this.servletContext = servletContext;
this.servletConfig = servletConfig;
if (servletContext == null && servletConfig != null) {
this.servletContext = servletConfig.getServletContext();
}
}
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (this.servletContext != null && bean instanceof ServletContextAware) {
((ServletContextAware) bean).setServletContext(this.servletContext);
}
if (this.servletConfig != null && bean instanceof ServletConfigAware) {
((ServletConfigAware) bean).setServletConfig(this.servletConfig);
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
}

View File

@ -0,0 +1,65 @@
/*
* 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.web.context.support;
import javax.servlet.ServletContext;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.web.context.ServletContextAware;
/**
* Simple FactoryBean that exposes the ServletContext for bean references.
* Can be used as alternative to implementing the ServletContextAware
* callback interface. Allows for passing the ServletContext reference
* to a constructor argument or any custom bean property.
*
* <p>Note that there's a special FactoryBean for exposing a specific
* ServletContext attribute, named ServletContextAttributeFactoryBean.
* So if all you need from the ServletContext is access to a specific
* attribute, ServletContextAttributeFactoryBean allows you to expose
* a constructor argument or bean property of the attribute type,
* which is a preferable to a dependency on the full ServletContext.
*
* @author Juergen Hoeller
* @since 1.1.4
* @see javax.servlet.ServletContext
* @see org.springframework.web.context.ServletContextAware
* @see ServletContextAttributeFactoryBean
*/
public class ServletContextFactoryBean implements FactoryBean, ServletContextAware {
private ServletContext servletContext;
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
public Object getObject() {
return this.servletContext;
}
public Class getObjectType() {
return (this.servletContext != null ? this.servletContext.getClass() : ServletContext.class);
}
public boolean isSingleton() {
return true;
}
}

View File

@ -0,0 +1,71 @@
/*
* Copyright 2002-2005 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.web.context.support;
import javax.servlet.ServletContext;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.web.context.ServletContextAware;
/**
* FactoryBean that retrieves a specific ServletContext init parameter
* (that is, a "context-param" defined in <code>web.xml</code>).
* Exposes that ServletContext init parameter when used as bean reference,
* effectively making it available as named Spring bean instance.
*
* @author Juergen Hoeller
* @since 1.2.4
* @see ServletContextAttributeFactoryBean
*/
public class ServletContextParameterFactoryBean implements FactoryBean, ServletContextAware {
private String initParamName;
private String paramValue;
/**
* Set the name of the ServletContext init parameter to expose.
*/
public void setInitParamName(String initParamName) {
this.initParamName = initParamName;
}
public void setServletContext(ServletContext servletContext) {
if (this.initParamName == null) {
throw new IllegalArgumentException("initParamName is required");
}
this.paramValue = servletContext.getInitParameter(this.initParamName);
if (this.paramValue == null) {
throw new IllegalStateException("No ServletContext init parameter '" + this.initParamName + "' found");
}
}
public Object getObject() throws Exception {
return this.paramValue;
}
public Class getObjectType() {
return String.class;
}
public boolean isSingleton() {
return true;
}
}

View File

@ -0,0 +1,158 @@
/*
* Copyright 2002-2005 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.web.context.support;
import java.util.Properties;
import javax.servlet.ServletContext;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.web.context.ServletContextAware;
/**
* Subclass of PropertyPlaceholderConfigurer that resolves placeholders as
* ServletContext init parameters (that is, <code>web.xml</code> context-param
* entries).
*
* <p>Can be combined with "locations" and/or "properties" values in addition
* to web.xml context-params. Alternatively, can be defined without local
* properties, to resolve all placeholders as <code>web.xml</code> context-params
* (or JVM system properties).
*
* <p>If a placeholder could not be resolved against the provided local
* properties within the application, this configurer will fall back to
* ServletContext parameters. Can also be configured to let ServletContext
* init parameters override local properties (contextOverride=true).
*
* <p>Optionally supports searching for ServletContext <i>attributes</i>: If turned
* on, an otherwise unresolvable placeholder will matched against the corresponding
* ServletContext attribute, using its stringified value if found. This can be
* used to feed dynamic values into Spring's placeholder resolution.
*
* <p>If not running within a WebApplicationContext (or any other context that
* is able to satisfy the ServletContextAware callback), this class will behave
* like the default PropertyPlaceholderConfigurer. This allows for keeping
* ServletContextPropertyPlaceholderConfigurer definitions in test suites.
*
* @author Juergen Hoeller
* @since 1.1.4
* @see #setLocations
* @see #setProperties
* @see #setSystemPropertiesModeName
* @see #setContextOverride
* @see #setSearchContextAttributes
* @see javax.servlet.ServletContext#getInitParameter(String)
* @see javax.servlet.ServletContext#getAttribute(String)
*/
public class ServletContextPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer
implements ServletContextAware {
private boolean contextOverride = false;
private boolean searchContextAttributes = false;
private ServletContext servletContext;
/**
* Set whether ServletContext init parameters (and optionally also ServletContext
* attributes) should override local properties within the application.
* Default is "false": ServletContext settings serve as fallback.
* <p>Note that system properties will still override ServletContext settings,
* if the system properties mode is set to "SYSTEM_PROPERTIES_MODE_OVERRIDE".
* @see #setSearchContextAttributes
* @see #setSystemPropertiesModeName
* @see #SYSTEM_PROPERTIES_MODE_OVERRIDE
*/
public void setContextOverride(boolean contextOverride) {
this.contextOverride = contextOverride;
}
/**
* Set whether to search for matching a ServletContext attribute before
* checking a ServletContext init parameter. Default is "false": only
* checking init parameters.
* <p>If turned on, the configurer will look for a ServletContext attribute with
* the same name as the placeholder, and use its stringified value if found.
* Exposure of such ServletContext attributes can be used to dynamically override
* init parameters defined in <code>web.xml</code>, for example in a custom
* context listener.
* @see javax.servlet.ServletContext#getInitParameter(String)
* @see javax.servlet.ServletContext#getAttribute(String)
*/
public void setSearchContextAttributes(boolean searchContextAttributes) {
this.searchContextAttributes = searchContextAttributes;
}
/**
* Set the ServletContext to resolve placeholders against.
* Will be auto-populated when running in a WebApplicationContext.
* <p>If not set, this configurer will simply not resolve placeholders
* against the ServletContext: It will effectively behave like a plain
* PropertyPlaceholderConfigurer in such a scenario.
*/
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
protected String resolvePlaceholder(String placeholder, Properties props) {
String value = null;
if (this.contextOverride && this.servletContext != null) {
value = resolvePlaceholder(placeholder, this.servletContext, this.searchContextAttributes);
}
if (value == null) {
value = super.resolvePlaceholder(placeholder, props);
}
if (value == null && this.servletContext != null) {
value = resolvePlaceholder(placeholder, this.servletContext, this.searchContextAttributes);
}
return value;
}
/**
* Resolves the given placeholder using the init parameters
* and optionally also the attributes of the given ServletContext.
* <p>Default implementation checks ServletContext attributes before
* init parameters. Can be overridden to customize this behavior,
* potentially also applying specific naming patterns for parameters
* and/or attributes (instead of using the exact placeholder name).
* @param placeholder the placeholder to resolve
* @param servletContext the ServletContext to check
* @param searchContextAttributes whether to search for a matching
* ServletContext attribute
* @return the resolved value, of null if none
* @see javax.servlet.ServletContext#getInitParameter(String)
* @see javax.servlet.ServletContext#getAttribute(String)
*/
protected String resolvePlaceholder(
String placeholder, ServletContext servletContext, boolean searchContextAttributes) {
String value = null;
if (searchContextAttributes) {
Object attrValue = servletContext.getAttribute(placeholder);
if (attrValue != null) {
value = attrValue.toString();
}
}
if (value == null) {
value = servletContext.getInitParameter(placeholder);
}
return value;
}
}

View File

@ -0,0 +1,200 @@
/*
* 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.web.context.support;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import javax.servlet.ServletContext;
import org.springframework.core.io.AbstractResource;
import org.springframework.core.io.ContextResource;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.util.WebUtils;
/**
* {@link org.springframework.core.io.Resource} implementation for
* {@link javax.servlet.ServletContext} resources, interpreting
* relative paths within the web application root directory.
*
* <p>Always supports stream access and URL access, but only allows
* <code>java.io.File</code> access when the web application archive
* is expanded.
*
* @author Juergen Hoeller
* @since 28.12.2003
* @see javax.servlet.ServletContext#getResourceAsStream
* @see javax.servlet.ServletContext#getResource
* @see javax.servlet.ServletContext#getRealPath
*/
public class ServletContextResource extends AbstractResource implements ContextResource {
private final ServletContext servletContext;
private final String path;
/**
* Create a new ServletContextResource.
* <p>The Servlet spec requires that resource paths start with a slash,
* even if many containers accept paths without leading slash too.
* Consequently, the given path will be prepended with a slash if it
* doesn't already start with one.
* @param servletContext the ServletContext to load from
* @param path the path of the resource
*/
public ServletContextResource(ServletContext servletContext, String path) {
// check ServletContext
Assert.notNull(servletContext, "Cannot resolve ServletContextResource without ServletContext");
this.servletContext = servletContext;
// check path
Assert.notNull(path, "Path is required");
String pathToUse = StringUtils.cleanPath(path);
if (!pathToUse.startsWith("/")) {
pathToUse = "/" + pathToUse;
}
this.path = pathToUse;
}
/**
* Return the ServletContext for this resource.
*/
public final ServletContext getServletContext() {
return this.servletContext;
}
/**
* Return the path for this resource.
*/
public final String getPath() {
return this.path;
}
/**
* This implementation checks <code>ServletContext.getResource</code>.
* @see javax.servlet.ServletContext#getResource(String)
*/
public boolean exists() {
try {
URL url = this.servletContext.getResource(this.path);
return (url != null);
}
catch (MalformedURLException ex) {
return false;
}
}
/**
* This implementation delegates to <code>ServletContext.getResourceAsStream</code>,
* but throws a FileNotFoundException if no resource found.
* @see javax.servlet.ServletContext#getResourceAsStream(String)
*/
public InputStream getInputStream() throws IOException {
InputStream is = this.servletContext.getResourceAsStream(this.path);
if (is == null) {
throw new FileNotFoundException("Could not open " + getDescription());
}
return is;
}
/**
* This implementation delegates to <code>ServletContext.getResource</code>,
* but throws a FileNotFoundException if no resource found.
* @see javax.servlet.ServletContext#getResource(String)
*/
public URL getURL() throws IOException {
URL url = this.servletContext.getResource(this.path);
if (url == null) {
throw new FileNotFoundException(
getDescription() + " cannot be resolved to URL because it does not exist");
}
return url;
}
/**
* This implementation delegates to <code>ServletContext.getRealPath</code>,
* but throws a FileNotFoundException if not found or not resolvable.
* @see javax.servlet.ServletContext#getRealPath(String)
*/
public File getFile() throws IOException {
String realPath = WebUtils.getRealPath(this.servletContext, this.path);
return new File(realPath);
}
/**
* This implementation creates a ServletContextResource, applying the given path
* relative to the path of the underlying file of this resource descriptor.
* @see org.springframework.util.StringUtils#applyRelativePath(String, String)
*/
public Resource createRelative(String relativePath) {
String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);
return new ServletContextResource(this.servletContext, pathToUse);
}
/**
* This implementation returns the name of the file that this ServletContext
* resource refers to.
* @see org.springframework.util.StringUtils#getFilename(String)
*/
public String getFilename() {
return StringUtils.getFilename(this.path);
}
/**
* This implementation returns a description that includes the ServletContext
* resource location.
*/
public String getDescription() {
return "ServletContext resource [" + this.path + "]";
}
public String getPathWithinContext() {
return this.path;
}
/**
* This implementation compares the underlying ServletContext resource locations.
*/
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof ServletContextResource) {
ServletContextResource otherRes = (ServletContextResource) obj;
return (this.servletContext.equals(otherRes.servletContext) && this.path.equals(otherRes.path));
}
return false;
}
/**
* This implementation returns the hash code of the underlying
* ServletContext resource location.
*/
public int hashCode() {
return this.path.hashCode();
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2002-2005 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.web.context.support;
import javax.servlet.ServletContext;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
/**
* ResourceLoader implementation that resolves paths as ServletContext
* resources, for use outside a WebApplicationContext (for example,
* in a HttpServletBean or GenericFilterBean subclass).
*
* <p>Within a WebApplicationContext, resource paths are automatically
* resolved as ServletContext resources by the context implementation.
*
* @author Juergen Hoeller
* @since 1.0.2
* @see #getResourceByPath
* @see ServletContextResource
* @see org.springframework.web.context.WebApplicationContext
* @see org.springframework.web.servlet.HttpServletBean
* @see org.springframework.web.filter.GenericFilterBean
*/
public class ServletContextResourceLoader extends DefaultResourceLoader {
private final ServletContext servletContext;
/**
* Create a new ServletContextResourceLoader.
* @param servletContext the ServletContext to load resources with
*/
public ServletContextResourceLoader(ServletContext servletContext) {
this.servletContext = servletContext;
}
/**
* This implementation supports file paths beneath the root of the web application.
* @see ServletContextResource
*/
protected Resource getResourceByPath(String path) {
return new ServletContextResource(this.servletContext, path);
}
}

View File

@ -0,0 +1,126 @@
/*
* 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.web.context.support;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.servlet.ServletContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.util.StringUtils;
/**
* ServletContext-aware subclass of {@link PathMatchingResourcePatternResolver},
* able to find matching resources below the web application root directory
* via Servlet 2.3's <code>ServletContext.getResourcePaths</code>.
* Falls back to the superclass' file system checking for other resources.
*
* @author Juergen Hoeller
* @since 1.1.2
*/
public class ServletContextResourcePatternResolver extends PathMatchingResourcePatternResolver {
/**
* Create a new ServletContextResourcePatternResolver.
* @param servletContext the ServletContext to load resources with
* @see ServletContextResourceLoader#ServletContextResourceLoader(javax.servlet.ServletContext)
*/
public ServletContextResourcePatternResolver(ServletContext servletContext) {
super(new ServletContextResourceLoader(servletContext));
}
/**
* Create a new ServletContextResourcePatternResolver.
* @param resourceLoader the ResourceLoader to load root directories and
* actual resources with
*/
public ServletContextResourcePatternResolver(ResourceLoader resourceLoader) {
super(resourceLoader);
}
/**
* Overridden version which checks for ServletContextResource
* and uses <code>ServletContext.getResourcePaths</code> to find
* matching resources below the web application root directory.
* In case of other resources, delegates to the superclass version.
* @see #doRetrieveMatchingServletContextResources
* @see ServletContextResource
* @see javax.servlet.ServletContext#getResourcePaths
*/
protected Set doFindPathMatchingFileResources(Resource rootDirResource, String subPattern) throws IOException {
if (rootDirResource instanceof ServletContextResource) {
ServletContextResource scResource = (ServletContextResource) rootDirResource;
ServletContext sc = scResource.getServletContext();
String fullPattern = scResource.getPath() + subPattern;
Set result = new LinkedHashSet(8);
doRetrieveMatchingServletContextResources(sc, fullPattern, scResource.getPath(), result);
return result;
}
else {
return super.doFindPathMatchingFileResources(rootDirResource, subPattern);
}
}
/**
* Recursively retrieve ServletContextResources that match the given pattern,
* adding them to the given result set.
* @param servletContext the ServletContext to work on
* @param fullPattern the pattern to match against,
* with preprended root directory path
* @param dir the current directory
* @param result the Set of matching Resources to add to
* @throws IOException if directory contents could not be retrieved
* @see ServletContextResource
* @see javax.servlet.ServletContext#getResourcePaths
*/
protected void doRetrieveMatchingServletContextResources(
ServletContext servletContext, String fullPattern, String dir, Set result) throws IOException {
Set candidates = servletContext.getResourcePaths(dir);
if (candidates != null) {
boolean dirDepthNotFixed = (fullPattern.indexOf("**") != -1);
for (Iterator it = candidates.iterator(); it.hasNext();) {
String currPath = (String) it.next();
if (!currPath.startsWith(dir)) {
// Returned resource path does not start with relative directory:
// assuming absolute path returned -> strip absolute path.
int dirIndex = currPath.indexOf(dir);
if (dirIndex != -1) {
currPath = currPath.substring(dirIndex);
}
}
if (currPath.endsWith("/") &&
(dirDepthNotFixed ||
StringUtils.countOccurrencesOf(currPath, "/") <= StringUtils.countOccurrencesOf(fullPattern, "/"))) {
// Search subdirectories recursively: ServletContext.getResourcePaths
// only returns entries for one directory level.
doRetrieveMatchingServletContextResources(servletContext, fullPattern, currPath, result);
}
if (getPathMatcher().match(fullPattern, currPath)) {
result.add(new ServletContextResource(servletContext, currPath));
}
}
}
}
}

View File

@ -0,0 +1,142 @@
/*
* 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.web.context.support;
/**
* Servlet-specific subclass of RequestHandledEvent,
* adding servlet-specific context information.
*
* @author Juergen Hoeller
* @since 2.0
* @see org.springframework.web.servlet.FrameworkServlet
* @see org.springframework.context.ApplicationContext#publishEvent
*/
public class ServletRequestHandledEvent extends RequestHandledEvent {
/** URL that the triggered the request */
private final String requestUrl;
/** IP address that the request came from */
private final String clientAddress;
/** Usually GET or POST */
private final String method;
/** Name of the servlet that handled the request */
private final String servletName;
/**
* Create a new ServletRequestHandledEvent.
* @param source the component that published the event
* @param requestUrl the URL of the request
* @param clientAddress the IP address that the request came from
* @param method the HTTP method of the request (usually GET or POST)
* @param servletName the name of the servlet that handled the request
* @param sessionId the id of the HTTP session, if any
* @param userName the name of the user that was associated with the
* request, if any (usually the UserPrincipal)
* @param processingTimeMillis the processing time of the request in milliseconds
*/
public ServletRequestHandledEvent(Object source, String requestUrl,
String clientAddress, String method, String servletName,
String sessionId, String userName, long processingTimeMillis) {
super(source, sessionId, userName, processingTimeMillis);
this.requestUrl = requestUrl;
this.clientAddress = clientAddress;
this.method = method;
this.servletName = servletName;
}
/**
* Create a new ServletRequestHandledEvent.
* @param source the component that published the event
* @param requestUrl the URL of the request
* @param clientAddress the IP address that the request came from
* @param method the HTTP method of the request (usually GET or POST)
* @param servletName the name of the servlet that handled the request
* @param sessionId the id of the HTTP session, if any
* @param userName the name of the user that was associated with the
* request, if any (usually the UserPrincipal)
* @param processingTimeMillis the processing time of the request in milliseconds
* @param failureCause the cause of failure, if any
*/
public ServletRequestHandledEvent(Object source, String requestUrl,
String clientAddress, String method, String servletName, String sessionId,
String userName, long processingTimeMillis, Throwable failureCause) {
super(source, sessionId, userName, processingTimeMillis, failureCause);
this.requestUrl = requestUrl;
this.clientAddress = clientAddress;
this.method = method;
this.servletName = servletName;
}
/**
* Return the URL of the request.
*/
public String getRequestUrl() {
return this.requestUrl;
}
/**
* Return the IP address that the request came from.
*/
public String getClientAddress() {
return this.clientAddress;
}
/**
* Return the HTTP method of the request (usually GET or POST).
*/
public String getMethod() {
return this.method;
}
/**
* Return the name of the servlet that handled the request.
*/
public String getServletName() {
return this.servletName;
}
public String getShortDescription() {
StringBuffer sb = new StringBuffer();
sb.append("url=[").append(getRequestUrl()).append("]; ");
sb.append("client=[").append(getClientAddress()).append("]; ");
sb.append(super.getShortDescription());
return sb.toString();
}
public String getDescription() {
StringBuffer sb = new StringBuffer();
sb.append("url=[").append(getRequestUrl()).append("]; ");
sb.append("client=[").append(getClientAddress()).append("]; ");
sb.append("method=[").append(getMethod()).append("]; ");
sb.append("servlet=[").append(getServletName()).append("]; ");
sb.append(super.getDescription());
return sb.toString();
}
public String toString() {
return "ServletRequestHandledEvent: " + getDescription();
}
}

View File

@ -0,0 +1,94 @@
/*
* 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.web.context.support;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
/**
* Convenient base class for self-autowiring classes that gets constructed
* within a Spring-based web application. Resolves <code>@Autowired</code>
* annotations in the endpoint class against beans in the current Spring
* root web application context (as determined by the current thread's
* context ClassLoader, which needs to be the web application's ClassLoader).
* Can alternatively be used as a delegate instead of as a base class.
*
* <p>A typical usage of this base class is a JAX-WS endpoint class:
* Such a Spring-based JAX-WS endpoint implementation will follow the
* standard JAX-WS contract for endpoint classes but will be 'thin'
* in that it delegates the actual work to one or more Spring-managed
* service beans - typically obtained using <code>@Autowired</code>.
* The lifecycle of such an endpoint instance will be managed by the
* JAX-WS runtime, hence the need for this base class to provide
* <code>@Autowired</code> processing based on the current Spring context.
*
* <p><b>NOTE:</b> If there is an explicit way to access the ServletContext,
* prefer such a way over using this class. The {@link WebApplicationContextUtils}
* class allows for easy access to the Spring root web application context
* based on the ServletContext.
*
* @author Juergen Hoeller
* @since 2.5.1
* @see WebApplicationObjectSupport
*/
public abstract class SpringBeanAutowiringSupport {
private static final Log logger = LogFactory.getLog(SpringBeanAutowiringSupport.class);
/**
* This constructor performs injection on this instance,
* based on the current web application context.
* <p>Intended for use as a base class.
* @see #processInjectionBasedOnCurrentContext
*/
public SpringBeanAutowiringSupport() {
processInjectionBasedOnCurrentContext(this);
}
/**
* Process <code>@Autowired</code> injection for the given target object,
* based on the current web application context.
* <p>Intended for use as a delegate.
* @param target the target object to process
* @see org.springframework.web.context.ContextLoader#getCurrentWebApplicationContext()
*/
public static void processInjectionBasedOnCurrentContext(Object target) {
Assert.notNull(target, "Target object must not be null");
WebApplicationContext cc = ContextLoader.getCurrentWebApplicationContext();
if (cc != null) {
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(cc.getAutowireCapableBeanFactory());
bpp.processInjection(target);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Current WebApplicationContext is not available for processing of " +
ClassUtils.getShortName(target.getClass()) + ": " +
"Make sure this class gets constructed in a Spring web application. Proceeding without injection.");
}
}
}
}

View File

@ -0,0 +1,172 @@
/*
* 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.web.context.support;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.ui.context.Theme;
import org.springframework.ui.context.ThemeSource;
import org.springframework.ui.context.support.UiApplicationContextUtils;
import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.ServletConfigAware;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.context.request.RequestScope;
import org.springframework.web.context.request.SessionScope;
/**
* Static {@link org.springframework.web.context.WebApplicationContext}
* implementation for testing. Not intended for use in production applications.
*
* <p>Implements the {@link org.springframework.web.context.ConfigurableWebApplicationContext}
* interface to allow for direct replacement of an {@link XmlWebApplicationContext},
* despite not actually supporting external configuration files.
*
* <p>Interprets resource paths as servlet context resources, i.e. as paths beneath
* the web application root. Absolute paths, e.g. for files outside the web app root,
* can be accessed via "file:" URLs, as implemented by
* {@link org.springframework.core.io.DefaultResourceLoader}.
*
* <p>In addition to the special beans detected by
* {@link org.springframework.context.support.AbstractApplicationContext},
* this class detects a bean of type {@link org.springframework.ui.context.ThemeSource}
* in the context, under the special bean name "themeSource".
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see org.springframework.ui.context.ThemeSource
*/
public class StaticWebApplicationContext extends StaticApplicationContext
implements ConfigurableWebApplicationContext, ThemeSource {
private ServletContext servletContext;
private ServletConfig servletConfig;
private String namespace;
private ThemeSource themeSource;
public StaticWebApplicationContext() {
setDisplayName("Root WebApplicationContext");
}
/**
* Set the ServletContext that this WebApplicationContext runs in.
*/
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
public ServletContext getServletContext() {
return this.servletContext;
}
public void setServletConfig(ServletConfig servletConfig) {
this.servletConfig = servletConfig;
if (servletConfig != null && this.servletContext == null) {
this.servletContext = servletConfig.getServletContext();
}
}
public ServletConfig getServletConfig() {
return this.servletConfig;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
if (namespace != null) {
setDisplayName("WebApplicationContext for namespace '" + namespace + "'");
}
}
public String getNamespace() {
return this.namespace;
}
/**
* The {@link StaticWebApplicationContext} class does not support this method.
* @throws UnsupportedOperationException <b>always</b>
*/
public void setConfigLocation(String configLocation) {
if (configLocation != null) {
throw new UnsupportedOperationException("StaticWebApplicationContext does not support config locations");
}
}
/**
* The {@link StaticWebApplicationContext} class does not support this method.
* @throws UnsupportedOperationException <b>always</b>
*/
public void setConfigLocations(String[] configLocations) {
if (configLocations != null) {
throw new UnsupportedOperationException("StaticWebApplicationContext does not support config locations");
}
}
public String[] getConfigLocations() {
return null;
}
/**
* Register request/session scopes, a {@link ServletContextAwareProcessor}, etc.
*/
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.registerScope(SCOPE_REQUEST, new RequestScope());
beanFactory.registerScope(SCOPE_SESSION, new SessionScope(false));
beanFactory.registerScope(SCOPE_GLOBAL_SESSION, new SessionScope(true));
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
}
/**
* This implementation supports file paths beneath the root of the ServletContext.
* @see ServletContextResource
*/
protected Resource getResourceByPath(String path) {
return new ServletContextResource(this.servletContext, path);
}
/**
* This implementation supports pattern matching in unexpanded WARs too.
* @see ServletContextResourcePatternResolver
*/
protected ResourcePatternResolver getResourcePatternResolver() {
return new ServletContextResourcePatternResolver(this);
}
/**
* Initialize the theme capability.
*/
protected void onRefresh() {
this.themeSource = UiApplicationContextUtils.initThemeSource(this);
}
public Theme getTheme(String themeName) {
return this.themeSource.getTheme(themeName);
}
}

View File

@ -0,0 +1,149 @@
/*
* 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.web.context.support;
import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.util.Assert;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.RequestScope;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.SessionScope;
/**
* Convenience methods for retrieving the root
* {@link org.springframework.web.context.WebApplicationContext} for a given
* <code>ServletContext</code>. This is e.g. useful for accessing a Spring
* context from within custom web views or Struts actions.
*
* <p>Note that there are more convenient ways of accessing the root context for
* many web frameworks, either part of Spring or available as external library.
* This helper class is just the most generic way to access the root context.
*
* @author Juergen Hoeller
* @see org.springframework.web.context.ContextLoader
* @see org.springframework.web.servlet.FrameworkServlet
* @see org.springframework.web.servlet.DispatcherServlet
* @see org.springframework.web.struts.ActionSupport
* @see org.springframework.web.struts.DelegatingActionProxy
* @see org.springframework.web.jsf.FacesContextUtils
* @see org.springframework.web.jsf.DelegatingVariableResolver
*/
public abstract class WebApplicationContextUtils {
/**
* Find the root WebApplicationContext for this web application, which is
* typically loaded via {@link org.springframework.web.context.ContextLoaderListener} or
* {@link org.springframework.web.context.ContextLoaderServlet}.
* <p>Will rethrow an exception that happened on root context startup,
* to differentiate between a failed context startup and no context at all.
* @param sc ServletContext to find the web application context for
* @return the root WebApplicationContext for this web app
* @throws IllegalStateException if the root WebApplicationContext could not be found
* @see org.springframework.web.context.WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
*/
public static WebApplicationContext getRequiredWebApplicationContext(ServletContext sc)
throws IllegalStateException {
WebApplicationContext wac = getWebApplicationContext(sc);
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?");
}
return wac;
}
/**
* Find the root WebApplicationContext for this web application, which is
* typically loaded via {@link org.springframework.web.context.ContextLoaderListener} or
* {@link org.springframework.web.context.ContextLoaderServlet}.
* <p>Will rethrow an exception that happened on root context startup,
* to differentiate between a failed context startup and no context at all.
* @param sc ServletContext to find the web application context for
* @return the root WebApplicationContext for this web app, or <code>null</code> if none
* @see org.springframework.web.context.WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
*/
public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
}
/**
* Find a custom WebApplicationContext for this web application.
* @param sc ServletContext to find the web application context for
* @param attrName the name of the ServletContext attribute to look for
* @return the desired WebApplicationContext for this web app, or <code>null</code> if none
*/
public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) {
Assert.notNull(sc, "ServletContext must not be null");
Object attr = sc.getAttribute(attrName);
if (attr == null) {
return null;
}
if (attr instanceof RuntimeException) {
throw (RuntimeException) attr;
}
if (attr instanceof Error) {
throw (Error) attr;
}
if (attr instanceof Exception) {
IllegalStateException ex = new IllegalStateException();
ex.initCause((Exception) attr);
throw ex;
}
if (!(attr instanceof WebApplicationContext)) {
throw new IllegalStateException("Context attribute is not of type WebApplicationContext: " + attr);
}
return (WebApplicationContext) attr;
}
/**
* Register web-specific scopes with the given BeanFactory,
* as used by the WebApplicationContext.
* @param beanFactory the BeanFactory to configure
*/
public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory) {
beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope(false));
beanFactory.registerScope(WebApplicationContext.SCOPE_GLOBAL_SESSION, new SessionScope(true));
beanFactory.registerResolvableDependency(ServletRequest.class, new ObjectFactory() {
public Object getObject() {
RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
if (!(requestAttr instanceof ServletRequestAttributes)) {
throw new IllegalStateException("Current request is not a servlet request");
}
return ((ServletRequestAttributes) requestAttr).getRequest();
}
});
beanFactory.registerResolvableDependency(HttpSession.class, new ObjectFactory() {
public Object getObject() {
RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
if (!(requestAttr instanceof ServletRequestAttributes)) {
throw new IllegalStateException("Current request is not a servlet request");
}
return ((ServletRequestAttributes) requestAttr).getRequest().getSession();
}
});
}
}

View File

@ -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.web.context.support;
import java.io.File;
import javax.servlet.ServletContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ApplicationObjectSupport;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.util.WebUtils;
/**
* Convenient superclass for application objects running in a WebApplicationContext.
* Provides <code>getWebApplicationContext()</code>, <code>getServletContext()</code>,
* and <code>getTempDir()</code> methods.
*
* @author Juergen Hoeller
* @since 28.08.2003
* @see SpringBeanAutowiringSupport
*/
public abstract class WebApplicationObjectSupport extends ApplicationObjectSupport
implements ServletContextAware {
private ServletContext servletContext;
public final void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
if (servletContext != null) {
initServletContext(servletContext);
}
}
/**
* Overrides the base class behavior to enforce running in an ApplicationContext.
* All accessors will throw IllegalStateException if not running in a context.
* @see #getApplicationContext()
* @see #getMessageSourceAccessor()
* @see #getWebApplicationContext()
* @see #getServletContext()
* @see #getTempDir()
*/
protected boolean isContextRequired() {
return true;
}
/**
* Calls {@link #initServletContext(javax.servlet.ServletContext)} if the
* given ApplicationContext is a {@link WebApplicationContext}.
*/
protected void initApplicationContext(ApplicationContext context) {
super.initApplicationContext(context);
if (context instanceof WebApplicationContext) {
ServletContext servletContext = ((WebApplicationContext) context).getServletContext();
if (servletContext != null) {
initServletContext(servletContext);
}
}
}
/**
* Subclasses may override this for custom initialization based
* on the ServletContext that this application object runs in.
* <p>The default implementation is empty. Called by
* {@link #initApplicationContext(org.springframework.context.ApplicationContext)}
* as well as {@link #setServletContext(javax.servlet.ServletContext)}.
* @param servletContext the ServletContext that this application object runs in
* (never <code>null</code>)
*/
protected void initServletContext(ServletContext servletContext) {
}
/**
* Return the current application context as WebApplicationContext.
* <p><b>NOTE:</b> Only use this if you actually need to access
* WebApplicationContext-specific functionality. Preferably use
* <code>getApplicationContext()</code> or <code>getServletContext()</code>
* else, to be able to run in non-WebApplicationContext environments as well.
* @throws IllegalStateException if not running in a WebApplicationContext
* @see #getApplicationContext()
*/
protected final WebApplicationContext getWebApplicationContext() throws IllegalStateException {
ApplicationContext ctx = getApplicationContext();
if (ctx instanceof WebApplicationContext) {
return (WebApplicationContext) getApplicationContext();
}
else if (isContextRequired()) {
throw new IllegalStateException("WebApplicationObjectSupport instance [" + this +
"] does not run in a WebApplicationContext but in: " + ctx);
}
else {
return null;
}
}
/**
* Return the current ServletContext.
* @throws IllegalStateException if not running within a ServletContext
*/
protected final ServletContext getServletContext() throws IllegalStateException {
if (this.servletContext != null) {
return this.servletContext;
}
ServletContext servletContext = getWebApplicationContext().getServletContext();
if (servletContext == null && isContextRequired()) {
throw new IllegalStateException("WebApplicationObjectSupport instance [" + this +
"] does not run within a ServletContext. Make sure the object is fully configured!");
}
return servletContext;
}
/**
* Return the temporary directory for the current web application,
* as provided by the servlet container.
* @return the File representing the temporary directory
* @throws IllegalStateException if not running within a ServletContext
* @see org.springframework.web.util.WebUtils#getTempDir(javax.servlet.ServletContext)
*/
protected final File getTempDir() throws IllegalStateException {
return WebUtils.getTempDir(getServletContext());
}
}

View File

@ -0,0 +1,143 @@
/*
* 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.web.context.support;
import java.io.IOException;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.ResourceEntityResolver;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
/**
* {@link org.springframework.web.context.WebApplicationContext} implementation
* which takes its configuration from XML documents, understood by an
* {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader}.
* This is essentially the equivalent of
* {@link org.springframework.context.support.AbstractXmlApplicationContext}
* for a web environment.
*
* <p>By default, the configuration will be taken from "/WEB-INF/applicationContext.xml"
* for the root context, and "/WEB-INF/test-servlet.xml" for a context with the namespace
* "test-servlet" (like for a DispatcherServlet instance with the servlet-name "test").
*
* <p>The config location defaults can be overridden via the "contextConfigLocation"
* context-param of {@link org.springframework.web.context.ContextLoader} and servlet
* init-param of {@link org.springframework.web.servlet.FrameworkServlet}. Config locations
* can either denote concrete files like "/WEB-INF/context.xml" or Ant-style patterns
* like "/WEB-INF/*-context.xml" (see {@link org.springframework.util.PathMatcher}
* javadoc for pattern details).
*
* <p>Note: In case of multiple config locations, later bean definitions will
* override ones defined in earlier loaded files. This can be leveraged to
* deliberately override certain bean definitions via an extra XML file.
*
* <p><b>For a WebApplicationContext that reads in a different bean definition format,
* create an analogous subclass of {@link AbstractRefreshableWebApplicationContext}.</b>
* Such a context implementation can be specified as "contextClass" context-param
* for ContextLoader or "contextClass" init-param for FrameworkServlet.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see #setNamespace
* @see #setConfigLocations
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
* @see org.springframework.web.context.ContextLoader#initWebApplicationContext
* @see org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext
*/
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {
/** Default config location for the root context */
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
/** Default prefix for building a config location for a namespace */
public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";
/** Default suffix for building a config location for a namespace */
public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";
/**
* Loads the bean definitions via an XmlBeanDefinitionReader.
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
* @see #initBeanDefinitionReader
* @see #loadBeanDefinitions
*/
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
/**
* Initialize the bean definition reader used for loading the bean
* definitions of this context. Default implementation is empty.
* <p>Can be overridden in subclasses, e.g. for turning off XML validation
* or using a different XmlBeanDefinitionParser implementation.
* @param beanDefinitionReader the bean definition reader used by this context
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setValidationMode
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setDocumentReaderClass
*/
protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
}
/**
* Load the bean definitions with the given XmlBeanDefinitionReader.
* <p>The lifecycle of the bean factory is handled by the refreshBeanFactory method;
* therefore this method is just supposed to load and/or register bean definitions.
* <p>Delegates to a ResourcePatternResolver for resolving location patterns
* into Resource instances.
* @throws org.springframework.beans.BeansException in case of bean registration errors
* @throws java.io.IOException if the required XML document isn't found
* @see #refreshBeanFactory
* @see #getConfigLocations
* @see #getResources
* @see #getResourcePatternResolver
*/
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (int i = 0; i < configLocations.length; i++) {
reader.loadBeanDefinitions(configLocations[i]);
}
}
}
/**
* The default location for the root context is "/WEB-INF/applicationContext.xml",
* and "/WEB-INF/test-servlet.xml" for a context with the namespace "test-servlet"
* (like for a DispatcherServlet instance with the servlet-name "test").
*/
protected String[] getDefaultConfigLocations() {
if (getNamespace() != null) {
return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
}
else {
return new String[] {DEFAULT_CONFIG_LOCATION};
}
}
}

View File

@ -0,0 +1,8 @@
<html>
<body>
Classes supporting the <code>org.springframework.web.context</code> package,
such as WebApplicationContext implementations and various utility classes.
</body>
</html>

View File

@ -0,0 +1,231 @@
/*
* 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.web.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.util.StringUtils;
/**
* Base class for <code>Filter</code>s that perform logging operations before and after a
* request is processed.
*
* <p>Subclasses should override the <code>beforeRequest(HttpServletRequest, String)</code>
* and <code>afterRequest(HttpServletRequest, String)</code> methods to perform the actual
* logging around the request.
*
* <p>Subclasses are passed the message to write to the log in the <code>beforeRequest</code>
* and <code>afterRequest</code> methods. By default, only the URI of the request is logged.
* However, setting the <code>includeQueryString</code> property to <code>true</code> will
* cause the query string of the request to be included also.
*
* <p>Prefixes and suffixes for the before and after messages can be configured
* using the <code>beforeMessagePrefix</code>, <code>afterMessagePrefix</code>,
* <code>beforeMessageSuffix</code> and <code>afterMessageSuffix</code> properties,
*
* @author Rob Harrop
* @author Juergen Hoeller
* @since 1.2.5
* @see #beforeRequest
* @see #afterRequest
*/
public abstract class AbstractRequestLoggingFilter extends OncePerRequestFilter {
public static final String DEFAULT_BEFORE_MESSAGE_PREFIX = "Before request [";
public static final String DEFAULT_BEFORE_MESSAGE_SUFFIX = "]";
public static final String DEFAULT_AFTER_MESSAGE_PREFIX = "After request [";
public static final String DEFAULT_AFTER_MESSAGE_SUFFIX = "]";
private boolean includeQueryString = false;
private boolean includeClientInfo = false;
private String beforeMessagePrefix = DEFAULT_BEFORE_MESSAGE_PREFIX;
private String beforeMessageSuffix = DEFAULT_BEFORE_MESSAGE_SUFFIX;
private String afterMessagePrefix = DEFAULT_AFTER_MESSAGE_PREFIX;
private String afterMessageSuffix = DEFAULT_AFTER_MESSAGE_SUFFIX;
/**
* Set whether or not the query string should be included in the log message.
* <p>Should be configured using an <code>&lt;init-param&gt;</code> for parameter
* name "includeQueryString" in the filter definition in <code>web.xml</code>.
*/
public void setIncludeQueryString(boolean includeQueryString) {
this.includeQueryString = includeQueryString;
}
/**
* Return whether or not the query string should be included in the log message.
*/
protected boolean isIncludeQueryString() {
return this.includeQueryString;
}
/**
* Set whether or not the client address and session id should be included
* in the log message.
* <p>Should be configured using an <code>&lt;init-param&gt;</code> for parameter
* name "includeClientInfo" in the filter definition in <code>web.xml</code>.
*/
public void setIncludeClientInfo(boolean includeClientInfo) {
this.includeClientInfo = includeClientInfo;
}
/**
* Return whether or not the client address and session id should be included
* in the log message.
*/
protected boolean isIncludeClientInfo() {
return this.includeClientInfo;
}
/**
* Set the value that should be prepended to the log message written
* <i>before</i> a request is processed.
*/
public void setBeforeMessagePrefix(String beforeMessagePrefix) {
this.beforeMessagePrefix = beforeMessagePrefix;
}
/**
* Set the value that should be apppended to the log message written
* <i>before</i> a request is processed.
*/
public void setBeforeMessageSuffix(String beforeMessageSuffix) {
this.beforeMessageSuffix = beforeMessageSuffix;
}
/**
* Set the value that should be prepended to the log message written
* <i>after</i> a request is processed.
*/
public void setAfterMessagePrefix(String afterMessagePrefix) {
this.afterMessagePrefix = afterMessagePrefix;
}
/**
* Set the value that should be appended to the log message written
* <i>after</i> a request is processed.
*/
public void setAfterMessageSuffix(String afterMessageSuffix) {
this.afterMessageSuffix = afterMessageSuffix;
}
/**
* Forwards the request to the next filter in the chain and delegates
* down to the subclasses to perform the actual request logging both
* before and after the request is processed.
* @see #beforeRequest
* @see #afterRequest
*/
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
beforeRequest(request, getBeforeMessage(request));
try {
filterChain.doFilter(request, response);
}
finally {
afterRequest(request, getAfterMessage(request));
}
}
/**
* Get the message to write to the log before the request.
* @see #createMessage
*/
private String getBeforeMessage(HttpServletRequest request) {
return createMessage(request, this.beforeMessagePrefix, this.beforeMessageSuffix);
}
/**
* Get the message to write to the log after the request.
* @see #createMessage
*/
private String getAfterMessage(HttpServletRequest request) {
return createMessage(request, this.afterMessagePrefix, this.afterMessageSuffix);
}
/**
* Create a log message for the given request, prefix and suffix.
* <p>If <code>includeQueryString</code> is <code>true</code> then
* the inner part of the log message will take the form
* <code>request_uri?query_string</code> otherwise the message will
* simply be of the form <code>request_uri</code>.
* <p>The final message is composed of the inner part as described
* and the supplied prefix and suffix.
*/
protected String createMessage(HttpServletRequest request, String prefix, String suffix) {
StringBuffer buffer = new StringBuffer();
buffer.append(prefix);
buffer.append("uri=").append(request.getRequestURI());
if (isIncludeQueryString()) {
buffer.append('?').append(request.getQueryString());
}
if (isIncludeClientInfo()) {
String client = request.getRemoteAddr();
if (StringUtils.hasLength(client)) {
buffer.append(";client=").append(client);
}
HttpSession session = request.getSession(false);
if (session != null) {
buffer.append(";session=").append(session.getId());
}
String user = request.getRemoteUser();
if (user != null) {
buffer.append(";user=").append(user);
}
}
buffer.append(suffix);
return buffer.toString();
}
/**
* Concrete subclasses should implement this method to write a log message
* <i>before</i> the request is processed.
* @param request current HTTP request
* @param message the message to log
*/
protected abstract void beforeRequest(HttpServletRequest request, String message);
/**
* Concrete subclasses should implement this method to write a log message
* <i>after</i> the request is processed.
* @param request current HTTP request
* @param message the message to log
*/
protected abstract void afterRequest(HttpServletRequest request, String message);
}

View File

@ -0,0 +1,99 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.util.ClassUtils;
/**
* Servlet 2.3/2.4 Filter that allows one to specify a character encoding for
* requests. This is useful because current browsers typically do not set a
* character encoding even if specified in the HTML page or form.
*
* <p>This filter can either apply its encoding if the request does not
* already specify an encoding, or enforce this filter's encoding in any case
* ("forceEncoding"="true"). In the latter case, the encoding will also be
* applied as default response encoding on Servlet 2.4+ containers (although
* this will usually be overridden by a full content type set in the view).
*
* @author Juergen Hoeller
* @since 15.03.2004
* @see #setEncoding
* @see #setForceEncoding
* @see javax.servlet.http.HttpServletRequest#setCharacterEncoding
* @see javax.servlet.http.HttpServletResponse#setCharacterEncoding
*/
public class CharacterEncodingFilter extends OncePerRequestFilter {
// Determine whether the Servlet 2.4 HttpServletResponse.setCharacterEncoding(String)
// method is available, for use in the "doFilterInternal" implementation.
private final static boolean responseSetCharacterEncodingAvailable = ClassUtils.hasMethod(
HttpServletResponse.class, "setCharacterEncoding", new Class[] {String.class});
private String encoding;
private boolean forceEncoding = false;
/**
* Set the encoding to use for requests. This encoding will be passed into a
* {@link javax.servlet.http.HttpServletRequest#setCharacterEncoding} call.
* <p>Whether this encoding will override existing request encodings
* (and whether it will be applied as default response encoding as well)
* depends on the {@link #setForceEncoding "forceEncoding"} flag.
*/
public void setEncoding(String encoding) {
this.encoding = encoding;
}
/**
* Set whether the configured {@link #setEncoding encoding} of this filter
* is supposed to override existing request and response encodings.
* <p>Default is "false", i.e. do not modify the encoding if
* {@link javax.servlet.http.HttpServletRequest#getCharacterEncoding()}
* returns a non-null value. Switch this to "true" to enforce the specified
* encoding in any case, applying it as default response encoding as well.
* <p>Note that the response encoding will only be set on Servlet 2.4+
* containers, since Servlet 2.3 did not provide a facility for setting
* a default response encoding.
*/
public void setForceEncoding(boolean forceEncoding) {
this.forceEncoding = forceEncoding;
}
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null)) {
request.setCharacterEncoding(this.encoding);
if (this.forceEncoding && responseSetCharacterEncodingAvailable) {
response.setCharacterEncoding(this.encoding);
}
}
filterChain.doFilter(request, response);
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 2002-2005 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.web.filter;
import javax.servlet.http.HttpServletRequest;
/**
* Simple request logging filter that writes the request URI
* (and optionally the query string) to the Commons Log.
*
* @author Rob Harrop
* @since 1.2.5
* @see #setIncludeQueryString
* @see #setBeforeMessagePrefix
* @see #setBeforeMessageSuffix
* @see #setAfterMessagePrefix
* @see #setAfterMessageSuffix
* @see org.apache.commons.logging.Log#debug(Object)
*/
public class CommonsRequestLoggingFilter extends AbstractRequestLoggingFilter {
/**
* Writes a log message before the request is processed.
*/
protected void beforeRequest(HttpServletRequest request, String message) {
if (logger.isDebugEnabled()) {
logger.debug(message);
}
}
/**
* Writes a log message after the request is processed.
*/
protected void afterRequest(HttpServletRequest request, String message) {
if (logger.isDebugEnabled()) {
logger.debug(message);
}
}
}

View File

@ -0,0 +1,252 @@
/*
* 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.web.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
/**
* Proxy for a standard Servlet 2.3 Filter, delegating to a Spring-managed
* bean that implements the Filter interface. Supports a "targetBeanName"
* filter init-param in <code>web.xml</code>, specifying the name of the
* target bean in the Spring application context.
*
* <p><code>web.xml</code> will usually contain a DelegatingFilterProxy definition,
* with the specified <code>filter-name</code> corresponding to a bean name in
* Spring's root application context. All calls to the filter proxy will then
* be delegated to that bean in the Spring context, which is required to implement
* the standard Servlet 2.3 Filter interface.
*
* <p>This approach is particularly useful for Filter implementation with complex
* setup needs, allowing to apply the full Spring bean definition machinery to
* Filter instances. Alternatively, consider standard Filter setup in combination
* with looking up service beans from the Spring root application context.
*
* <p><b>NOTE:</b> The lifecycle methods defined by the Servlet Filter interface
* will by default <i>not</i> be delegated to the target bean, relying on the
* Spring application context to manage the lifecycle of that bean. Specifying
* the "targetFilterLifecycle" filter init-param as "true" will enforce invocation
* of the <code>Filter.init</code> and <code>Filter.destroy</code> lifecycle methods
* on the target bean, letting the servlet container manage the filter lifecycle.
*
* <p>This class is inspired by Acegi Security's FilterToBeanProxy class,
* written by Ben Alex.
*
* @author Juergen Hoeller
* @author Sam Brannen
* @since 1.2
* @see #setTargetBeanName
* @see #setTargetFilterLifecycle
* @see javax.servlet.Filter#doFilter
* @see javax.servlet.Filter#init
* @see javax.servlet.Filter#destroy
*/
public class DelegatingFilterProxy extends GenericFilterBean {
private String contextAttribute;
private String targetBeanName;
private boolean targetFilterLifecycle = false;
private Filter delegate;
private final Object delegateMonitor = new Object();
/**
* Set the name of the ServletContext attribute which should be used to retrieve the
* {@link WebApplicationContext} from which to load the delegate {@link Filter} bean.
*/
public void setContextAttribute(String contextAttribute) {
this.contextAttribute = contextAttribute;
}
/**
* Return the name of the ServletContext attribute which should be used to retrieve the
* {@link WebApplicationContext} from which to load the delegate {@link Filter} bean.
*/
public String getContextAttribute() {
return this.contextAttribute;
}
/**
* Set the name of the target bean in the Spring application context.
* The target bean must implement the standard Servlet 2.3 Filter interface.
* <p>By default, the <code>filter-name</code> as specified for the
* DelegatingFilterProxy in <code>web.xml</code> will be used.
*/
public void setTargetBeanName(String targetBeanName) {
this.targetBeanName = targetBeanName;
}
/**
* Return the name of the target bean in the Spring application context.
*/
protected String getTargetBeanName() {
return this.targetBeanName;
}
/**
* Set whether to invoke the <code>Filter.init</code> and
* <code>Filter.destroy</code> lifecycle methods on the target bean.
* <p>Default is "false"; target beans usually rely on the Spring application
* context for managing their lifecycle. Setting this flag to "true" means
* that the servlet container will control the lifecycle of the target
* Filter, with this proxy delegating the corresponding calls.
*/
public void setTargetFilterLifecycle(boolean targetFilterLifecycle) {
this.targetFilterLifecycle = targetFilterLifecycle;
}
/**
* Return whether to invoke the <code>Filter.init</code> and
* <code>Filter.destroy</code> lifecycle methods on the target bean.
*/
protected boolean isTargetFilterLifecycle() {
return this.targetFilterLifecycle;
}
protected void initFilterBean() throws ServletException {
// If no target bean name specified, use filter name.
if (this.targetBeanName == null) {
this.targetBeanName = getFilterName();
}
// Fetch Spring root application context and initialize the delegate early,
// if possible. If the root application context will be started after this
// filter proxy, we'll have to resort to lazy initialization.
synchronized (this.delegateMonitor) {
WebApplicationContext wac = findWebApplicationContext();
if (wac != null) {
this.delegate = initDelegate(wac);
}
}
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// Lazily initialize the delegate if necessary.
Filter delegateToUse = null;
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
WebApplicationContext wac = findWebApplicationContext();
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?");
}
this.delegate = initDelegate(wac);
}
delegateToUse = this.delegate;
}
// Let the delegate perform the actual doFilter operation.
invokeDelegate(delegateToUse, request, response, filterChain);
}
public void destroy() {
Filter delegateToUse = null;
synchronized (this.delegateMonitor) {
delegateToUse = this.delegate;
}
if (delegateToUse != null) {
destroyDelegate(delegateToUse);
}
}
/**
* Retrieve a <code>WebApplicationContext</code> from the <code>ServletContext</code>
* attribute with the {@link #setContextAttribute configured name}. The
* <code>WebApplicationContext</code> must have already been loaded and stored in the
* <code>ServletContext</code> before this filter gets initialized (or invoked).
* <p>Subclasses may override this method to provide a different
* <code>WebApplicationContext</code> retrieval strategy.
* @return the WebApplicationContext for this proxy, or <code>null</code> if not found
* @see #getContextAttribute()
*/
protected WebApplicationContext findWebApplicationContext() {
String attrName = getContextAttribute();
if (attrName != null) {
return WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
}
else {
return WebApplicationContextUtils.getWebApplicationContext(getServletContext());
}
}
/**
* Initialize the Filter delegate, defined as bean the given Spring
* application context.
* <p>Default implementation fetches the bean from the application context
* and calls the standard <code>Filter.init</code> method on it, passing
* in the FilterConfig of this Filter proxy.
* @param wac the root application context
* @return the initialized delegate Filter
* @throws ServletException if thrown by the Filter
* @see #getTargetBeanName()
* @see #isTargetFilterLifecycle()
* @see #getFilterConfig()
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
*/
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
Filter delegate = (Filter) wac.getBean(getTargetBeanName(), Filter.class);
if (isTargetFilterLifecycle()) {
delegate.init(getFilterConfig());
}
return delegate;
}
/**
* Actually invoke the delegate Filter with the given request and response.
* @param delegate the delegate Filter
* @param request the current HTTP request
* @param response the current HTTP response
* @param filterChain the current FilterChain
* @throws ServletException if thrown by the Filter
* @throws IOException if thrown by the Filter
*/
protected void invokeDelegate(
Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
delegate.doFilter(request, response, filterChain);
}
/**
* Destroy the Filter delegate.
* Default implementation simply calls <code>Filter.destroy</code> on it.
* @param delegate the Filter delegate (never <code>null</code>)
* @see #isTargetFilterLifecycle()
* @see javax.servlet.Filter#destroy()
*/
protected void destroyDelegate(Filter delegate) {
if (isTargetFilterLifecycle()) {
delegate.destroy();
}
}
}

Some files were not shown because too many files have changed in this diff Show More