提交 04eaa47e 编写于 作者: A Arjen Poutsma

Initial import of web module

上级 d1061e7e
<?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>
<?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>
<?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>
\ No newline at end of file
/*
* 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);
}
}
}
/*
* 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);
}
}
}
/*
* 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;
}
}
/*
* 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);
}
}
}
/*
* 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) {
}
}
}
}
/*
* 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);
}
}
}
/*
* 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);
}
}
}
/*
* 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);
}
}
}
/*
* 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;
}
}
/*
* 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);
}
}
}
/*
* 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;
}
/*
* 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());
}
}
/*
* 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());
}
}
<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>
/*
* 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;
}
}
/*
* 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);
}
}
/*
* 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();
}
/*
* 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);
}
}
}
/*
* 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;
}
}
/*
* 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;
}
/*
* 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;
}
}
/*
* 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);
}
}
/*
* 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;
}
}
<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>
/*
* 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;
}
}
/*
* 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);
}
/*
* 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();
}
}
/*
* 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);
}
}
}
}
/*
* 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;
}
}
/*
* 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() {
}
}
<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>
/*
* 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));
}
}
<html>
<body>
Support for specific JAX-RPC providers. Contains an Axis-specific
JaxRpcServicePostProcessor for declaratively registering bean mappings.
</body>
</html>
/*
* 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();
}
}
}
<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>
/*
* 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);
}
}
# 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
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册