Drop RPC-style remoting
Closes gh-27422
This commit is contained in:
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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;
|
||||
|
||||
import org.springframework.core.NestedRuntimeException;
|
||||
|
||||
/**
|
||||
* Generic remote access exception. A service proxy for any remoting
|
||||
* protocol should throw this exception or subclasses of it, in order
|
||||
* to transparently expose a plain Java business interface.
|
||||
*
|
||||
* <p>When using conforming proxies, switching the actual remoting protocol
|
||||
* e.g. from Hessian does not affect client code. Clients work with a plain
|
||||
* natural Java business interface that the service exposes. A client object
|
||||
* simply receives an implementation for the interface that it needs via a
|
||||
* bean reference, like it does for a local bean as well.
|
||||
*
|
||||
* <p>A client may catch RemoteAccessException if it wants to, but as
|
||||
* remote access errors are typically unrecoverable, it will probably let
|
||||
* such exceptions propagate to a higher level that handles them generically.
|
||||
* In this case, the client code doesn't show any signs of being involved in
|
||||
* remote access, as there aren't any remoting-specific dependencies.
|
||||
*
|
||||
* <p>Even when switching from a remote service proxy to a local implementation
|
||||
* of the same interface, this amounts to just a matter of configuration. Obviously,
|
||||
* the client code should be somewhat aware that it <i>might be working</i>
|
||||
* against a remote service, for example in terms of repeated method calls that
|
||||
* cause unnecessary roundtrips etc. However, it doesn't have to be aware whether
|
||||
* it is <i>actually working</i> against a remote service or a local implementation,
|
||||
* or with which remoting protocol it is working under the hood.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 14.05.2003
|
||||
*/
|
||||
public class RemoteAccessException extends NestedRuntimeException {
|
||||
|
||||
/** Use serialVersionUID from Spring 1.2 for interoperability. */
|
||||
private static final long serialVersionUID = -4906825139312227864L;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor for RemoteAccessException.
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public RemoteAccessException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for RemoteAccessException.
|
||||
* @param msg the detail message
|
||||
* @param cause the root cause (usually from using an underlying
|
||||
* remoting API such as RMI)
|
||||
*/
|
||||
public RemoteAccessException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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;
|
||||
|
||||
/**
|
||||
* RemoteAccessException subclass to be thrown when no connection
|
||||
* could be established with a remote service.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class RemoteConnectFailureException extends RemoteAccessException {
|
||||
|
||||
/**
|
||||
* Constructor for RemoteConnectFailureException.
|
||||
* @param msg the detail message
|
||||
* @param cause the root cause from the remoting API in use
|
||||
*/
|
||||
public RemoteConnectFailureException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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;
|
||||
|
||||
/**
|
||||
* RemoteAccessException subclass to be thrown when the execution
|
||||
* of the target method failed on the server side, for example
|
||||
* when a method was not found on the target object.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see RemoteProxyFailureException
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class RemoteInvocationFailureException extends RemoteAccessException {
|
||||
|
||||
/**
|
||||
* Constructor for RemoteInvocationFailureException.
|
||||
* @param msg the detail message
|
||||
* @param cause the root cause from the remoting API in use
|
||||
*/
|
||||
public RemoteInvocationFailureException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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;
|
||||
|
||||
/**
|
||||
* RemoteAccessException subclass to be thrown in case of a lookup failure,
|
||||
* typically if the lookup happens on demand for each method invocation.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class RemoteLookupFailureException extends RemoteAccessException {
|
||||
|
||||
/**
|
||||
* Constructor for RemoteLookupFailureException.
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public RemoteLookupFailureException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for RemoteLookupFailureException.
|
||||
* @param msg message
|
||||
* @param cause the root cause from the remoting API in use
|
||||
*/
|
||||
public RemoteLookupFailureException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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;
|
||||
|
||||
/**
|
||||
* RemoteAccessException subclass to be thrown in case of a failure
|
||||
* within the client-side proxy for a remote service, for example
|
||||
* when a method was not found on the underlying RMI stub.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.2.8
|
||||
* @see RemoteInvocationFailureException
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class RemoteProxyFailureException extends RemoteAccessException {
|
||||
|
||||
/**
|
||||
* Constructor for RemoteProxyFailureException.
|
||||
* @param msg the detail message
|
||||
* @param cause the root cause from the remoting API in use
|
||||
*/
|
||||
public RemoteProxyFailureException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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;
|
||||
|
||||
/**
|
||||
* RemoteAccessException subclass to be thrown when the execution
|
||||
* of the target method did not complete before a configurable
|
||||
* timeout, for example when a reply message was not received.
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.2
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class RemoteTimeoutException extends RemoteAccessException {
|
||||
|
||||
/**
|
||||
* Constructor for RemoteTimeoutException.
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public RemoteTimeoutException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for RemoteTimeoutException.
|
||||
* @param msg the detail message
|
||||
* @param cause the root cause from the remoting API in use
|
||||
*/
|
||||
public RemoteTimeoutException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* Exception hierarchy for Spring's remoting infrastructure,
|
||||
* independent of any specific remote method invocation system.
|
||||
*/
|
||||
@NonNullApi
|
||||
@NonNullFields
|
||||
package org.springframework.remoting;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
import org.springframework.lang.NonNullFields;
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.rmi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.rmi.server.RMIClassLoader;
|
||||
|
||||
import org.springframework.core.ConfigurableObjectInputStream;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Special ObjectInputStream subclass that falls back to a specified codebase
|
||||
* to load classes from if not found locally. In contrast to standard RMI
|
||||
* conventions for dynamic class download, it is the client that determines
|
||||
* the codebase URL here, rather than the "java.rmi.server.codebase" system
|
||||
* property on the server.
|
||||
*
|
||||
* <p>Uses the JDK's RMIClassLoader to load classes from the specified codebase.
|
||||
* The codebase can consist of multiple URLs, separated by spaces.
|
||||
* Note that RMIClassLoader requires a SecurityManager to be set, like when
|
||||
* using dynamic class download with standard RMI! (See the RMI documentation
|
||||
* for details.)
|
||||
*
|
||||
* <p>Despite residing in the RMI package, this class is <i>not</i> used for
|
||||
* RmiClientInterceptor, which uses the standard RMI infrastructure instead
|
||||
* and thus is only able to rely on RMI's standard dynamic class download via
|
||||
* "java.rmi.server.codebase". CodebaseAwareObjectInputStream is used by
|
||||
* HttpInvokerClientInterceptor (see the "codebaseUrl" property there).
|
||||
*
|
||||
* <p>Thanks to Lionel Mestre for suggesting the option and providing
|
||||
* a prototype!
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1.3
|
||||
* @see java.rmi.server.RMIClassLoader
|
||||
* @see RemoteInvocationSerializingExporter#createObjectInputStream
|
||||
* @see org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor#setCodebaseUrl
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
public class CodebaseAwareObjectInputStream extends ConfigurableObjectInputStream {
|
||||
|
||||
private final String codebaseUrl;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new CodebaseAwareObjectInputStream for the given InputStream and codebase.
|
||||
* @param in the InputStream to read from
|
||||
* @param codebaseUrl the codebase URL to load classes from if not found locally
|
||||
* (can consist of multiple URLs, separated by spaces)
|
||||
* @see java.io.ObjectInputStream#ObjectInputStream(java.io.InputStream)
|
||||
*/
|
||||
public CodebaseAwareObjectInputStream(InputStream in, String codebaseUrl) throws IOException {
|
||||
this(in, null, codebaseUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new CodebaseAwareObjectInputStream for the given InputStream and codebase.
|
||||
* @param in the InputStream to read from
|
||||
* @param classLoader the ClassLoader to use for loading local classes
|
||||
* (may be {@code null} to indicate RMI's default ClassLoader)
|
||||
* @param codebaseUrl the codebase URL to load classes from if not found locally
|
||||
* (can consist of multiple URLs, separated by spaces)
|
||||
* @see java.io.ObjectInputStream#ObjectInputStream(java.io.InputStream)
|
||||
*/
|
||||
public CodebaseAwareObjectInputStream(
|
||||
InputStream in, @Nullable ClassLoader classLoader, String codebaseUrl) throws IOException {
|
||||
|
||||
super(in, classLoader);
|
||||
this.codebaseUrl = codebaseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new CodebaseAwareObjectInputStream for the given InputStream and codebase.
|
||||
* @param in the InputStream to read from
|
||||
* @param classLoader the ClassLoader to use for loading local classes
|
||||
* (may be {@code null} to indicate RMI's default ClassLoader)
|
||||
* @param acceptProxyClasses whether to accept deserialization of proxy classes
|
||||
* (may be deactivated as a security measure)
|
||||
* @see java.io.ObjectInputStream#ObjectInputStream(java.io.InputStream)
|
||||
*/
|
||||
public CodebaseAwareObjectInputStream(
|
||||
InputStream in, @Nullable ClassLoader classLoader, boolean acceptProxyClasses) throws IOException {
|
||||
|
||||
super(in, classLoader, acceptProxyClasses);
|
||||
this.codebaseUrl = null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Class<?> resolveFallbackIfPossible(String className, ClassNotFoundException ex)
|
||||
throws IOException, ClassNotFoundException {
|
||||
|
||||
// If codebaseUrl is set, try to load the class with the RMIClassLoader.
|
||||
// Else, propagate the ClassNotFoundException.
|
||||
if (this.codebaseUrl == null) {
|
||||
throw ex;
|
||||
}
|
||||
return RMIClassLoader.loadClass(this.codebaseUrl, className);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClassLoader getFallbackClassLoader() throws IOException {
|
||||
return RMIClassLoader.getClassLoader(this.codebaseUrl);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,452 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.rmi;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.rmi.RemoteException;
|
||||
|
||||
import javax.naming.Context;
|
||||
import javax.naming.NamingException;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.jndi.JndiObjectLocator;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.remoting.RemoteConnectFailureException;
|
||||
import org.springframework.remoting.RemoteInvocationFailureException;
|
||||
import org.springframework.remoting.RemoteLookupFailureException;
|
||||
import org.springframework.remoting.support.DefaultRemoteInvocationFactory;
|
||||
import org.springframework.remoting.support.RemoteInvocation;
|
||||
import org.springframework.remoting.support.RemoteInvocationFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link org.aopalliance.intercept.MethodInterceptor} for accessing RMI services
|
||||
* from JNDI. Typically used for RMI-IIOP but can also be used for EJB home objects
|
||||
* (for example, a Stateful Session Bean home). In contrast to a plain JNDI lookup,
|
||||
* this accessor also performs narrowing through PortableRemoteObject.
|
||||
*
|
||||
* <p>With conventional RMI services, this invoker is typically used with the RMI
|
||||
* service interface. Alternatively, this invoker can also proxy a remote RMI 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 RMI stub will automatically get converted to
|
||||
* Spring's unchecked RemoteAccessException.
|
||||
*
|
||||
* <p>The JNDI environment can be specified as "jndiEnvironment" property,
|
||||
* or be configured in a {@code jndi.properties} file or as system properties.
|
||||
* For example:
|
||||
*
|
||||
* <pre class="code"><property name="jndiEnvironment">
|
||||
* <props>
|
||||
* <prop key="java.naming.factory.initial">com.sun.jndi.cosnaming.CNCtxFactory</prop>
|
||||
* <prop key="java.naming.provider.url">iiop://localhost:1050</prop>
|
||||
* </props>
|
||||
* </property></pre>
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
* @see #setJndiTemplate
|
||||
* @see #setJndiEnvironment
|
||||
* @see #setJndiName
|
||||
* @see JndiRmiServiceExporter
|
||||
* @see JndiRmiProxyFactoryBean
|
||||
* @see org.springframework.remoting.RemoteAccessException
|
||||
* @see java.rmi.RemoteException
|
||||
* @see java.rmi.Remote
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
public class JndiRmiClientInterceptor extends JndiObjectLocator implements MethodInterceptor, InitializingBean {
|
||||
|
||||
private Class<?> serviceInterface;
|
||||
|
||||
private RemoteInvocationFactory remoteInvocationFactory = new DefaultRemoteInvocationFactory();
|
||||
|
||||
private boolean lookupStubOnStartup = true;
|
||||
|
||||
private boolean cacheStub = true;
|
||||
|
||||
private boolean refreshStubOnConnectFailure = false;
|
||||
|
||||
private boolean exposeAccessContext = false;
|
||||
|
||||
private Object cachedStub;
|
||||
|
||||
private final Object stubMonitor = new Object();
|
||||
|
||||
|
||||
/**
|
||||
* Set the interface of the service to access.
|
||||
* The interface must be suitable for the particular service and remoting tool.
|
||||
* <p>Typically required to be able to create a suitable service proxy,
|
||||
* but can also be optional if the lookup returns a typed stub.
|
||||
*/
|
||||
public void setServiceInterface(Class<?> serviceInterface) {
|
||||
Assert.notNull(serviceInterface, "'serviceInterface' must not be null");
|
||||
Assert.isTrue(serviceInterface.isInterface(), "'serviceInterface' must be an interface");
|
||||
this.serviceInterface = serviceInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the interface of the service to access.
|
||||
*/
|
||||
public Class<?> getServiceInterface() {
|
||||
return this.serviceInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the RemoteInvocationFactory to use for this accessor.
|
||||
* Default is a {@link DefaultRemoteInvocationFactory}.
|
||||
* <p>A custom invocation factory can add further context information
|
||||
* to the invocation, for example user credentials.
|
||||
*/
|
||||
public void setRemoteInvocationFactory(RemoteInvocationFactory remoteInvocationFactory) {
|
||||
this.remoteInvocationFactory = remoteInvocationFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the RemoteInvocationFactory used by this accessor.
|
||||
*/
|
||||
public RemoteInvocationFactory getRemoteInvocationFactory() {
|
||||
return this.remoteInvocationFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to look up the RMI stub on startup. Default is "true".
|
||||
* <p>Can be turned off to allow for late start of the RMI server.
|
||||
* In this case, the RMI stub will be fetched on first access.
|
||||
* @see #setCacheStub
|
||||
*/
|
||||
public void setLookupStubOnStartup(boolean lookupStubOnStartup) {
|
||||
this.lookupStubOnStartup = lookupStubOnStartup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to cache the RMI stub once it has been located.
|
||||
* Default is "true".
|
||||
* <p>Can be turned off to allow for hot restart of the RMI server.
|
||||
* In this case, the RMI stub will be fetched for each invocation.
|
||||
* @see #setLookupStubOnStartup
|
||||
*/
|
||||
public void setCacheStub(boolean cacheStub) {
|
||||
this.cacheStub = cacheStub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to refresh the RMI stub on connect failure.
|
||||
* Default is "false".
|
||||
* <p>Can be turned on to allow for hot restart of the RMI server.
|
||||
* If a cached RMI stub throws an RMI exception that indicates a
|
||||
* remote connect failure, a fresh proxy will be fetched and the
|
||||
* invocation will be retried.
|
||||
* @see java.rmi.ConnectException
|
||||
* @see java.rmi.ConnectIOException
|
||||
* @see java.rmi.NoSuchObjectException
|
||||
*/
|
||||
public void setRefreshStubOnConnectFailure(boolean refreshStubOnConnectFailure) {
|
||||
this.refreshStubOnConnectFailure = refreshStubOnConnectFailure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to expose the JNDI environment context for all access to the target
|
||||
* RMI stub, i.e. for all method invocations on the exposed object reference.
|
||||
* <p>Default is "false", i.e. to only expose the JNDI context for object lookup.
|
||||
* Switch this flag to "true" in order to expose the JNDI environment (including
|
||||
* the authorization context) for each RMI invocation, as needed by WebLogic
|
||||
* for RMI stubs with authorization requirements.
|
||||
*/
|
||||
public void setExposeAccessContext(boolean exposeAccessContext) {
|
||||
this.exposeAccessContext = exposeAccessContext;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws NamingException {
|
||||
super.afterPropertiesSet();
|
||||
prepare();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the RMI stub on startup, if necessary.
|
||||
* @throws RemoteLookupFailureException if RMI stub creation failed
|
||||
* @see #setLookupStubOnStartup
|
||||
* @see #lookupStub
|
||||
*/
|
||||
public void prepare() throws RemoteLookupFailureException {
|
||||
// Cache RMI stub on initialization?
|
||||
if (this.lookupStubOnStartup) {
|
||||
Object remoteObj = lookupStub();
|
||||
if (logger.isDebugEnabled()) {
|
||||
if (remoteObj instanceof RmiInvocationHandler) {
|
||||
logger.debug("JNDI RMI object [" + getJndiName() + "] is an RMI invoker");
|
||||
}
|
||||
else if (getServiceInterface() != null) {
|
||||
boolean isImpl = getServiceInterface().isInstance(remoteObj);
|
||||
logger.debug("Using service interface [" + getServiceInterface().getName() +
|
||||
"] for JNDI RMI object [" + getJndiName() + "] - " +
|
||||
(!isImpl ? "not " : "") + "directly implemented");
|
||||
}
|
||||
}
|
||||
if (this.cacheStub) {
|
||||
this.cachedStub = remoteObj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the RMI stub, typically by looking it up.
|
||||
* <p>Called on interceptor initialization if "cacheStub" is "true";
|
||||
* else called for each invocation by {@link #getStub()}.
|
||||
* <p>The default implementation retrieves the service from the
|
||||
* JNDI environment. This can be overridden in subclasses.
|
||||
* @return the RMI stub to store in this interceptor
|
||||
* @throws RemoteLookupFailureException if RMI stub creation failed
|
||||
* @see #setCacheStub
|
||||
* @see #lookup
|
||||
*/
|
||||
protected Object lookupStub() throws RemoteLookupFailureException {
|
||||
try {
|
||||
return lookup();
|
||||
}
|
||||
catch (NamingException ex) {
|
||||
throw new RemoteLookupFailureException("JNDI lookup for RMI service [" + getJndiName() + "] failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the RMI stub to use. Called for each invocation.
|
||||
* <p>The default implementation returns the stub created on initialization,
|
||||
* if any. Else, it invokes {@link #lookupStub} to get a new stub for
|
||||
* each invocation. This can be overridden in subclasses, for example in
|
||||
* order to cache a stub for a given amount of time before recreating it,
|
||||
* or to test the stub whether it is still alive.
|
||||
* @return the RMI stub to use for an invocation
|
||||
* @throws NamingException if stub creation failed
|
||||
* @throws RemoteLookupFailureException if RMI stub creation failed
|
||||
*/
|
||||
protected Object getStub() throws NamingException, RemoteLookupFailureException {
|
||||
if (!this.cacheStub || (this.lookupStubOnStartup && !this.refreshStubOnConnectFailure)) {
|
||||
return (this.cachedStub != null ? this.cachedStub : lookupStub());
|
||||
}
|
||||
else {
|
||||
synchronized (this.stubMonitor) {
|
||||
if (this.cachedStub == null) {
|
||||
this.cachedStub = lookupStub();
|
||||
}
|
||||
return this.cachedStub;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetches an RMI stub and delegates to {@link #doInvoke}.
|
||||
* If configured to refresh on connect failure, it will call
|
||||
* {@link #refreshAndRetry} on corresponding RMI exceptions.
|
||||
* @see #getStub
|
||||
* @see #doInvoke
|
||||
* @see #refreshAndRetry
|
||||
* @see java.rmi.ConnectException
|
||||
* @see java.rmi.ConnectIOException
|
||||
* @see java.rmi.NoSuchObjectException
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
Object stub;
|
||||
try {
|
||||
stub = getStub();
|
||||
}
|
||||
catch (NamingException ex) {
|
||||
throw new RemoteLookupFailureException("JNDI lookup for RMI service [" + getJndiName() + "] failed", ex);
|
||||
}
|
||||
|
||||
Context ctx = (this.exposeAccessContext ? getJndiTemplate().getContext() : null);
|
||||
try {
|
||||
return doInvoke(invocation, stub);
|
||||
}
|
||||
catch (RemoteConnectFailureException ex) {
|
||||
return handleRemoteConnectFailure(invocation, ex);
|
||||
}
|
||||
catch (RemoteException ex) {
|
||||
if (isConnectFailure(ex)) {
|
||||
return handleRemoteConnectFailure(invocation, ex);
|
||||
}
|
||||
else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
getJndiTemplate().releaseContext(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given RMI exception indicates a connect failure.
|
||||
* <p>The default implementation delegates to
|
||||
* {@link RmiClientInterceptorUtils#isConnectFailure}.
|
||||
* @param ex the RMI exception to check
|
||||
* @return whether the exception should be treated as connect failure
|
||||
*/
|
||||
protected boolean isConnectFailure(RemoteException ex) {
|
||||
return RmiClientInterceptorUtils.isConnectFailure(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the stub and retry the remote invocation if necessary.
|
||||
* <p>If not configured to refresh on connect failure, this method
|
||||
* simply rethrows the original exception.
|
||||
* @param invocation the invocation that failed
|
||||
* @param ex the exception raised on remote invocation
|
||||
* @return the result value of the new invocation, if succeeded
|
||||
* @throws Throwable an exception raised by the new invocation, if failed too.
|
||||
*/
|
||||
private Object handleRemoteConnectFailure(MethodInvocation invocation, Exception ex) throws Throwable {
|
||||
if (this.refreshStubOnConnectFailure) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not connect to RMI service [" + getJndiName() + "] - retrying", ex);
|
||||
}
|
||||
else if (logger.isInfoEnabled()) {
|
||||
logger.info("Could not connect to RMI service [" + getJndiName() + "] - retrying");
|
||||
}
|
||||
return refreshAndRetry(invocation);
|
||||
}
|
||||
else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the RMI stub and retry the given invocation.
|
||||
* Called by invoke on connect failure.
|
||||
* @param invocation the AOP method invocation
|
||||
* @return the invocation result, if any
|
||||
* @throws Throwable in case of invocation failure
|
||||
* @see #invoke
|
||||
*/
|
||||
@Nullable
|
||||
protected Object refreshAndRetry(MethodInvocation invocation) throws Throwable {
|
||||
Object freshStub;
|
||||
synchronized (this.stubMonitor) {
|
||||
this.cachedStub = null;
|
||||
freshStub = lookupStub();
|
||||
if (this.cacheStub) {
|
||||
this.cachedStub = freshStub;
|
||||
}
|
||||
}
|
||||
return doInvoke(invocation, freshStub);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Perform the given invocation on the given RMI stub.
|
||||
* @param invocation the AOP method invocation
|
||||
* @param stub the RMI stub to invoke
|
||||
* @return the invocation result, if any
|
||||
* @throws Throwable in case of invocation failure
|
||||
*/
|
||||
@Nullable
|
||||
protected Object doInvoke(MethodInvocation invocation, Object stub) throws Throwable {
|
||||
if (stub instanceof RmiInvocationHandler) {
|
||||
// RMI invoker
|
||||
try {
|
||||
return doInvoke(invocation, (RmiInvocationHandler) stub);
|
||||
}
|
||||
catch (RemoteException ex) {
|
||||
throw convertRmiAccessException(ex, invocation.getMethod());
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
throw ex.getTargetException();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new RemoteInvocationFailureException("Invocation of method [" + invocation.getMethod() +
|
||||
"] failed in RMI service [" + getJndiName() + "]", ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// traditional RMI stub
|
||||
try {
|
||||
return RmiClientInterceptorUtils.invokeRemoteMethod(invocation, stub);
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
Throwable targetEx = ex.getTargetException();
|
||||
if (targetEx instanceof RemoteException) {
|
||||
throw convertRmiAccessException((RemoteException) targetEx, invocation.getMethod());
|
||||
}
|
||||
else {
|
||||
throw targetEx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the given AOP method invocation to the given {@link RmiInvocationHandler}.
|
||||
* <p>The default implementation delegates to {@link #createRemoteInvocation}.
|
||||
* @param methodInvocation the current AOP method invocation
|
||||
* @param invocationHandler the RmiInvocationHandler to apply the invocation to
|
||||
* @return the invocation result
|
||||
* @throws RemoteException in case of communication errors
|
||||
* @throws NoSuchMethodException if the method name could not be resolved
|
||||
* @throws IllegalAccessException if the method could not be accessed
|
||||
* @throws InvocationTargetException if the method invocation resulted in an exception
|
||||
* @see org.springframework.remoting.support.RemoteInvocation
|
||||
*/
|
||||
protected Object doInvoke(MethodInvocation methodInvocation, RmiInvocationHandler invocationHandler)
|
||||
throws RemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
|
||||
if (AopUtils.isToStringMethod(methodInvocation.getMethod())) {
|
||||
return "RMI invoker proxy for service URL [" + getJndiName() + "]";
|
||||
}
|
||||
|
||||
return invocationHandler.invoke(createRemoteInvocation(methodInvocation));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new RemoteInvocation object for the given AOP method invocation.
|
||||
* <p>The default implementation delegates to the configured
|
||||
* {@link #setRemoteInvocationFactory RemoteInvocationFactory}.
|
||||
* This can be overridden in subclasses in order to provide custom RemoteInvocation
|
||||
* subclasses, containing additional invocation parameters (e.g. user credentials).
|
||||
* <p>Note that it is preferable to build a custom RemoteInvocationFactory
|
||||
* as a reusable strategy, instead of overriding this method.
|
||||
* @param methodInvocation the current AOP method invocation
|
||||
* @return the RemoteInvocation object
|
||||
* @see RemoteInvocationFactory#createRemoteInvocation
|
||||
*/
|
||||
protected RemoteInvocation createRemoteInvocation(MethodInvocation methodInvocation) {
|
||||
return getRemoteInvocationFactory().createRemoteInvocation(methodInvocation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given RMI RemoteException that happened during remote access
|
||||
* to Spring's RemoteAccessException if the method signature does not declare
|
||||
* RemoteException. Else, return the original RemoteException.
|
||||
* @param method the invoked method
|
||||
* @param ex the RemoteException that happened
|
||||
* @return the exception to be thrown to the caller
|
||||
*/
|
||||
private Exception convertRmiAccessException(RemoteException ex, Method method) {
|
||||
return RmiClientInterceptorUtils.convertRmiAccessException(method, ex, isConnectFailure(ex), getJndiName());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.rmi;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link FactoryBean} for RMI proxies from JNDI.
|
||||
*
|
||||
* <p>Typically used for RMI-IIOP (CORBA), but can also be used for EJB home objects
|
||||
* (for example, a Stateful Session Bean home). In contrast to a plain JNDI lookup,
|
||||
* this accessor also performs narrowing through {@link javax.rmi.PortableRemoteObject}.
|
||||
*
|
||||
* <p>With conventional RMI services, this invoker is typically used with the RMI
|
||||
* service interface. Alternatively, this invoker can also proxy a remote RMI 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 RMI stub will automatically get converted to
|
||||
* Spring's unchecked RemoteAccessException.
|
||||
*
|
||||
* <p>The JNDI environment can be specified as "jndiEnvironment" property,
|
||||
* or be configured in a {@code jndi.properties} file or as system properties.
|
||||
* For example:
|
||||
*
|
||||
* <pre class="code"><property name="jndiEnvironment">
|
||||
* <props>
|
||||
* <prop key="java.naming.factory.initial">com.sun.jndi.cosnaming.CNCtxFactory</prop>
|
||||
* <prop key="java.naming.provider.url">iiop://localhost:1050</prop>
|
||||
* </props>
|
||||
* </property></pre>
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
* @see #setServiceInterface
|
||||
* @see #setJndiName
|
||||
* @see #setJndiTemplate
|
||||
* @see #setJndiEnvironment
|
||||
* @see #setJndiName
|
||||
* @see JndiRmiServiceExporter
|
||||
* @see org.springframework.remoting.RemoteAccessException
|
||||
* @see java.rmi.RemoteException
|
||||
* @see java.rmi.Remote
|
||||
* @see javax.rmi.PortableRemoteObject#narrow
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
public class JndiRmiProxyFactoryBean extends JndiRmiClientInterceptor
|
||||
implements FactoryBean<Object>, BeanClassLoaderAware {
|
||||
|
||||
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
|
||||
|
||||
private Object serviceProxy;
|
||||
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.beanClassLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws NamingException {
|
||||
super.afterPropertiesSet();
|
||||
Class<?> ifc = getServiceInterface();
|
||||
Assert.notNull(ifc, "Property 'serviceInterface' is required");
|
||||
this.serviceProxy = new ProxyFactory(ifc, this).getProxy(this.beanClassLoader);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object getObject() {
|
||||
return this.serviceProxy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return getServiceInterface();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.rmi;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.rmi.Remote;
|
||||
import java.rmi.RemoteException;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.jndi.JndiTemplate;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Service exporter which binds RMI services to JNDI.
|
||||
* Typically used for RMI-IIOP (CORBA).
|
||||
*
|
||||
* <p>Exports services via the {@link javax.rmi.PortableRemoteObject} class.
|
||||
* You need to run "rmic" with the "-iiop" option to generate corresponding
|
||||
* stubs and skeletons for each exported service.
|
||||
*
|
||||
* <p>Also supports exposing any non-RMI service via RMI invokers, to be accessed
|
||||
* via {@link JndiRmiClientInterceptor} / {@link JndiRmiProxyFactoryBean}'s
|
||||
* automatic detection of such invokers.
|
||||
*
|
||||
* <p>With an RMI invoker, RMI communication works on the {@link RmiInvocationHandler}
|
||||
* level, needing only one stub for any service. Service interfaces do not have to
|
||||
* extend {@code java.rmi.Remote} or throw {@code java.rmi.RemoteException}
|
||||
* on all methods, but in and out parameters have to be serializable.
|
||||
*
|
||||
* <p>The JNDI environment can be specified as "jndiEnvironment" bean property,
|
||||
* or be configured in a {@code jndi.properties} file or as system properties.
|
||||
* For example:
|
||||
*
|
||||
* <pre class="code"><property name="jndiEnvironment">
|
||||
* <props>
|
||||
* <prop key="java.naming.factory.initial">com.sun.jndi.cosnaming.CNCtxFactory</prop>
|
||||
* <prop key="java.naming.provider.url">iiop://localhost:1050</prop>
|
||||
* </props>
|
||||
* </property></pre>
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
* @see #setService
|
||||
* @see #setJndiTemplate
|
||||
* @see #setJndiEnvironment
|
||||
* @see #setJndiName
|
||||
* @see JndiRmiClientInterceptor
|
||||
* @see JndiRmiProxyFactoryBean
|
||||
* @see javax.rmi.PortableRemoteObject#exportObject
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
public class JndiRmiServiceExporter extends RmiBasedExporter implements InitializingBean, DisposableBean {
|
||||
|
||||
@Nullable
|
||||
private static Method exportObject;
|
||||
|
||||
@Nullable
|
||||
private static Method unexportObject;
|
||||
|
||||
static {
|
||||
try {
|
||||
Class<?> portableRemoteObject =
|
||||
JndiRmiServiceExporter.class.getClassLoader().loadClass("javax.rmi.PortableRemoteObject");
|
||||
exportObject = portableRemoteObject.getMethod("exportObject", Remote.class);
|
||||
unexportObject = portableRemoteObject.getMethod("unexportObject", Remote.class);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// java.corba module not available on JDK 9+
|
||||
exportObject = null;
|
||||
unexportObject = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private JndiTemplate jndiTemplate = new JndiTemplate();
|
||||
|
||||
private String jndiName;
|
||||
|
||||
private Remote exportedObject;
|
||||
|
||||
|
||||
/**
|
||||
* Set the JNDI template to use for JNDI lookups.
|
||||
* You can also specify JNDI environment settings via "jndiEnvironment".
|
||||
* @see #setJndiEnvironment
|
||||
*/
|
||||
public void setJndiTemplate(JndiTemplate jndiTemplate) {
|
||||
this.jndiTemplate = (jndiTemplate != null ? jndiTemplate : new JndiTemplate());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JNDI environment to use for JNDI lookups.
|
||||
* Creates a JndiTemplate with the given environment settings.
|
||||
* @see #setJndiTemplate
|
||||
*/
|
||||
public void setJndiEnvironment(Properties jndiEnvironment) {
|
||||
this.jndiTemplate = new JndiTemplate(jndiEnvironment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JNDI name of the exported RMI service.
|
||||
*/
|
||||
public void setJndiName(String jndiName) {
|
||||
this.jndiName = jndiName;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws NamingException, RemoteException {
|
||||
prepare();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize this service exporter, binding the specified service to JNDI.
|
||||
* @throws NamingException if service binding failed
|
||||
* @throws RemoteException if service export failed
|
||||
*/
|
||||
public void prepare() throws NamingException, RemoteException {
|
||||
if (this.jndiName == null) {
|
||||
throw new IllegalArgumentException("Property 'jndiName' is required");
|
||||
}
|
||||
|
||||
// Initialize and cache exported object.
|
||||
this.exportedObject = getObjectToExport();
|
||||
invokePortableRemoteObject(exportObject);
|
||||
|
||||
rebind();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebind the specified service to JNDI, for recovering in case
|
||||
* of the target registry having been restarted.
|
||||
* @throws NamingException if service binding failed
|
||||
*/
|
||||
public void rebind() throws NamingException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Binding RMI service to JNDI location [" + this.jndiName + "]");
|
||||
}
|
||||
this.jndiTemplate.rebind(this.jndiName, this.exportedObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbind the RMI service from JNDI on bean factory shutdown.
|
||||
*/
|
||||
@Override
|
||||
public void destroy() throws NamingException, RemoteException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Unbinding RMI service from JNDI location [" + this.jndiName + "]");
|
||||
}
|
||||
this.jndiTemplate.unbind(this.jndiName);
|
||||
invokePortableRemoteObject(unexportObject);
|
||||
}
|
||||
|
||||
|
||||
private void invokePortableRemoteObject(@Nullable Method method) throws RemoteException {
|
||||
if (method != null) {
|
||||
try {
|
||||
method.invoke(null, this.exportedObject);
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
Throwable targetEx = ex.getTargetException();
|
||||
if (targetEx instanceof RemoteException) {
|
||||
throw (RemoteException) targetEx;
|
||||
}
|
||||
ReflectionUtils.rethrowRuntimeException(targetEx);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException("PortableRemoteObject invocation failed", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.rmi;
|
||||
|
||||
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.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.remoting.support.RemoteInvocation;
|
||||
import org.springframework.remoting.support.RemoteInvocationBasedExporter;
|
||||
import org.springframework.remoting.support.RemoteInvocationResult;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Abstract base class for remote service exporters that explicitly deserialize
|
||||
* {@link org.springframework.remoting.support.RemoteInvocation} objects and serialize
|
||||
* {@link org.springframework.remoting.support.RemoteInvocationResult} objects,
|
||||
* for example Spring's HTTP invoker.
|
||||
*
|
||||
* <p>Provides template methods for {@code ObjectInputStream} and
|
||||
* {@code ObjectOutputStream} handling.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5.1
|
||||
* @see java.io.ObjectInputStream
|
||||
* @see java.io.ObjectOutputStream
|
||||
* @see #doReadRemoteInvocation
|
||||
* @see #doWriteRemoteInvocationResult
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class RemoteInvocationSerializingExporter extends RemoteInvocationBasedExporter
|
||||
implements InitializingBean {
|
||||
|
||||
/**
|
||||
* Default content type: "application/x-java-serialized-object".
|
||||
*/
|
||||
public static final String CONTENT_TYPE_SERIALIZED_OBJECT = "application/x-java-serialized-object";
|
||||
|
||||
|
||||
private String contentType = CONTENT_TYPE_SERIALIZED_OBJECT;
|
||||
|
||||
private boolean acceptProxyClasses = true;
|
||||
|
||||
private Object proxy;
|
||||
|
||||
|
||||
/**
|
||||
* Specify the content type to use for sending remote invocation responses.
|
||||
* <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 remote invocation responses.
|
||||
*/
|
||||
public String getContentType() {
|
||||
return this.contentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to accept deserialization of proxy classes.
|
||||
* <p>Default is "true". May be deactivated as a security measure.
|
||||
*/
|
||||
public void setAcceptProxyClasses(boolean acceptProxyClasses) {
|
||||
this.acceptProxyClasses = acceptProxyClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether to accept deserialization of proxy classes.
|
||||
*/
|
||||
public boolean isAcceptProxyClasses() {
|
||||
return this.acceptProxyClasses;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
prepare();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize this service exporter.
|
||||
*/
|
||||
public void prepare() {
|
||||
this.proxy = getProxyForService();
|
||||
}
|
||||
|
||||
protected final Object getProxy() {
|
||||
if (this.proxy == null) {
|
||||
throw new IllegalStateException(ClassUtils.getShortName(getClass()) + " has not been initialized");
|
||||
}
|
||||
return this.proxy;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create an ObjectInputStream for the given InputStream.
|
||||
* <p>The default implementation creates a Spring {@link CodebaseAwareObjectInputStream}.
|
||||
* @param is the InputStream to read from
|
||||
* @return the new ObjectInputStream instance to use
|
||||
* @throws java.io.IOException if creation of the ObjectInputStream failed
|
||||
*/
|
||||
protected ObjectInputStream createObjectInputStream(InputStream is) throws IOException {
|
||||
return new CodebaseAwareObjectInputStream(is, getBeanClassLoader(), isAcceptProxyClasses());
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the actual reading of an invocation result object from the
|
||||
* given ObjectInputStream.
|
||||
* <p>The default implementation simply calls
|
||||
* {@link java.io.ObjectInputStream#readObject()}.
|
||||
* 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 java.io.IOException in case of I/O failure
|
||||
* @throws ClassNotFoundException if case of a transferred class not
|
||||
* being found in the local ClassLoader
|
||||
*/
|
||||
protected RemoteInvocation doReadRemoteInvocation(ObjectInputStream ois)
|
||||
throws IOException, ClassNotFoundException {
|
||||
|
||||
Object obj = ois.readObject();
|
||||
if (!(obj instanceof RemoteInvocation)) {
|
||||
throw new RemoteException("Deserialized object needs to be assignable to type [" +
|
||||
RemoteInvocation.class.getName() + "]: " + ClassUtils.getDescriptiveType(obj));
|
||||
}
|
||||
return (RemoteInvocation) obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an ObjectOutputStream for the given OutputStream.
|
||||
* <p>The default implementation creates a plain
|
||||
* {@link java.io.ObjectOutputStream}.
|
||||
* @param os the OutputStream to write to
|
||||
* @return the new ObjectOutputStream instance to use
|
||||
* @throws java.io.IOException if creation of the ObjectOutputStream failed
|
||||
*/
|
||||
protected ObjectOutputStream createObjectOutputStream(OutputStream os) throws IOException {
|
||||
return new ObjectOutputStream(os);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the actual writing of the given invocation result object
|
||||
* to the given ObjectOutputStream.
|
||||
* <p>The default implementation simply calls
|
||||
* {@link java.io.ObjectOutputStream#writeObject}.
|
||||
* Can be overridden for serialization of a custom wrapper object rather
|
||||
* than the plain invocation, for example an encryption-aware holder.
|
||||
* @param result the RemoteInvocationResult object
|
||||
* @param oos the ObjectOutputStream to write to
|
||||
* @throws java.io.IOException if thrown by I/O methods
|
||||
*/
|
||||
protected void doWriteRemoteInvocationResult(RemoteInvocationResult result, ObjectOutputStream oos)
|
||||
throws IOException {
|
||||
|
||||
oos.writeObject(result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.rmi;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.rmi.Remote;
|
||||
|
||||
import org.springframework.remoting.support.RemoteInvocation;
|
||||
import org.springframework.remoting.support.RemoteInvocationBasedExporter;
|
||||
|
||||
/**
|
||||
* Convenient superclass for RMI-based remote exporters. Provides a facility
|
||||
* to automatically wrap a given plain Java service object with an
|
||||
* RmiInvocationWrapper, exposing the {@link RmiInvocationHandler} remote interface.
|
||||
*
|
||||
* <p>Using the RMI invoker mechanism, RMI communication operates at the {@link RmiInvocationHandler}
|
||||
* level, sharing a common invoker stub for any number of services. Service interfaces are <i>not</i>
|
||||
* required to extend {@code java.rmi.Remote} or declare {@code java.rmi.RemoteException}
|
||||
* on all service methods. However, in and out parameters still have to be serializable.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.2.5
|
||||
* @see RmiServiceExporter
|
||||
* @see JndiRmiServiceExporter
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class RmiBasedExporter extends RemoteInvocationBasedExporter {
|
||||
|
||||
/**
|
||||
* Determine the object to export: either the service object itself
|
||||
* or a RmiInvocationWrapper in case of a non-RMI service object.
|
||||
* @return the RMI object to export
|
||||
* @see #setService
|
||||
* @see #setServiceInterface
|
||||
*/
|
||||
protected Remote getObjectToExport() {
|
||||
// determine remote object
|
||||
if (getService() instanceof Remote &&
|
||||
(getServiceInterface() == null || Remote.class.isAssignableFrom(getServiceInterface()))) {
|
||||
// conventional RMI service
|
||||
return (Remote) getService();
|
||||
}
|
||||
else {
|
||||
// RMI invoker
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("RMI service [" + getService() + "] is an RMI invoker");
|
||||
}
|
||||
return new RmiInvocationWrapper(getProxyForService(), this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Redefined here to be visible to RmiInvocationWrapper.
|
||||
* Simply delegates to the corresponding superclass method.
|
||||
*/
|
||||
@Override
|
||||
protected Object invoke(RemoteInvocation invocation, Object targetObject)
|
||||
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
|
||||
return super.invoke(invocation, targetObject);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,424 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.rmi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLStreamHandler;
|
||||
import java.rmi.Naming;
|
||||
import java.rmi.NotBoundException;
|
||||
import java.rmi.Remote;
|
||||
import java.rmi.RemoteException;
|
||||
import java.rmi.registry.LocateRegistry;
|
||||
import java.rmi.registry.Registry;
|
||||
import java.rmi.server.RMIClientSocketFactory;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.remoting.RemoteConnectFailureException;
|
||||
import org.springframework.remoting.RemoteInvocationFailureException;
|
||||
import org.springframework.remoting.RemoteLookupFailureException;
|
||||
import org.springframework.remoting.support.RemoteInvocationBasedAccessor;
|
||||
import org.springframework.remoting.support.RemoteInvocationUtils;
|
||||
|
||||
/**
|
||||
* {@link org.aopalliance.intercept.MethodInterceptor} for accessing conventional
|
||||
* RMI services or RMI invokers. The service URL must be a valid RMI URL
|
||||
* (e.g. "rmi://localhost:1099/myservice").
|
||||
*
|
||||
* <p>RMI invokers work at the RmiInvocationHandler level, needing only one stub for
|
||||
* any service. Service interfaces do not have to extend {@code java.rmi.Remote}
|
||||
* or throw {@code java.rmi.RemoteException}. Spring's unchecked
|
||||
* RemoteAccessException will be thrown on remote invocation failure.
|
||||
* Of course, in and out parameters have to be serializable.
|
||||
*
|
||||
* <p>With conventional RMI services, this invoker is typically used with the RMI
|
||||
* service interface. Alternatively, this invoker can also proxy a remote RMI 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 RMI stub will automatically get converted to
|
||||
* Spring's unchecked RemoteAccessException.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 29.09.2003
|
||||
* @see RmiServiceExporter
|
||||
* @see RmiProxyFactoryBean
|
||||
* @see RmiInvocationHandler
|
||||
* @see org.springframework.remoting.RemoteAccessException
|
||||
* @see java.rmi.RemoteException
|
||||
* @see java.rmi.Remote
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
public class RmiClientInterceptor extends RemoteInvocationBasedAccessor
|
||||
implements MethodInterceptor {
|
||||
|
||||
private boolean lookupStubOnStartup = true;
|
||||
|
||||
private boolean cacheStub = true;
|
||||
|
||||
private boolean refreshStubOnConnectFailure = false;
|
||||
|
||||
private RMIClientSocketFactory registryClientSocketFactory;
|
||||
|
||||
private Remote cachedStub;
|
||||
|
||||
private final Object stubMonitor = new Object();
|
||||
|
||||
|
||||
/**
|
||||
* Set whether to look up the RMI stub on startup. Default is "true".
|
||||
* <p>Can be turned off to allow for late start of the RMI server.
|
||||
* In this case, the RMI stub will be fetched on first access.
|
||||
* @see #setCacheStub
|
||||
*/
|
||||
public void setLookupStubOnStartup(boolean lookupStubOnStartup) {
|
||||
this.lookupStubOnStartup = lookupStubOnStartup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to cache the RMI stub once it has been located.
|
||||
* Default is "true".
|
||||
* <p>Can be turned off to allow for hot restart of the RMI server.
|
||||
* In this case, the RMI stub will be fetched for each invocation.
|
||||
* @see #setLookupStubOnStartup
|
||||
*/
|
||||
public void setCacheStub(boolean cacheStub) {
|
||||
this.cacheStub = cacheStub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to refresh the RMI stub on connect failure.
|
||||
* Default is "false".
|
||||
* <p>Can be turned on to allow for hot restart of the RMI server.
|
||||
* If a cached RMI stub throws an RMI exception that indicates a
|
||||
* remote connect failure, a fresh proxy will be fetched and the
|
||||
* invocation will be retried.
|
||||
* @see java.rmi.ConnectException
|
||||
* @see java.rmi.ConnectIOException
|
||||
* @see java.rmi.NoSuchObjectException
|
||||
*/
|
||||
public void setRefreshStubOnConnectFailure(boolean refreshStubOnConnectFailure) {
|
||||
this.refreshStubOnConnectFailure = refreshStubOnConnectFailure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom RMI client socket factory to use for accessing the RMI registry.
|
||||
* @see java.rmi.server.RMIClientSocketFactory
|
||||
* @see java.rmi.registry.LocateRegistry#getRegistry(String, int, RMIClientSocketFactory)
|
||||
*/
|
||||
public void setRegistryClientSocketFactory(RMIClientSocketFactory registryClientSocketFactory) {
|
||||
this.registryClientSocketFactory = registryClientSocketFactory;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
super.afterPropertiesSet();
|
||||
prepare();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches RMI stub on startup, if necessary.
|
||||
* @throws RemoteLookupFailureException if RMI stub creation failed
|
||||
* @see #setLookupStubOnStartup
|
||||
* @see #lookupStub
|
||||
*/
|
||||
public void prepare() throws RemoteLookupFailureException {
|
||||
// Cache RMI stub on initialization?
|
||||
if (this.lookupStubOnStartup) {
|
||||
Remote remoteObj = lookupStub();
|
||||
if (logger.isDebugEnabled()) {
|
||||
if (remoteObj instanceof RmiInvocationHandler) {
|
||||
logger.debug("RMI stub [" + getServiceUrl() + "] is an RMI invoker");
|
||||
}
|
||||
else if (getServiceInterface() != null) {
|
||||
boolean isImpl = getServiceInterface().isInstance(remoteObj);
|
||||
logger.debug("Using service interface [" + getServiceInterface().getName() +
|
||||
"] for RMI stub [" + getServiceUrl() + "] - " +
|
||||
(!isImpl ? "not " : "") + "directly implemented");
|
||||
}
|
||||
}
|
||||
if (this.cacheStub) {
|
||||
this.cachedStub = remoteObj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the RMI stub, typically by looking it up.
|
||||
* <p>Called on interceptor initialization if "cacheStub" is "true";
|
||||
* else called for each invocation by {@link #getStub()}.
|
||||
* <p>The default implementation looks up the service URL via
|
||||
* {@code java.rmi.Naming}. This can be overridden in subclasses.
|
||||
* @return the RMI stub to store in this interceptor
|
||||
* @throws RemoteLookupFailureException if RMI stub creation failed
|
||||
* @see #setCacheStub
|
||||
* @see java.rmi.Naming#lookup
|
||||
*/
|
||||
protected Remote lookupStub() throws RemoteLookupFailureException {
|
||||
try {
|
||||
Remote stub = null;
|
||||
if (this.registryClientSocketFactory != null) {
|
||||
// RMIClientSocketFactory specified for registry access.
|
||||
// Unfortunately, due to RMI API limitations, this means
|
||||
// that we need to parse the RMI URL ourselves and perform
|
||||
// straight LocateRegistry.getRegistry/Registry.lookup calls.
|
||||
URL url = new URL(null, getServiceUrl(), new DummyURLStreamHandler());
|
||||
String protocol = url.getProtocol();
|
||||
if (protocol != null && !"rmi".equals(protocol)) {
|
||||
throw new MalformedURLException("Invalid URL scheme '" + protocol + "'");
|
||||
}
|
||||
String host = url.getHost();
|
||||
int port = url.getPort();
|
||||
String name = url.getPath();
|
||||
if (name != null && name.startsWith("/")) {
|
||||
name = name.substring(1);
|
||||
}
|
||||
Registry registry = LocateRegistry.getRegistry(host, port, this.registryClientSocketFactory);
|
||||
stub = registry.lookup(name);
|
||||
}
|
||||
else {
|
||||
// Can proceed with standard RMI lookup API...
|
||||
stub = Naming.lookup(getServiceUrl());
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Located RMI stub with URL [" + getServiceUrl() + "]");
|
||||
}
|
||||
return stub;
|
||||
}
|
||||
catch (MalformedURLException ex) {
|
||||
throw new RemoteLookupFailureException("Service URL [" + getServiceUrl() + "] is invalid", ex);
|
||||
}
|
||||
catch (NotBoundException ex) {
|
||||
throw new RemoteLookupFailureException(
|
||||
"Could not find RMI service [" + getServiceUrl() + "] in RMI registry", ex);
|
||||
}
|
||||
catch (RemoteException ex) {
|
||||
throw new RemoteLookupFailureException("Lookup of RMI stub failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the RMI stub to use. Called for each invocation.
|
||||
* <p>The default implementation returns the stub created on initialization,
|
||||
* if any. Else, it invokes {@link #lookupStub} to get a new stub for
|
||||
* each invocation. This can be overridden in subclasses, for example in
|
||||
* order to cache a stub for a given amount of time before recreating it,
|
||||
* or to test the stub whether it is still alive.
|
||||
* @return the RMI stub to use for an invocation
|
||||
* @throws RemoteLookupFailureException if RMI stub creation failed
|
||||
* @see #lookupStub
|
||||
*/
|
||||
protected Remote getStub() throws RemoteLookupFailureException {
|
||||
if (!this.cacheStub || (this.lookupStubOnStartup && !this.refreshStubOnConnectFailure)) {
|
||||
return (this.cachedStub != null ? this.cachedStub : lookupStub());
|
||||
}
|
||||
else {
|
||||
synchronized (this.stubMonitor) {
|
||||
if (this.cachedStub == null) {
|
||||
this.cachedStub = lookupStub();
|
||||
}
|
||||
return this.cachedStub;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetches an RMI stub and delegates to {@code doInvoke}.
|
||||
* If configured to refresh on connect failure, it will call
|
||||
* {@link #refreshAndRetry} on corresponding RMI exceptions.
|
||||
* @see #getStub
|
||||
* @see #doInvoke(MethodInvocation, Remote)
|
||||
* @see #refreshAndRetry
|
||||
* @see java.rmi.ConnectException
|
||||
* @see java.rmi.ConnectIOException
|
||||
* @see java.rmi.NoSuchObjectException
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
Remote stub = getStub();
|
||||
try {
|
||||
return doInvoke(invocation, stub);
|
||||
}
|
||||
catch (RemoteConnectFailureException ex) {
|
||||
return handleRemoteConnectFailure(invocation, ex);
|
||||
}
|
||||
catch (RemoteException ex) {
|
||||
if (isConnectFailure(ex)) {
|
||||
return handleRemoteConnectFailure(invocation, ex);
|
||||
}
|
||||
else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given RMI exception indicates a connect failure.
|
||||
* <p>The default implementation delegates to
|
||||
* {@link RmiClientInterceptorUtils#isConnectFailure}.
|
||||
* @param ex the RMI exception to check
|
||||
* @return whether the exception should be treated as connect failure
|
||||
*/
|
||||
protected boolean isConnectFailure(RemoteException ex) {
|
||||
return RmiClientInterceptorUtils.isConnectFailure(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the stub and retry the remote invocation if necessary.
|
||||
* <p>If not configured to refresh on connect failure, this method
|
||||
* simply rethrows the original exception.
|
||||
* @param invocation the invocation that failed
|
||||
* @param ex the exception raised on remote invocation
|
||||
* @return the result value of the new invocation, if succeeded
|
||||
* @throws Throwable an exception raised by the new invocation,
|
||||
* if it failed as well
|
||||
* @see #setRefreshStubOnConnectFailure
|
||||
* @see #doInvoke
|
||||
*/
|
||||
@Nullable
|
||||
private Object handleRemoteConnectFailure(MethodInvocation invocation, Exception ex) throws Throwable {
|
||||
if (this.refreshStubOnConnectFailure) {
|
||||
String msg = "Could not connect to RMI service [" + getServiceUrl() + "] - retrying";
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.warn(msg, ex);
|
||||
}
|
||||
else if (logger.isWarnEnabled()) {
|
||||
logger.warn(msg);
|
||||
}
|
||||
return refreshAndRetry(invocation);
|
||||
}
|
||||
else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the RMI stub and retry the given invocation.
|
||||
* Called by invoke on connect failure.
|
||||
* @param invocation the AOP method invocation
|
||||
* @return the invocation result, if any
|
||||
* @throws Throwable in case of invocation failure
|
||||
* @see #invoke
|
||||
*/
|
||||
@Nullable
|
||||
protected Object refreshAndRetry(MethodInvocation invocation) throws Throwable {
|
||||
Remote freshStub = null;
|
||||
synchronized (this.stubMonitor) {
|
||||
this.cachedStub = null;
|
||||
freshStub = lookupStub();
|
||||
if (this.cacheStub) {
|
||||
this.cachedStub = freshStub;
|
||||
}
|
||||
}
|
||||
return doInvoke(invocation, freshStub);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the given invocation on the given RMI stub.
|
||||
* @param invocation the AOP method invocation
|
||||
* @param stub the RMI stub to invoke
|
||||
* @return the invocation result, if any
|
||||
* @throws Throwable in case of invocation failure
|
||||
*/
|
||||
@Nullable
|
||||
protected Object doInvoke(MethodInvocation invocation, Remote stub) throws Throwable {
|
||||
if (stub instanceof RmiInvocationHandler) {
|
||||
// RMI invoker
|
||||
try {
|
||||
return doInvoke(invocation, (RmiInvocationHandler) stub);
|
||||
}
|
||||
catch (RemoteException ex) {
|
||||
throw RmiClientInterceptorUtils.convertRmiAccessException(
|
||||
invocation.getMethod(), ex, isConnectFailure(ex), getServiceUrl());
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
Throwable exToThrow = ex.getTargetException();
|
||||
RemoteInvocationUtils.fillInClientStackTraceIfPossible(exToThrow);
|
||||
throw exToThrow;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new RemoteInvocationFailureException("Invocation of method [" + invocation.getMethod() +
|
||||
"] failed in RMI service [" + getServiceUrl() + "]", ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// traditional RMI stub
|
||||
try {
|
||||
return RmiClientInterceptorUtils.invokeRemoteMethod(invocation, stub);
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
Throwable targetEx = ex.getTargetException();
|
||||
if (targetEx instanceof RemoteException) {
|
||||
RemoteException rex = (RemoteException) targetEx;
|
||||
throw RmiClientInterceptorUtils.convertRmiAccessException(
|
||||
invocation.getMethod(), rex, isConnectFailure(rex), getServiceUrl());
|
||||
}
|
||||
else {
|
||||
throw targetEx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the given AOP method invocation to the given {@link RmiInvocationHandler}.
|
||||
* <p>The default implementation delegates to {@link #createRemoteInvocation}.
|
||||
* @param methodInvocation the current AOP method invocation
|
||||
* @param invocationHandler the RmiInvocationHandler to apply the invocation to
|
||||
* @return the invocation result
|
||||
* @throws RemoteException in case of communication errors
|
||||
* @throws NoSuchMethodException if the method name could not be resolved
|
||||
* @throws IllegalAccessException if the method could not be accessed
|
||||
* @throws InvocationTargetException if the method invocation resulted in an exception
|
||||
* @see org.springframework.remoting.support.RemoteInvocation
|
||||
*/
|
||||
@Nullable
|
||||
protected Object doInvoke(MethodInvocation methodInvocation, RmiInvocationHandler invocationHandler)
|
||||
throws RemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
|
||||
if (AopUtils.isToStringMethod(methodInvocation.getMethod())) {
|
||||
return "RMI invoker proxy for service URL [" + getServiceUrl() + "]";
|
||||
}
|
||||
|
||||
return invocationHandler.invoke(createRemoteInvocation(methodInvocation));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dummy URLStreamHandler that's just specified to suppress the standard
|
||||
* {@code java.net.URL} URLStreamHandler lookup, to be able to
|
||||
* use the standard URL class for parsing "rmi:..." URLs.
|
||||
*/
|
||||
private static class DummyURLStreamHandler extends URLStreamHandler {
|
||||
|
||||
@Override
|
||||
protected URLConnection openConnection(URL url) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.rmi;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.SocketException;
|
||||
import java.rmi.ConnectException;
|
||||
import java.rmi.ConnectIOException;
|
||||
import java.rmi.NoSuchObjectException;
|
||||
import java.rmi.RemoteException;
|
||||
import java.rmi.StubNotFoundException;
|
||||
import java.rmi.UnknownHostException;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.remoting.RemoteAccessException;
|
||||
import org.springframework.remoting.RemoteConnectFailureException;
|
||||
import org.springframework.remoting.RemoteProxyFailureException;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Factored-out methods for performing invocations within an RMI client.
|
||||
* Can handle both RMI and non-RMI service interfaces working on an RMI stub.
|
||||
*
|
||||
* <p>Note: This is an SPI class, not intended to be used by applications.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class RmiClientInterceptorUtils {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(RmiClientInterceptorUtils.class);
|
||||
|
||||
|
||||
/**
|
||||
* Perform a raw method invocation on the given RMI stub,
|
||||
* letting reflection exceptions through as-is.
|
||||
* @param invocation the AOP MethodInvocation
|
||||
* @param stub the RMI stub
|
||||
* @return the invocation result, if any
|
||||
* @throws InvocationTargetException if thrown by reflection
|
||||
*/
|
||||
@Nullable
|
||||
public static Object invokeRemoteMethod(MethodInvocation invocation, Object stub)
|
||||
throws InvocationTargetException {
|
||||
|
||||
Method method = invocation.getMethod();
|
||||
try {
|
||||
if (method.getDeclaringClass().isInstance(stub)) {
|
||||
// directly implemented
|
||||
return method.invoke(stub, invocation.getArguments());
|
||||
}
|
||||
else {
|
||||
// not directly implemented
|
||||
Method stubMethod = stub.getClass().getMethod(method.getName(), method.getParameterTypes());
|
||||
return stubMethod.invoke(stub, invocation.getArguments());
|
||||
}
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
throw new RemoteProxyFailureException("No matching RMI stub method found for: " + method, ex);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new RemoteProxyFailureException("Invocation of RMI stub method failed: " + method, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the given arbitrary exception that happened during remote access
|
||||
* in either a RemoteException or a Spring RemoteAccessException (if the
|
||||
* method signature does not support RemoteException).
|
||||
* <p>Only call this for remote access exceptions, not for exceptions
|
||||
* thrown by the target service itself!
|
||||
* @param method the invoked method
|
||||
* @param ex the exception that happened, to be used as cause for the
|
||||
* RemoteAccessException or RemoteException
|
||||
* @param message the message for the RemoteAccessException respectively
|
||||
* RemoteException
|
||||
* @return the exception to be thrown to the caller
|
||||
*/
|
||||
public static Exception convertRmiAccessException(Method method, Throwable ex, String message) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(message, ex);
|
||||
}
|
||||
if (ReflectionUtils.declaresException(method, RemoteException.class)) {
|
||||
return new RemoteException(message, ex);
|
||||
}
|
||||
else {
|
||||
return new RemoteAccessException(message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given RemoteException that happened during remote access
|
||||
* to Spring's RemoteAccessException if the method signature does not
|
||||
* support RemoteException. Else, return the original RemoteException.
|
||||
* @param method the invoked method
|
||||
* @param ex the RemoteException that happened
|
||||
* @param serviceName the name of the service (for debugging purposes)
|
||||
* @return the exception to be thrown to the caller
|
||||
*/
|
||||
public static Exception convertRmiAccessException(Method method, RemoteException ex, String serviceName) {
|
||||
return convertRmiAccessException(method, ex, isConnectFailure(ex), serviceName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given RemoteException that happened during remote access
|
||||
* to Spring's RemoteAccessException if the method signature does not
|
||||
* support RemoteException. Else, return the original RemoteException.
|
||||
* @param method the invoked method
|
||||
* @param ex the RemoteException that happened
|
||||
* @param isConnectFailure whether the given exception should be considered
|
||||
* a connect failure
|
||||
* @param serviceName the name of the service (for debugging purposes)
|
||||
* @return the exception to be thrown to the caller
|
||||
*/
|
||||
public static Exception convertRmiAccessException(
|
||||
Method method, RemoteException ex, boolean isConnectFailure, String serviceName) {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Remote service [" + serviceName + "] threw exception", ex);
|
||||
}
|
||||
if (ReflectionUtils.declaresException(method, ex.getClass())) {
|
||||
return ex;
|
||||
}
|
||||
else {
|
||||
if (isConnectFailure) {
|
||||
return new RemoteConnectFailureException("Could not connect to remote service [" + serviceName + "]", ex);
|
||||
}
|
||||
else {
|
||||
return new RemoteAccessException("Could not access remote service [" + serviceName + "]", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given RMI exception indicates a connect failure.
|
||||
* <p>Treats RMI's ConnectException, ConnectIOException, UnknownHostException,
|
||||
* NoSuchObjectException and StubNotFoundException as connect failure.
|
||||
* @param ex the RMI exception to check
|
||||
* @return whether the exception should be treated as connect failure
|
||||
* @see java.rmi.ConnectException
|
||||
* @see java.rmi.ConnectIOException
|
||||
* @see java.rmi.UnknownHostException
|
||||
* @see java.rmi.NoSuchObjectException
|
||||
* @see java.rmi.StubNotFoundException
|
||||
*/
|
||||
public static boolean isConnectFailure(RemoteException ex) {
|
||||
return (ex instanceof ConnectException || ex instanceof ConnectIOException ||
|
||||
ex instanceof UnknownHostException || ex instanceof NoSuchObjectException ||
|
||||
ex instanceof StubNotFoundException || ex.getCause() instanceof SocketException);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.rmi;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.rmi.Remote;
|
||||
import java.rmi.RemoteException;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.remoting.support.RemoteInvocation;
|
||||
|
||||
/**
|
||||
* Interface for RMI invocation handlers instances on the server,
|
||||
* wrapping exported services. A client uses a stub implementing
|
||||
* this interface to access such a service.
|
||||
*
|
||||
* <p>This is an SPI interface, not to be used directly by applications.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 14.05.2003
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
public interface RmiInvocationHandler extends Remote {
|
||||
|
||||
/**
|
||||
* Return the name of the target interface that this invoker operates on.
|
||||
* @return the name of the target interface, or {@code null} if none
|
||||
* @throws RemoteException in case of communication errors
|
||||
* @see RmiServiceExporter#getServiceInterface()
|
||||
*/
|
||||
@Nullable
|
||||
public String getTargetInterfaceName() throws RemoteException;
|
||||
|
||||
/**
|
||||
* Apply the given invocation to the target object.
|
||||
* <p>Called by
|
||||
* {@link RmiClientInterceptor#doInvoke(org.aopalliance.intercept.MethodInvocation, RmiInvocationHandler)}.
|
||||
* @param invocation object that encapsulates invocation parameters
|
||||
* @return the object returned from the invoked method, if any
|
||||
* @throws RemoteException in case of communication errors
|
||||
* @throws NoSuchMethodException if the method name could not be resolved
|
||||
* @throws IllegalAccessException if the method could not be accessed
|
||||
* @throws InvocationTargetException if the method invocation resulted in an exception
|
||||
*/
|
||||
@Nullable
|
||||
public Object invoke(RemoteInvocation invocation)
|
||||
throws RemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException;
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.rmi;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.rmi.RemoteException;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.remoting.support.RemoteInvocation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Server-side implementation of {@link RmiInvocationHandler}. An instance
|
||||
* of this class exists for each remote object. Automatically created
|
||||
* by {@link RmiServiceExporter} for non-RMI service implementations.
|
||||
*
|
||||
* <p>This is an SPI class, not to be used directly by applications.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 14.05.2003
|
||||
* @see RmiServiceExporter
|
||||
*/
|
||||
@Deprecated
|
||||
class RmiInvocationWrapper implements RmiInvocationHandler {
|
||||
|
||||
private final Object wrappedObject;
|
||||
|
||||
private final RmiBasedExporter rmiExporter;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new RmiInvocationWrapper for the given object.
|
||||
* @param wrappedObject the object to wrap with an RmiInvocationHandler
|
||||
* @param rmiExporter the RMI exporter to handle the actual invocation
|
||||
*/
|
||||
public RmiInvocationWrapper(Object wrappedObject, RmiBasedExporter rmiExporter) {
|
||||
Assert.notNull(wrappedObject, "Object to wrap is required");
|
||||
Assert.notNull(rmiExporter, "RMI exporter is required");
|
||||
this.wrappedObject = wrappedObject;
|
||||
this.rmiExporter = rmiExporter;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Exposes the exporter's service interface, if any, as target interface.
|
||||
* @see RmiBasedExporter#getServiceInterface()
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public String getTargetInterfaceName() {
|
||||
Class<?> ifc = this.rmiExporter.getServiceInterface();
|
||||
return (ifc != null ? ifc.getName() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates the actual invocation handling to the RMI exporter.
|
||||
* @see RmiBasedExporter#invoke(org.springframework.remoting.support.RemoteInvocation, Object)
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public Object invoke(RemoteInvocation invocation)
|
||||
throws RemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
|
||||
return this.rmiExporter.invoke(invocation, this.wrappedObject);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.rmi;
|
||||
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link FactoryBean} for RMI proxies, supporting both conventional RMI services
|
||||
* and RMI invokers. Exposes the proxied service for use as a bean reference,
|
||||
* using the specified service interface. Proxies will throw Spring's unchecked
|
||||
* RemoteAccessException on remote invocation failure instead of RMI's RemoteException.
|
||||
*
|
||||
* <p>The service URL must be a valid RMI URL like "rmi://localhost:1099/myservice".
|
||||
* RMI invokers work at the RmiInvocationHandler level, using the same invoker stub
|
||||
* for any service. Service interfaces do not have to extend {@code java.rmi.Remote}
|
||||
* or throw {@code java.rmi.RemoteException}. Of course, in and out parameters
|
||||
* have to be serializable.
|
||||
*
|
||||
* <p>With conventional RMI services, this proxy factory is typically used with the
|
||||
* RMI service interface. Alternatively, this factory can also proxy a remote RMI
|
||||
* 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 RMI stub will automatically get converted to
|
||||
* Spring's unchecked RemoteAccessException.
|
||||
*
|
||||
* <p>The major advantage of RMI, compared to Hessian, is serialization.
|
||||
* Effectively, any serializable Java object can be transported without hassle.
|
||||
* Hessian has its own (de-)serialization mechanisms, but is HTTP-based and thus
|
||||
* much easier to setup than RMI. Alternatively, consider Spring's HTTP invoker
|
||||
* to combine Java serialization with HTTP-based transport.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 13.05.2003
|
||||
* @see #setServiceInterface
|
||||
* @see #setServiceUrl
|
||||
* @see RmiClientInterceptor
|
||||
* @see RmiServiceExporter
|
||||
* @see java.rmi.Remote
|
||||
* @see java.rmi.RemoteException
|
||||
* @see org.springframework.remoting.RemoteAccessException
|
||||
* @see org.springframework.remoting.caucho.HessianProxyFactoryBean
|
||||
* @see org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
public class RmiProxyFactoryBean extends RmiClientInterceptor implements FactoryBean<Object>, BeanClassLoaderAware {
|
||||
|
||||
private Object serviceProxy;
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
super.afterPropertiesSet();
|
||||
Class<?> ifc = getServiceInterface();
|
||||
Assert.notNull(ifc, "Property 'serviceInterface' is required");
|
||||
this.serviceProxy = new ProxyFactory(ifc, this).getProxy(getBeanClassLoader());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object getObject() {
|
||||
return this.serviceProxy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return getServiceInterface();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,317 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.rmi;
|
||||
|
||||
import java.rmi.RemoteException;
|
||||
import java.rmi.registry.LocateRegistry;
|
||||
import java.rmi.registry.Registry;
|
||||
import java.rmi.server.RMIClientSocketFactory;
|
||||
import java.rmi.server.RMIServerSocketFactory;
|
||||
import java.rmi.server.UnicastRemoteObject;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* {@link FactoryBean} that locates a {@link java.rmi.registry.Registry} and
|
||||
* exposes it for bean references. Can also create a local RMI registry
|
||||
* on the fly if none exists already.
|
||||
*
|
||||
* <p>Can be used to set up and pass around the actual Registry object to
|
||||
* applications objects that need to work with RMI. One example for such an
|
||||
* object that needs to work with RMI is Spring's {@link RmiServiceExporter},
|
||||
* which either works with a passed-in Registry reference or falls back to
|
||||
* the registry as specified by its local properties and defaults.
|
||||
*
|
||||
* <p>Also useful to enforce creation of a local RMI registry at a given port,
|
||||
* for example for a JMX connector. If used in conjunction with
|
||||
* {@link org.springframework.jmx.support.ConnectorServerFactoryBean},
|
||||
* it is recommended to mark the connector definition (ConnectorServerFactoryBean)
|
||||
* as "depends-on" the registry definition (RmiRegistryFactoryBean),
|
||||
* to guarantee starting up the registry first.
|
||||
*
|
||||
* <p>Note: The implementation of this class mirrors the corresponding logic
|
||||
* in {@link RmiServiceExporter}, and also offers the same customization hooks.
|
||||
* RmiServiceExporter implements its own registry lookup as a convenience:
|
||||
* It is very common to simply rely on the registry defaults.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.2.3
|
||||
* @see RmiServiceExporter#setRegistry
|
||||
* @see org.springframework.jmx.support.ConnectorServerFactoryBean
|
||||
* @see java.rmi.registry.Registry
|
||||
* @see java.rmi.registry.LocateRegistry
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
public class RmiRegistryFactoryBean implements FactoryBean<Registry>, InitializingBean, DisposableBean {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private String host;
|
||||
|
||||
private int port = Registry.REGISTRY_PORT;
|
||||
|
||||
private RMIClientSocketFactory clientSocketFactory;
|
||||
|
||||
private RMIServerSocketFactory serverSocketFactory;
|
||||
|
||||
private Registry registry;
|
||||
|
||||
private boolean alwaysCreate = false;
|
||||
|
||||
private boolean created = false;
|
||||
|
||||
|
||||
/**
|
||||
* Set the host of the registry for the exported RMI service,
|
||||
* i.e. {@code rmi://HOST:port/name}
|
||||
* <p>Default is localhost.
|
||||
*/
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the host of the registry for the exported RMI service.
|
||||
*/
|
||||
public String getHost() {
|
||||
return this.host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the port of the registry for the exported RMI service,
|
||||
* i.e. {@code rmi://host:PORT/name}
|
||||
* <p>Default is {@code Registry.REGISTRY_PORT} (1099).
|
||||
*/
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the port of the registry for the exported RMI service.
|
||||
*/
|
||||
public int getPort() {
|
||||
return this.port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom RMI client socket factory to use for the RMI registry.
|
||||
* <p>If the given object also implements {@code java.rmi.server.RMIServerSocketFactory},
|
||||
* it will automatically be registered as server socket factory too.
|
||||
* @see #setServerSocketFactory
|
||||
* @see java.rmi.server.RMIClientSocketFactory
|
||||
* @see java.rmi.server.RMIServerSocketFactory
|
||||
* @see java.rmi.registry.LocateRegistry#getRegistry(String, int, java.rmi.server.RMIClientSocketFactory)
|
||||
*/
|
||||
public void setClientSocketFactory(RMIClientSocketFactory clientSocketFactory) {
|
||||
this.clientSocketFactory = clientSocketFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom RMI server socket factory to use for the RMI registry.
|
||||
* <p>Only needs to be specified when the client socket factory does not
|
||||
* implement {@code java.rmi.server.RMIServerSocketFactory} already.
|
||||
* @see #setClientSocketFactory
|
||||
* @see java.rmi.server.RMIClientSocketFactory
|
||||
* @see java.rmi.server.RMIServerSocketFactory
|
||||
* @see java.rmi.registry.LocateRegistry#createRegistry(int, RMIClientSocketFactory, java.rmi.server.RMIServerSocketFactory)
|
||||
*/
|
||||
public void setServerSocketFactory(RMIServerSocketFactory serverSocketFactory) {
|
||||
this.serverSocketFactory = serverSocketFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to always create the registry in-process,
|
||||
* not attempting to locate an existing registry at the specified port.
|
||||
* <p>Default is "false". Switch this flag to "true" in order to avoid
|
||||
* the overhead of locating an existing registry when you always
|
||||
* intend to create a new registry in any case.
|
||||
*/
|
||||
public void setAlwaysCreate(boolean alwaysCreate) {
|
||||
this.alwaysCreate = alwaysCreate;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
// Check socket factories for registry.
|
||||
if (this.clientSocketFactory instanceof RMIServerSocketFactory) {
|
||||
this.serverSocketFactory = (RMIServerSocketFactory) this.clientSocketFactory;
|
||||
}
|
||||
if ((this.clientSocketFactory != null && this.serverSocketFactory == null) ||
|
||||
(this.clientSocketFactory == null && this.serverSocketFactory != null)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Both RMIClientSocketFactory and RMIServerSocketFactory or none required");
|
||||
}
|
||||
|
||||
// Fetch RMI registry to expose.
|
||||
this.registry = getRegistry(this.host, this.port, this.clientSocketFactory, this.serverSocketFactory);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Locate or create the RMI registry.
|
||||
* @param registryHost the registry host to use (if this is specified,
|
||||
* no implicit creation of a RMI registry will happen)
|
||||
* @param registryPort the registry port to use
|
||||
* @param clientSocketFactory the RMI client socket factory for the registry (if any)
|
||||
* @param serverSocketFactory the RMI server socket factory for the registry (if any)
|
||||
* @return the RMI registry
|
||||
* @throws java.rmi.RemoteException if the registry couldn't be located or created
|
||||
*/
|
||||
protected Registry getRegistry(String registryHost, int registryPort,
|
||||
@Nullable RMIClientSocketFactory clientSocketFactory, @Nullable RMIServerSocketFactory serverSocketFactory)
|
||||
throws RemoteException {
|
||||
|
||||
if (registryHost != null) {
|
||||
// Host explicitly specified: only lookup possible.
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Looking for RMI registry at port '" + registryPort + "' of host [" + registryHost + "]");
|
||||
}
|
||||
Registry reg = LocateRegistry.getRegistry(registryHost, registryPort, clientSocketFactory);
|
||||
testRegistry(reg);
|
||||
return reg;
|
||||
}
|
||||
|
||||
else {
|
||||
return getRegistry(registryPort, clientSocketFactory, serverSocketFactory);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate or create the RMI registry.
|
||||
* @param registryPort the registry port to use
|
||||
* @param clientSocketFactory the RMI client socket factory for the registry (if any)
|
||||
* @param serverSocketFactory the RMI server socket factory for the registry (if any)
|
||||
* @return the RMI registry
|
||||
* @throws RemoteException if the registry couldn't be located or created
|
||||
*/
|
||||
protected Registry getRegistry(int registryPort,
|
||||
@Nullable RMIClientSocketFactory clientSocketFactory, @Nullable RMIServerSocketFactory serverSocketFactory)
|
||||
throws RemoteException {
|
||||
|
||||
if (clientSocketFactory != null) {
|
||||
if (this.alwaysCreate) {
|
||||
logger.debug("Creating new RMI registry");
|
||||
this.created = true;
|
||||
return LocateRegistry.createRegistry(registryPort, clientSocketFactory, serverSocketFactory);
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Looking for RMI registry at port '" + registryPort + "', using custom socket factory");
|
||||
}
|
||||
synchronized (LocateRegistry.class) {
|
||||
try {
|
||||
// Retrieve existing registry.
|
||||
Registry reg = LocateRegistry.getRegistry(null, registryPort, clientSocketFactory);
|
||||
testRegistry(reg);
|
||||
return reg;
|
||||
}
|
||||
catch (RemoteException ex) {
|
||||
logger.trace("RMI registry access threw exception", ex);
|
||||
logger.debug("Could not detect RMI registry - creating new one");
|
||||
// Assume no registry found -> create new one.
|
||||
this.created = true;
|
||||
return LocateRegistry.createRegistry(registryPort, clientSocketFactory, serverSocketFactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
return getRegistry(registryPort);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate or create the RMI registry.
|
||||
* @param registryPort the registry port to use
|
||||
* @return the RMI registry
|
||||
* @throws RemoteException if the registry couldn't be located or created
|
||||
*/
|
||||
protected Registry getRegistry(int registryPort) throws RemoteException {
|
||||
if (this.alwaysCreate) {
|
||||
logger.debug("Creating new RMI registry");
|
||||
this.created = true;
|
||||
return LocateRegistry.createRegistry(registryPort);
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Looking for RMI registry at port '" + registryPort + "'");
|
||||
}
|
||||
synchronized (LocateRegistry.class) {
|
||||
try {
|
||||
// Retrieve existing registry.
|
||||
Registry reg = LocateRegistry.getRegistry(registryPort);
|
||||
testRegistry(reg);
|
||||
return reg;
|
||||
}
|
||||
catch (RemoteException ex) {
|
||||
logger.trace("RMI registry access threw exception", ex);
|
||||
logger.debug("Could not detect RMI registry - creating new one");
|
||||
// Assume no registry found -> create new one.
|
||||
this.created = true;
|
||||
return LocateRegistry.createRegistry(registryPort);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the given RMI registry, calling some operation on it to
|
||||
* check whether it is still active.
|
||||
* <p>Default implementation calls {@code Registry.list()}.
|
||||
* @param registry the RMI registry to test
|
||||
* @throws RemoteException if thrown by registry methods
|
||||
* @see java.rmi.registry.Registry#list()
|
||||
*/
|
||||
protected void testRegistry(Registry registry) throws RemoteException {
|
||||
registry.list();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Registry getObject() throws Exception {
|
||||
return this.registry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Registry> getObjectType() {
|
||||
return (this.registry != null ? this.registry.getClass() : Registry.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unexport the RMI registry on bean factory shutdown,
|
||||
* provided that this bean actually created a registry.
|
||||
*/
|
||||
@Override
|
||||
public void destroy() throws RemoteException {
|
||||
if (this.created) {
|
||||
logger.debug("Unexporting RMI registry");
|
||||
UnicastRemoteObject.unexportObject(this.registry, true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,464 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.rmi;
|
||||
|
||||
import java.rmi.AlreadyBoundException;
|
||||
import java.rmi.NoSuchObjectException;
|
||||
import java.rmi.NotBoundException;
|
||||
import java.rmi.Remote;
|
||||
import java.rmi.RemoteException;
|
||||
import java.rmi.registry.LocateRegistry;
|
||||
import java.rmi.registry.Registry;
|
||||
import java.rmi.server.RMIClientSocketFactory;
|
||||
import java.rmi.server.RMIServerSocketFactory;
|
||||
import java.rmi.server.UnicastRemoteObject;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* RMI exporter that exposes the specified service as RMI object with the specified name.
|
||||
* Such services can be accessed via plain RMI or via {@link RmiProxyFactoryBean}.
|
||||
* Also supports exposing any non-RMI service via RMI invokers, to be accessed via
|
||||
* {@link RmiClientInterceptor} / {@link RmiProxyFactoryBean}'s automatic detection
|
||||
* of such invokers.
|
||||
*
|
||||
* <p>With an RMI invoker, RMI communication works on the {@link RmiInvocationHandler}
|
||||
* level, needing only one stub for any service. Service interfaces do not have to
|
||||
* extend {@code java.rmi.Remote} or throw {@code java.rmi.RemoteException}
|
||||
* on all methods, but in and out parameters have to be serializable.
|
||||
*
|
||||
* <p>The major advantage of RMI, compared to Hessian, is serialization.
|
||||
* Effectively, any serializable Java object can be transported without hassle.
|
||||
* Hessian has its own (de-)serialization mechanisms, but is HTTP-based and thus
|
||||
* much easier to setup than RMI. Alternatively, consider Spring's HTTP invoker
|
||||
* to combine Java serialization with HTTP-based transport.
|
||||
*
|
||||
* <p>Note: RMI makes a best-effort attempt to obtain the fully qualified host name.
|
||||
* If one cannot be determined, it will fall back and use the IP address. Depending
|
||||
* on your network configuration, in some cases it will resolve the IP to the loopback
|
||||
* address. To ensure that RMI will use the host name bound to the correct network
|
||||
* interface, you should pass the {@code java.rmi.server.hostname} property to the
|
||||
* JVM that will export the registry and/or the service using the "-D" JVM argument.
|
||||
* For example: {@code -Djava.rmi.server.hostname=myserver.com}
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 13.05.2003
|
||||
* @see RmiClientInterceptor
|
||||
* @see RmiProxyFactoryBean
|
||||
* @see java.rmi.Remote
|
||||
* @see java.rmi.RemoteException
|
||||
* @see org.springframework.remoting.caucho.HessianServiceExporter
|
||||
* @see org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
public class RmiServiceExporter extends RmiBasedExporter implements InitializingBean, DisposableBean {
|
||||
|
||||
private String serviceName;
|
||||
|
||||
private int servicePort = 0; // anonymous port
|
||||
|
||||
private RMIClientSocketFactory clientSocketFactory;
|
||||
|
||||
private RMIServerSocketFactory serverSocketFactory;
|
||||
|
||||
private Registry registry;
|
||||
|
||||
private String registryHost;
|
||||
|
||||
private int registryPort = Registry.REGISTRY_PORT;
|
||||
|
||||
private RMIClientSocketFactory registryClientSocketFactory;
|
||||
|
||||
private RMIServerSocketFactory registryServerSocketFactory;
|
||||
|
||||
private boolean alwaysCreateRegistry = false;
|
||||
|
||||
private boolean replaceExistingBinding = true;
|
||||
|
||||
private Remote exportedObject;
|
||||
|
||||
private boolean createdRegistry = false;
|
||||
|
||||
|
||||
/**
|
||||
* Set the name of the exported RMI service,
|
||||
* i.e. {@code rmi://host:port/NAME}
|
||||
*/
|
||||
public void setServiceName(String serviceName) {
|
||||
this.serviceName = serviceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the port that the exported RMI service will use.
|
||||
* <p>Default is 0 (anonymous port).
|
||||
*/
|
||||
public void setServicePort(int servicePort) {
|
||||
this.servicePort = servicePort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom RMI client socket factory to use for exporting the service.
|
||||
* <p>If the given object also implements {@code java.rmi.server.RMIServerSocketFactory},
|
||||
* it will automatically be registered as server socket factory too.
|
||||
* @see #setServerSocketFactory
|
||||
* @see java.rmi.server.RMIClientSocketFactory
|
||||
* @see java.rmi.server.RMIServerSocketFactory
|
||||
* @see UnicastRemoteObject#exportObject(Remote, int, RMIClientSocketFactory, RMIServerSocketFactory)
|
||||
*/
|
||||
public void setClientSocketFactory(RMIClientSocketFactory clientSocketFactory) {
|
||||
this.clientSocketFactory = clientSocketFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom RMI server socket factory to use for exporting the service.
|
||||
* <p>Only needs to be specified when the client socket factory does not
|
||||
* implement {@code java.rmi.server.RMIServerSocketFactory} already.
|
||||
* @see #setClientSocketFactory
|
||||
* @see java.rmi.server.RMIClientSocketFactory
|
||||
* @see java.rmi.server.RMIServerSocketFactory
|
||||
* @see UnicastRemoteObject#exportObject(Remote, int, RMIClientSocketFactory, RMIServerSocketFactory)
|
||||
*/
|
||||
public void setServerSocketFactory(RMIServerSocketFactory serverSocketFactory) {
|
||||
this.serverSocketFactory = serverSocketFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the RMI registry to register the exported service with.
|
||||
* Typically used in combination with RmiRegistryFactoryBean.
|
||||
* <p>Alternatively, you can specify all registry properties locally.
|
||||
* This exporter will then try to locate the specified registry,
|
||||
* automatically creating a new local one if appropriate.
|
||||
* <p>Default is a local registry at the default port (1099),
|
||||
* created on the fly if necessary.
|
||||
* @see RmiRegistryFactoryBean
|
||||
* @see #setRegistryHost
|
||||
* @see #setRegistryPort
|
||||
* @see #setRegistryClientSocketFactory
|
||||
* @see #setRegistryServerSocketFactory
|
||||
*/
|
||||
public void setRegistry(Registry registry) {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the host of the registry for the exported RMI service,
|
||||
* i.e. {@code rmi://HOST:port/name}
|
||||
* <p>Default is localhost.
|
||||
*/
|
||||
public void setRegistryHost(String registryHost) {
|
||||
this.registryHost = registryHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the port of the registry for the exported RMI service,
|
||||
* i.e. {@code rmi://host:PORT/name}
|
||||
* <p>Default is {@code Registry.REGISTRY_PORT} (1099).
|
||||
* @see java.rmi.registry.Registry#REGISTRY_PORT
|
||||
*/
|
||||
public void setRegistryPort(int registryPort) {
|
||||
this.registryPort = registryPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom RMI client socket factory to use for the RMI registry.
|
||||
* <p>If the given object also implements {@code java.rmi.server.RMIServerSocketFactory},
|
||||
* it will automatically be registered as server socket factory too.
|
||||
* @see #setRegistryServerSocketFactory
|
||||
* @see java.rmi.server.RMIClientSocketFactory
|
||||
* @see java.rmi.server.RMIServerSocketFactory
|
||||
* @see LocateRegistry#getRegistry(String, int, RMIClientSocketFactory)
|
||||
*/
|
||||
public void setRegistryClientSocketFactory(RMIClientSocketFactory registryClientSocketFactory) {
|
||||
this.registryClientSocketFactory = registryClientSocketFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom RMI server socket factory to use for the RMI registry.
|
||||
* <p>Only needs to be specified when the client socket factory does not
|
||||
* implement {@code java.rmi.server.RMIServerSocketFactory} already.
|
||||
* @see #setRegistryClientSocketFactory
|
||||
* @see java.rmi.server.RMIClientSocketFactory
|
||||
* @see java.rmi.server.RMIServerSocketFactory
|
||||
* @see LocateRegistry#createRegistry(int, RMIClientSocketFactory, RMIServerSocketFactory)
|
||||
*/
|
||||
public void setRegistryServerSocketFactory(RMIServerSocketFactory registryServerSocketFactory) {
|
||||
this.registryServerSocketFactory = registryServerSocketFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to always create the registry in-process,
|
||||
* not attempting to locate an existing registry at the specified port.
|
||||
* <p>Default is "false". Switch this flag to "true" in order to avoid
|
||||
* the overhead of locating an existing registry when you always
|
||||
* intend to create a new registry in any case.
|
||||
*/
|
||||
public void setAlwaysCreateRegistry(boolean alwaysCreateRegistry) {
|
||||
this.alwaysCreateRegistry = alwaysCreateRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to replace an existing binding in the RMI registry,
|
||||
* that is, whether to simply override an existing binding with the
|
||||
* specified service in case of a naming conflict in the registry.
|
||||
* <p>Default is "true", assuming that an existing binding for this
|
||||
* exporter's service name is an accidental leftover from a previous
|
||||
* execution. Switch this to "false" to make the exporter fail in such
|
||||
* a scenario, indicating that there was already an RMI object bound.
|
||||
*/
|
||||
public void setReplaceExistingBinding(boolean replaceExistingBinding) {
|
||||
this.replaceExistingBinding = replaceExistingBinding;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws RemoteException {
|
||||
prepare();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize this service exporter, registering the service as RMI object.
|
||||
* <p>Creates an RMI registry on the specified port if none exists.
|
||||
* @throws RemoteException if service registration failed
|
||||
*/
|
||||
public void prepare() throws RemoteException {
|
||||
checkService();
|
||||
|
||||
if (this.serviceName == null) {
|
||||
throw new IllegalArgumentException("Property 'serviceName' is required");
|
||||
}
|
||||
|
||||
// Check socket factories for exported object.
|
||||
if (this.clientSocketFactory instanceof RMIServerSocketFactory) {
|
||||
this.serverSocketFactory = (RMIServerSocketFactory) this.clientSocketFactory;
|
||||
}
|
||||
if ((this.clientSocketFactory != null && this.serverSocketFactory == null) ||
|
||||
(this.clientSocketFactory == null && this.serverSocketFactory != null)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Both RMIClientSocketFactory and RMIServerSocketFactory or none required");
|
||||
}
|
||||
|
||||
// Check socket factories for RMI registry.
|
||||
if (this.registryClientSocketFactory instanceof RMIServerSocketFactory) {
|
||||
this.registryServerSocketFactory = (RMIServerSocketFactory) this.registryClientSocketFactory;
|
||||
}
|
||||
if (this.registryClientSocketFactory == null && this.registryServerSocketFactory != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"RMIServerSocketFactory without RMIClientSocketFactory for registry not supported");
|
||||
}
|
||||
|
||||
this.createdRegistry = false;
|
||||
|
||||
// Determine RMI registry to use.
|
||||
if (this.registry == null) {
|
||||
this.registry = getRegistry(this.registryHost, this.registryPort,
|
||||
this.registryClientSocketFactory, this.registryServerSocketFactory);
|
||||
this.createdRegistry = true;
|
||||
}
|
||||
|
||||
// Initialize and cache exported object.
|
||||
this.exportedObject = getObjectToExport();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Binding service '" + this.serviceName + "' to RMI registry: " + this.registry);
|
||||
}
|
||||
|
||||
// Export RMI object.
|
||||
if (this.clientSocketFactory != null) {
|
||||
UnicastRemoteObject.exportObject(
|
||||
this.exportedObject, this.servicePort, this.clientSocketFactory, this.serverSocketFactory);
|
||||
}
|
||||
else {
|
||||
UnicastRemoteObject.exportObject(this.exportedObject, this.servicePort);
|
||||
}
|
||||
|
||||
// Bind RMI object to registry.
|
||||
try {
|
||||
if (this.replaceExistingBinding) {
|
||||
this.registry.rebind(this.serviceName, this.exportedObject);
|
||||
}
|
||||
else {
|
||||
this.registry.bind(this.serviceName, this.exportedObject);
|
||||
}
|
||||
}
|
||||
catch (AlreadyBoundException ex) {
|
||||
// Already an RMI object bound for the specified service name...
|
||||
unexportObjectSilently();
|
||||
throw new IllegalStateException(
|
||||
"Already an RMI object bound for name '" + this.serviceName + "': " + ex.toString());
|
||||
}
|
||||
catch (RemoteException ex) {
|
||||
// Registry binding failed: let's unexport the RMI object as well.
|
||||
unexportObjectSilently();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Locate or create the RMI registry for this exporter.
|
||||
* @param registryHost the registry host to use (if this is specified,
|
||||
* no implicit creation of a RMI registry will happen)
|
||||
* @param registryPort the registry port to use
|
||||
* @param clientSocketFactory the RMI client socket factory for the registry (if any)
|
||||
* @param serverSocketFactory the RMI server socket factory for the registry (if any)
|
||||
* @return the RMI registry
|
||||
* @throws RemoteException if the registry couldn't be located or created
|
||||
*/
|
||||
protected Registry getRegistry(String registryHost, int registryPort,
|
||||
@Nullable RMIClientSocketFactory clientSocketFactory, @Nullable RMIServerSocketFactory serverSocketFactory)
|
||||
throws RemoteException {
|
||||
|
||||
if (registryHost != null) {
|
||||
// Host explicitly specified: only lookup possible.
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Looking for RMI registry at port '" + registryPort + "' of host [" + registryHost + "]");
|
||||
}
|
||||
Registry reg = LocateRegistry.getRegistry(registryHost, registryPort, clientSocketFactory);
|
||||
testRegistry(reg);
|
||||
return reg;
|
||||
}
|
||||
|
||||
else {
|
||||
return getRegistry(registryPort, clientSocketFactory, serverSocketFactory);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate or create the RMI registry for this exporter.
|
||||
* @param registryPort the registry port to use
|
||||
* @param clientSocketFactory the RMI client socket factory for the registry (if any)
|
||||
* @param serverSocketFactory the RMI server socket factory for the registry (if any)
|
||||
* @return the RMI registry
|
||||
* @throws RemoteException if the registry couldn't be located or created
|
||||
*/
|
||||
protected Registry getRegistry(int registryPort,
|
||||
@Nullable RMIClientSocketFactory clientSocketFactory, @Nullable RMIServerSocketFactory serverSocketFactory)
|
||||
throws RemoteException {
|
||||
|
||||
if (clientSocketFactory != null) {
|
||||
if (this.alwaysCreateRegistry) {
|
||||
logger.debug("Creating new RMI registry");
|
||||
return LocateRegistry.createRegistry(registryPort, clientSocketFactory, serverSocketFactory);
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Looking for RMI registry at port '" + registryPort + "', using custom socket factory");
|
||||
}
|
||||
synchronized (LocateRegistry.class) {
|
||||
try {
|
||||
// Retrieve existing registry.
|
||||
Registry reg = LocateRegistry.getRegistry(null, registryPort, clientSocketFactory);
|
||||
testRegistry(reg);
|
||||
return reg;
|
||||
}
|
||||
catch (RemoteException ex) {
|
||||
logger.trace("RMI registry access threw exception", ex);
|
||||
logger.debug("Could not detect RMI registry - creating new one");
|
||||
// Assume no registry found -> create new one.
|
||||
return LocateRegistry.createRegistry(registryPort, clientSocketFactory, serverSocketFactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
return getRegistry(registryPort);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate or create the RMI registry for this exporter.
|
||||
* @param registryPort the registry port to use
|
||||
* @return the RMI registry
|
||||
* @throws RemoteException if the registry couldn't be located or created
|
||||
*/
|
||||
protected Registry getRegistry(int registryPort) throws RemoteException {
|
||||
if (this.alwaysCreateRegistry) {
|
||||
logger.debug("Creating new RMI registry");
|
||||
return LocateRegistry.createRegistry(registryPort);
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Looking for RMI registry at port '" + registryPort + "'");
|
||||
}
|
||||
synchronized (LocateRegistry.class) {
|
||||
try {
|
||||
// Retrieve existing registry.
|
||||
Registry reg = LocateRegistry.getRegistry(registryPort);
|
||||
testRegistry(reg);
|
||||
return reg;
|
||||
}
|
||||
catch (RemoteException ex) {
|
||||
logger.trace("RMI registry access threw exception", ex);
|
||||
logger.debug("Could not detect RMI registry - creating new one");
|
||||
// Assume no registry found -> create new one.
|
||||
return LocateRegistry.createRegistry(registryPort);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the given RMI registry, calling some operation on it to
|
||||
* check whether it is still active.
|
||||
* <p>Default implementation calls {@code Registry.list()}.
|
||||
* @param registry the RMI registry to test
|
||||
* @throws RemoteException if thrown by registry methods
|
||||
* @see java.rmi.registry.Registry#list()
|
||||
*/
|
||||
protected void testRegistry(Registry registry) throws RemoteException {
|
||||
registry.list();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unbind the RMI service from the registry on bean factory shutdown.
|
||||
*/
|
||||
@Override
|
||||
public void destroy() throws RemoteException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Unbinding RMI service '" + this.serviceName +
|
||||
"' from registry" + (this.createdRegistry ? (" at port '" + this.registryPort + "'") : ""));
|
||||
}
|
||||
try {
|
||||
this.registry.unbind(this.serviceName);
|
||||
}
|
||||
catch (NotBoundException ex) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("RMI service '" + this.serviceName + "' is not bound to registry" +
|
||||
(this.createdRegistry ? (" at port '" + this.registryPort + "' anymore") : ""), ex);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
unexportObjectSilently();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unexport the registered RMI object, logging any exception that arises.
|
||||
*/
|
||||
private void unexportObjectSilently() {
|
||||
try {
|
||||
UnicastRemoteObject.unexportObject(this.exportedObject, true);
|
||||
}
|
||||
catch (NoSuchObjectException ex) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("RMI object for service '" + this.serviceName + "' is not exported anymore", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
/**
|
||||
* Remoting classes for conventional RMI and transparent remoting via
|
||||
* RMI invokers. Provides a proxy factory for accessing RMI services,
|
||||
* and an exporter for making beans available to RMI clients.
|
||||
*/
|
||||
package org.springframework.remoting.rmi;
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.soap;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
import org.springframework.remoting.RemoteInvocationFailureException;
|
||||
|
||||
/**
|
||||
* RemoteInvocationFailureException subclass that provides the details
|
||||
* of a SOAP fault.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see javax.xml.rpc.soap.SOAPFaultException
|
||||
* @see javax.xml.ws.soap.SOAPFaultException
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class SoapFaultException extends RemoteInvocationFailureException {
|
||||
|
||||
/**
|
||||
* Constructor for SoapFaultException.
|
||||
* @param msg the detail message
|
||||
* @param cause the root cause from the SOAP API in use
|
||||
*/
|
||||
protected SoapFaultException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the SOAP fault code.
|
||||
*/
|
||||
public abstract String getFaultCode();
|
||||
|
||||
/**
|
||||
* Return the SOAP fault code as a {@code QName} object.
|
||||
*/
|
||||
public abstract QName getFaultCodeAsQName();
|
||||
|
||||
/**
|
||||
* Return the descriptive SOAP fault string.
|
||||
*/
|
||||
public abstract String getFaultString();
|
||||
|
||||
/**
|
||||
* Return the actor that caused this fault.
|
||||
*/
|
||||
public abstract String getFaultActor();
|
||||
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
/**
|
||||
* SOAP-specific exceptions and support classes for Spring's remoting subsystem.
|
||||
*/
|
||||
package org.springframework.remoting.soap;
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.support;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of the {@link RemoteInvocationExecutor} interface.
|
||||
* Simply delegates to {@link RemoteInvocation}'s invoke method.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
* @see RemoteInvocation#invoke
|
||||
*/
|
||||
public class DefaultRemoteInvocationExecutor implements RemoteInvocationExecutor {
|
||||
|
||||
@Override
|
||||
public Object invoke(RemoteInvocation invocation, Object targetObject)
|
||||
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException{
|
||||
|
||||
Assert.notNull(invocation, "RemoteInvocation must not be null");
|
||||
Assert.notNull(targetObject, "Target object must not be null");
|
||||
return invocation.invoke(targetObject);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.support;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
/**
|
||||
* Default implementation of the {@link RemoteInvocationFactory} interface.
|
||||
* Simply creates a new standard {@link RemoteInvocation} object.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
*/
|
||||
public class DefaultRemoteInvocationFactory implements RemoteInvocationFactory {
|
||||
|
||||
@Override
|
||||
public RemoteInvocation createRemoteInvocation(MethodInvocation methodInvocation) {
|
||||
return new RemoteInvocation(methodInvocation);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.support;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Abstract base class for classes that access a remote service.
|
||||
* Provides a "serviceInterface" bean property.
|
||||
*
|
||||
* <p>Note that the service interface being used will show some signs of
|
||||
* remotability, like the granularity of method calls that it offers.
|
||||
* Furthermore, it has to have serializable arguments etc.
|
||||
*
|
||||
* <p>Accessors are supposed to throw Spring's generic
|
||||
* {@link org.springframework.remoting.RemoteAccessException} in case
|
||||
* of remote invocation failure, provided that the service interface
|
||||
* does not declare {@code java.rmi.RemoteException}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 13.05.2003
|
||||
* @see org.springframework.remoting.RemoteAccessException
|
||||
* @see java.rmi.RemoteException
|
||||
*/
|
||||
public abstract class RemoteAccessor extends RemotingSupport {
|
||||
|
||||
private Class<?> serviceInterface;
|
||||
|
||||
|
||||
/**
|
||||
* Set the interface of the service to access.
|
||||
* The interface must be suitable for the particular service and remoting strategy.
|
||||
* <p>Typically required to be able to create a suitable service proxy,
|
||||
* but can also be optional if the lookup returns a typed proxy.
|
||||
*/
|
||||
public void setServiceInterface(Class<?> serviceInterface) {
|
||||
Assert.notNull(serviceInterface, "'serviceInterface' must not be null");
|
||||
Assert.isTrue(serviceInterface.isInterface(), "'serviceInterface' must be an interface");
|
||||
this.serviceInterface = serviceInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the interface of the service to access.
|
||||
*/
|
||||
public Class<?> getServiceInterface() {
|
||||
return this.serviceInterface;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.support;
|
||||
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.framework.adapter.AdvisorAdapterRegistry;
|
||||
import org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Abstract base class for classes that export a remote service.
|
||||
* Provides "service" and "serviceInterface" bean properties.
|
||||
*
|
||||
* <p>Note that the service interface being used will show some signs of
|
||||
* remotability, like the granularity of method calls that it offers.
|
||||
* Furthermore, it has to have serializable arguments etc.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 26.12.2003
|
||||
*/
|
||||
public abstract class RemoteExporter extends RemotingSupport {
|
||||
|
||||
private Object service;
|
||||
|
||||
private Class<?> serviceInterface;
|
||||
|
||||
private Boolean registerTraceInterceptor;
|
||||
|
||||
private Object[] interceptors;
|
||||
|
||||
|
||||
/**
|
||||
* Set the service to export.
|
||||
* Typically populated via a bean reference.
|
||||
*/
|
||||
public void setService(Object service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the service to export.
|
||||
*/
|
||||
public Object getService() {
|
||||
return this.service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the interface of the service to export.
|
||||
* The interface must be suitable for the particular service and remoting strategy.
|
||||
*/
|
||||
public void setServiceInterface(Class<?> serviceInterface) {
|
||||
Assert.notNull(serviceInterface, "'serviceInterface' must not be null");
|
||||
Assert.isTrue(serviceInterface.isInterface(), "'serviceInterface' must be an interface");
|
||||
this.serviceInterface = serviceInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the interface of the service to export.
|
||||
*/
|
||||
public Class<?> getServiceInterface() {
|
||||
return this.serviceInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to register a RemoteInvocationTraceInterceptor for exported
|
||||
* services. Only applied when a subclass uses {@code getProxyForService}
|
||||
* for creating the proxy to expose.
|
||||
* <p>Default is "true". RemoteInvocationTraceInterceptor's most important value
|
||||
* is that it logs exception stacktraces on the server, before propagating an
|
||||
* exception to the client. Note that RemoteInvocationTraceInterceptor will <i>not</i>
|
||||
* be registered by default if the "interceptors" property has been specified.
|
||||
* @see #setInterceptors
|
||||
* @see #getProxyForService
|
||||
* @see RemoteInvocationTraceInterceptor
|
||||
*/
|
||||
public void setRegisterTraceInterceptor(boolean registerTraceInterceptor) {
|
||||
this.registerTraceInterceptor = registerTraceInterceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set additional interceptors (or advisors) to be applied before the
|
||||
* remote endpoint, e.g. a PerformanceMonitorInterceptor.
|
||||
* <p>You may specify any AOP Alliance MethodInterceptors or other
|
||||
* Spring AOP Advices, as well as Spring AOP Advisors.
|
||||
* @see #getProxyForService
|
||||
* @see org.springframework.aop.interceptor.PerformanceMonitorInterceptor
|
||||
*/
|
||||
public void setInterceptors(Object[] interceptors) {
|
||||
this.interceptors = interceptors;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether the service reference has been set.
|
||||
* @see #setService
|
||||
*/
|
||||
protected void checkService() throws IllegalArgumentException {
|
||||
Assert.notNull(getService(), "Property 'service' is required");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a service reference has been set,
|
||||
* and whether it matches the specified service.
|
||||
* @see #setServiceInterface
|
||||
* @see #setService
|
||||
*/
|
||||
protected void checkServiceInterface() throws IllegalArgumentException {
|
||||
Class<?> serviceInterface = getServiceInterface();
|
||||
Assert.notNull(serviceInterface, "Property 'serviceInterface' is required");
|
||||
|
||||
Object service = getService();
|
||||
if (service instanceof String) {
|
||||
throw new IllegalArgumentException("Service [" + service + "] is a String " +
|
||||
"rather than an actual service reference: Have you accidentally specified " +
|
||||
"the service bean name as value instead of as reference?");
|
||||
}
|
||||
if (!serviceInterface.isInstance(service)) {
|
||||
throw new IllegalArgumentException("Service interface [" + serviceInterface.getName() +
|
||||
"] needs to be implemented by service [" + service + "] of class [" +
|
||||
service.getClass().getName() + "]");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a proxy for the given service object, implementing the specified
|
||||
* service interface.
|
||||
* <p>Used to export a proxy that does not expose any internals but just
|
||||
* a specific interface intended for remote access. Furthermore, a
|
||||
* {@link RemoteInvocationTraceInterceptor} will be registered (by default).
|
||||
* @return the proxy
|
||||
* @see #setServiceInterface
|
||||
* @see #setRegisterTraceInterceptor
|
||||
* @see RemoteInvocationTraceInterceptor
|
||||
*/
|
||||
protected Object getProxyForService() {
|
||||
checkService();
|
||||
checkServiceInterface();
|
||||
|
||||
ProxyFactory proxyFactory = new ProxyFactory();
|
||||
proxyFactory.addInterface(getServiceInterface());
|
||||
|
||||
if (this.registerTraceInterceptor != null ? this.registerTraceInterceptor : this.interceptors == null) {
|
||||
proxyFactory.addAdvice(new RemoteInvocationTraceInterceptor(getExporterName()));
|
||||
}
|
||||
if (this.interceptors != null) {
|
||||
AdvisorAdapterRegistry adapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();
|
||||
for (Object interceptor : this.interceptors) {
|
||||
proxyFactory.addAdvisor(adapterRegistry.wrap(interceptor));
|
||||
}
|
||||
}
|
||||
|
||||
proxyFactory.setTarget(getService());
|
||||
proxyFactory.setOpaque(true);
|
||||
|
||||
return proxyFactory.getProxy(getBeanClassLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a short name for this exporter.
|
||||
* Used for tracing of remote invocations.
|
||||
* <p>Default is the unqualified class name (without package).
|
||||
* Can be overridden in subclasses.
|
||||
* @see #getProxyForService
|
||||
* @see RemoteInvocationTraceInterceptor
|
||||
* @see org.springframework.util.ClassUtils#getShortName
|
||||
*/
|
||||
protected String getExporterName() {
|
||||
return ClassUtils.getShortName(getClass());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,225 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Encapsulates a remote invocation, providing core method invocation properties
|
||||
* in a serializable fashion. Used for RMI and HTTP-based serialization invokers.
|
||||
*
|
||||
* <p>This is an SPI class, typically not used directly by applications.
|
||||
* Can be subclassed for additional invocation parameters.
|
||||
*
|
||||
* <p>Both {@link RemoteInvocation} and {@link RemoteInvocationResult} are designed
|
||||
* for use with standard Java serialization as well as JavaBean-style serialization.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 25.02.2004
|
||||
* @see RemoteInvocationResult
|
||||
* @see RemoteInvocationFactory
|
||||
* @see RemoteInvocationExecutor
|
||||
* @see org.springframework.remoting.rmi.RmiProxyFactoryBean
|
||||
* @see org.springframework.remoting.rmi.RmiServiceExporter
|
||||
* @see org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean
|
||||
* @see org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter
|
||||
*/
|
||||
public class RemoteInvocation implements Serializable {
|
||||
|
||||
/** use serialVersionUID from Spring 1.1 for interoperability. */
|
||||
private static final long serialVersionUID = 6876024250231820554L;
|
||||
|
||||
|
||||
private String methodName;
|
||||
|
||||
private Class<?>[] parameterTypes;
|
||||
|
||||
private Object[] arguments;
|
||||
|
||||
private Map<String, Serializable> attributes;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new RemoteInvocation for the given AOP method invocation.
|
||||
* @param methodInvocation the AOP invocation to convert
|
||||
*/
|
||||
public RemoteInvocation(MethodInvocation methodInvocation) {
|
||||
this.methodName = methodInvocation.getMethod().getName();
|
||||
this.parameterTypes = methodInvocation.getMethod().getParameterTypes();
|
||||
this.arguments = methodInvocation.getArguments();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new RemoteInvocation for the given parameters.
|
||||
* @param methodName the name of the method to invoke
|
||||
* @param parameterTypes the parameter types of the method
|
||||
* @param arguments the arguments for the invocation
|
||||
*/
|
||||
public RemoteInvocation(String methodName, Class<?>[] parameterTypes, Object[] arguments) {
|
||||
this.methodName = methodName;
|
||||
this.parameterTypes = parameterTypes;
|
||||
this.arguments = arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new RemoteInvocation for JavaBean-style deserialization
|
||||
* (e.g. with Jackson).
|
||||
*/
|
||||
public RemoteInvocation() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the name of the target method.
|
||||
* <p>This setter is intended for JavaBean-style deserialization.
|
||||
*/
|
||||
public void setMethodName(String methodName) {
|
||||
this.methodName = methodName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the target method.
|
||||
*/
|
||||
public String getMethodName() {
|
||||
return this.methodName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the parameter types of the target method.
|
||||
* <p>This setter is intended for JavaBean-style deserialization.
|
||||
*/
|
||||
public void setParameterTypes(Class<?>[] parameterTypes) {
|
||||
this.parameterTypes = parameterTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the parameter types of the target method.
|
||||
*/
|
||||
public Class<?>[] getParameterTypes() {
|
||||
return this.parameterTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the arguments for the target method call.
|
||||
* <p>This setter is intended for JavaBean-style deserialization.
|
||||
*/
|
||||
public void setArguments(Object[] arguments) {
|
||||
this.arguments = arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the arguments for the target method call.
|
||||
*/
|
||||
public Object[] getArguments() {
|
||||
return this.arguments;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add an additional invocation attribute. Useful to add additional
|
||||
* invocation context without having to subclass RemoteInvocation.
|
||||
* <p>Attribute keys have to be unique, and no overriding of existing
|
||||
* attributes is allowed.
|
||||
* <p>The implementation avoids to unnecessarily create the attributes
|
||||
* Map, to minimize serialization size.
|
||||
* @param key the attribute key
|
||||
* @param value the attribute value
|
||||
* @throws IllegalStateException if the key is already bound
|
||||
*/
|
||||
public void addAttribute(String key, Serializable value) throws IllegalStateException {
|
||||
if (this.attributes == null) {
|
||||
this.attributes = new HashMap<>();
|
||||
}
|
||||
if (this.attributes.containsKey(key)) {
|
||||
throw new IllegalStateException("There is already an attribute with key '" + key + "' bound");
|
||||
}
|
||||
this.attributes.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the attribute for the given key, if any.
|
||||
* <p>The implementation avoids to unnecessarily create the attributes
|
||||
* Map, to minimize serialization size.
|
||||
* @param key the attribute key
|
||||
* @return the attribute value, or {@code null} if not defined
|
||||
*/
|
||||
@Nullable
|
||||
public Serializable getAttribute(String key) {
|
||||
if (this.attributes == null) {
|
||||
return null;
|
||||
}
|
||||
return this.attributes.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the attributes Map. Only here for special purposes:
|
||||
* Preferably, use {@link #addAttribute} and {@link #getAttribute}.
|
||||
* @param attributes the attributes Map
|
||||
* @see #addAttribute
|
||||
* @see #getAttribute
|
||||
*/
|
||||
public void setAttributes(@Nullable Map<String, Serializable> attributes) {
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the attributes Map. Mainly here for debugging purposes:
|
||||
* Preferably, use {@link #addAttribute} and {@link #getAttribute}.
|
||||
* @return the attributes Map, or {@code null} if none created
|
||||
* @see #addAttribute
|
||||
* @see #getAttribute
|
||||
*/
|
||||
@Nullable
|
||||
public Map<String, Serializable> getAttributes() {
|
||||
return this.attributes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Perform this invocation on the given target object.
|
||||
* Typically called when a RemoteInvocation is received on the server.
|
||||
* @param targetObject the target object to apply the invocation to
|
||||
* @return the invocation result
|
||||
* @throws NoSuchMethodException if the method name could not be resolved
|
||||
* @throws IllegalAccessException if the method could not be accessed
|
||||
* @throws InvocationTargetException if the method invocation resulted in an exception
|
||||
* @see java.lang.reflect.Method#invoke
|
||||
*/
|
||||
public Object invoke(Object targetObject)
|
||||
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
|
||||
Method method = targetObject.getClass().getMethod(this.methodName, this.parameterTypes);
|
||||
return method.invoke(targetObject, this.arguments);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RemoteInvocation: method name '" + this.methodName + "'; parameter types " +
|
||||
ClassUtils.classNamesToString(this.parameterTypes);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.support;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Abstract base class for remote service accessors that are based
|
||||
* on serialization of {@link RemoteInvocation} objects.
|
||||
*
|
||||
* Provides a "remoteInvocationFactory" property, with a
|
||||
* {@link DefaultRemoteInvocationFactory} as default strategy.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
* @see #setRemoteInvocationFactory
|
||||
* @see RemoteInvocation
|
||||
* @see RemoteInvocationFactory
|
||||
* @see DefaultRemoteInvocationFactory
|
||||
*/
|
||||
public abstract class RemoteInvocationBasedAccessor extends UrlBasedRemoteAccessor {
|
||||
|
||||
private RemoteInvocationFactory remoteInvocationFactory = new DefaultRemoteInvocationFactory();
|
||||
|
||||
|
||||
/**
|
||||
* Set the RemoteInvocationFactory to use for this accessor.
|
||||
* Default is a {@link DefaultRemoteInvocationFactory}.
|
||||
* <p>A custom invocation factory can add further context information
|
||||
* to the invocation, for example user credentials.
|
||||
*/
|
||||
public void setRemoteInvocationFactory(RemoteInvocationFactory remoteInvocationFactory) {
|
||||
this.remoteInvocationFactory =
|
||||
(remoteInvocationFactory != null ? remoteInvocationFactory : new DefaultRemoteInvocationFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the RemoteInvocationFactory used by this accessor.
|
||||
*/
|
||||
public RemoteInvocationFactory getRemoteInvocationFactory() {
|
||||
return this.remoteInvocationFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new RemoteInvocation object for the given AOP method invocation.
|
||||
* <p>The default implementation delegates to the configured
|
||||
* {@link #setRemoteInvocationFactory RemoteInvocationFactory}.
|
||||
* This can be overridden in subclasses in order to provide custom RemoteInvocation
|
||||
* subclasses, containing additional invocation parameters (e.g. user credentials).
|
||||
* <p>Note that it is preferable to build a custom RemoteInvocationFactory
|
||||
* as a reusable strategy, instead of overriding this method.
|
||||
* @param methodInvocation the current AOP method invocation
|
||||
* @return the RemoteInvocation object
|
||||
* @see RemoteInvocationFactory#createRemoteInvocation
|
||||
*/
|
||||
protected RemoteInvocation createRemoteInvocation(MethodInvocation methodInvocation) {
|
||||
return getRemoteInvocationFactory().createRemoteInvocation(methodInvocation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recreate the invocation result contained in the given RemoteInvocationResult object.
|
||||
* <p>The default implementation calls the default {@code recreate()} method.
|
||||
* This can be overridden in subclass to provide custom recreation, potentially
|
||||
* processing the returned result object.
|
||||
* @param result the RemoteInvocationResult to recreate
|
||||
* @return a return value if the invocation result is a successful return
|
||||
* @throws Throwable if the invocation result is an exception
|
||||
* @see RemoteInvocationResult#recreate()
|
||||
*/
|
||||
@Nullable
|
||||
protected Object recreateRemoteInvocationResult(RemoteInvocationResult result) throws Throwable {
|
||||
return result.recreate();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.support;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
/**
|
||||
* Abstract base class for remote service exporters that are based
|
||||
* on deserialization of {@link RemoteInvocation} objects.
|
||||
*
|
||||
* <p>Provides a "remoteInvocationExecutor" property, with a
|
||||
* {@link DefaultRemoteInvocationExecutor} as default strategy.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
* @see RemoteInvocationExecutor
|
||||
* @see DefaultRemoteInvocationExecutor
|
||||
*/
|
||||
public abstract class RemoteInvocationBasedExporter extends RemoteExporter {
|
||||
|
||||
private RemoteInvocationExecutor remoteInvocationExecutor = new DefaultRemoteInvocationExecutor();
|
||||
|
||||
|
||||
/**
|
||||
* Set the RemoteInvocationExecutor to use for this exporter.
|
||||
* Default is a DefaultRemoteInvocationExecutor.
|
||||
* <p>A custom invocation executor can extract further context information
|
||||
* from the invocation, for example user credentials.
|
||||
*/
|
||||
public void setRemoteInvocationExecutor(RemoteInvocationExecutor remoteInvocationExecutor) {
|
||||
this.remoteInvocationExecutor = remoteInvocationExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the RemoteInvocationExecutor used by this exporter.
|
||||
*/
|
||||
public RemoteInvocationExecutor getRemoteInvocationExecutor() {
|
||||
return this.remoteInvocationExecutor;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply the given remote invocation to the given target object.
|
||||
* The default implementation delegates to the RemoteInvocationExecutor.
|
||||
* <p>Can be overridden in subclasses for custom invocation behavior,
|
||||
* possibly for applying additional invocation parameters from a
|
||||
* custom RemoteInvocation subclass. Note that it is preferable to use
|
||||
* a custom RemoteInvocationExecutor which is a reusable strategy.
|
||||
* @param invocation the remote invocation
|
||||
* @param targetObject the target object to apply the invocation to
|
||||
* @return the invocation result
|
||||
* @throws NoSuchMethodException if the method name could not be resolved
|
||||
* @throws IllegalAccessException if the method could not be accessed
|
||||
* @throws InvocationTargetException if the method invocation resulted in an exception
|
||||
* @see RemoteInvocationExecutor#invoke
|
||||
*/
|
||||
protected Object invoke(RemoteInvocation invocation, Object targetObject)
|
||||
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Executing " + invocation);
|
||||
}
|
||||
try {
|
||||
return getRemoteInvocationExecutor().invoke(invocation, targetObject);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not find target method for " + invocation, ex);
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
catch (IllegalAccessException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not access target method for " + invocation, ex);
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Target method failed for " + invocation, ex.getTargetException());
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the given remote invocation to the given target object, wrapping
|
||||
* the invocation result in a serializable RemoteInvocationResult object.
|
||||
* The default implementation creates a plain RemoteInvocationResult.
|
||||
* <p>Can be overridden in subclasses for custom invocation behavior,
|
||||
* for example to return additional context information. Note that this
|
||||
* is not covered by the RemoteInvocationExecutor strategy!
|
||||
* @param invocation the remote invocation
|
||||
* @param targetObject the target object to apply the invocation to
|
||||
* @return the invocation result
|
||||
* @see #invoke
|
||||
*/
|
||||
protected RemoteInvocationResult invokeAndCreateResult(RemoteInvocation invocation, Object targetObject) {
|
||||
try {
|
||||
Object value = invoke(invocation, targetObject);
|
||||
return new RemoteInvocationResult(value);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
return new RemoteInvocationResult(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.support;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
/**
|
||||
* Strategy interface for executing a {@link RemoteInvocation} on a target object.
|
||||
*
|
||||
* <p>Used by {@link org.springframework.remoting.rmi.RmiServiceExporter} (for RMI invokers)
|
||||
* and by {@link org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
* @see DefaultRemoteInvocationFactory
|
||||
* @see org.springframework.remoting.rmi.RmiServiceExporter#setRemoteInvocationExecutor
|
||||
* @see org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter#setRemoteInvocationExecutor
|
||||
*/
|
||||
public interface RemoteInvocationExecutor {
|
||||
|
||||
/**
|
||||
* Perform this invocation on the given target object.
|
||||
* Typically called when a RemoteInvocation is received on the server.
|
||||
* @param invocation the RemoteInvocation
|
||||
* @param targetObject the target object to apply the invocation to
|
||||
* @return the invocation result
|
||||
* @throws NoSuchMethodException if the method name could not be resolved
|
||||
* @throws IllegalAccessException if the method could not be accessed
|
||||
* @throws InvocationTargetException if the method invocation resulted in an exception
|
||||
* @see java.lang.reflect.Method#invoke
|
||||
*/
|
||||
Object invoke(RemoteInvocation invocation, Object targetObject)
|
||||
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException;
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.support;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
/**
|
||||
* Strategy interface for creating a {@link RemoteInvocation} from an AOP Alliance
|
||||
* {@link org.aopalliance.intercept.MethodInvocation}.
|
||||
*
|
||||
* <p>Used by {@link org.springframework.remoting.rmi.RmiClientInterceptor} (for RMI invokers)
|
||||
* and by {@link org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
* @see DefaultRemoteInvocationFactory
|
||||
* @see org.springframework.remoting.rmi.RmiClientInterceptor#setRemoteInvocationFactory
|
||||
* @see org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor#setRemoteInvocationFactory
|
||||
*/
|
||||
public interface RemoteInvocationFactory {
|
||||
|
||||
/**
|
||||
* Create a serializable RemoteInvocation object from the given AOP
|
||||
* MethodInvocation.
|
||||
* <p>Can be implemented to add custom context information to the
|
||||
* remote invocation, for example user credentials.
|
||||
* @param methodInvocation the original AOP MethodInvocation object
|
||||
* @return the RemoteInvocation object
|
||||
*/
|
||||
RemoteInvocation createRemoteInvocation(MethodInvocation methodInvocation);
|
||||
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Encapsulates a remote invocation result, holding a result value or an exception.
|
||||
* Used for HTTP-based serialization invokers.
|
||||
*
|
||||
* <p>This is an SPI class, typically not used directly by applications.
|
||||
* Can be subclassed for additional invocation parameters.
|
||||
*
|
||||
* <p>Both {@link RemoteInvocation} and {@link RemoteInvocationResult} are designed
|
||||
* for use with standard Java serialization as well as JavaBean-style serialization.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
* @see RemoteInvocation
|
||||
*/
|
||||
public class RemoteInvocationResult implements Serializable {
|
||||
|
||||
/** Use serialVersionUID from Spring 1.1 for interoperability. */
|
||||
private static final long serialVersionUID = 2138555143707773549L;
|
||||
|
||||
|
||||
@Nullable
|
||||
private Object value;
|
||||
|
||||
@Nullable
|
||||
private Throwable exception;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new RemoteInvocationResult for the given result value.
|
||||
* @param value the result value returned by a successful invocation
|
||||
* of the target method
|
||||
*/
|
||||
public RemoteInvocationResult(@Nullable Object value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new RemoteInvocationResult for the given exception.
|
||||
* @param exception the exception thrown by an unsuccessful invocation
|
||||
* of the target method
|
||||
*/
|
||||
public RemoteInvocationResult(@Nullable Throwable exception) {
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new RemoteInvocationResult for JavaBean-style deserialization
|
||||
* (e.g. with Jackson).
|
||||
* @see #setValue
|
||||
* @see #setException
|
||||
*/
|
||||
public RemoteInvocationResult() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the result value returned by a successful invocation of the
|
||||
* target method, if any.
|
||||
* <p>This setter is intended for JavaBean-style deserialization.
|
||||
* Use {@link #RemoteInvocationResult(Object)} otherwise.
|
||||
* @see #RemoteInvocationResult()
|
||||
*/
|
||||
public void setValue(@Nullable Object value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the result value returned by a successful invocation
|
||||
* of the target method, if any.
|
||||
* @see #hasException
|
||||
*/
|
||||
@Nullable
|
||||
public Object getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the exception thrown by an unsuccessful invocation of the
|
||||
* target method, if any.
|
||||
* <p>This setter is intended for JavaBean-style deserialization.
|
||||
* Use {@link #RemoteInvocationResult(Throwable)} otherwise.
|
||||
* @see #RemoteInvocationResult()
|
||||
*/
|
||||
public void setException(@Nullable Throwable exception) {
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the exception thrown by an unsuccessful invocation
|
||||
* of the target method, if any.
|
||||
* @see #hasException
|
||||
*/
|
||||
@Nullable
|
||||
public Throwable getException() {
|
||||
return this.exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this invocation result holds an exception.
|
||||
* If this returns {@code false}, the result value applies
|
||||
* (even if it is {@code null}).
|
||||
* @see #getValue
|
||||
* @see #getException
|
||||
*/
|
||||
public boolean hasException() {
|
||||
return (this.exception != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this invocation result holds an InvocationTargetException,
|
||||
* thrown by an invocation of the target method itself.
|
||||
* @see #hasException()
|
||||
*/
|
||||
public boolean hasInvocationTargetException() {
|
||||
return (this.exception instanceof InvocationTargetException);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Recreate the invocation result, either returning the result value
|
||||
* in case of a successful invocation of the target method, or
|
||||
* rethrowing the exception thrown by the target method.
|
||||
* @return the result value, if any
|
||||
* @throws Throwable the exception, if any
|
||||
*/
|
||||
@Nullable
|
||||
public Object recreate() throws Throwable {
|
||||
if (this.exception != null) {
|
||||
Throwable exToThrow = this.exception;
|
||||
if (this.exception instanceof InvocationTargetException) {
|
||||
exToThrow = ((InvocationTargetException) this.exception).getTargetException();
|
||||
}
|
||||
RemoteInvocationUtils.fillInClientStackTraceIfPossible(exToThrow);
|
||||
throw exToThrow;
|
||||
}
|
||||
else {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* AOP Alliance MethodInterceptor for tracing remote invocations.
|
||||
* Automatically applied by RemoteExporter and its subclasses.
|
||||
*
|
||||
* <p>Logs an incoming remote call as well as the finished processing of a remote call
|
||||
* at DEBUG level. If the processing of a remote call results in a checked exception,
|
||||
* the exception will get logged at INFO level; if it results in an unchecked
|
||||
* exception (or error), the exception will get logged at WARN level.
|
||||
*
|
||||
* <p>The logging of exceptions is particularly useful to save the stacktrace
|
||||
* information on the server-side rather than just propagating the exception
|
||||
* to the client (who might or might not log it properly).
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.2
|
||||
* @see RemoteExporter#setRegisterTraceInterceptor
|
||||
* @see RemoteExporter#getProxyForService
|
||||
*/
|
||||
public class RemoteInvocationTraceInterceptor implements MethodInterceptor {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(RemoteInvocationTraceInterceptor.class);
|
||||
|
||||
private final String exporterNameClause;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new RemoteInvocationTraceInterceptor.
|
||||
*/
|
||||
public RemoteInvocationTraceInterceptor() {
|
||||
this.exporterNameClause = "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new RemoteInvocationTraceInterceptor.
|
||||
* @param exporterName the name of the remote exporter
|
||||
* (to be used as context information in log messages)
|
||||
*/
|
||||
public RemoteInvocationTraceInterceptor(String exporterName) {
|
||||
this.exporterNameClause = exporterName + " ";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
Method method = invocation.getMethod();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Incoming " + this.exporterNameClause + "remote call: " +
|
||||
ClassUtils.getQualifiedMethodName(method));
|
||||
}
|
||||
try {
|
||||
Object retVal = invocation.proceed();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Finished processing of " + this.exporterNameClause + "remote call: " +
|
||||
ClassUtils.getQualifiedMethodName(method));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
if (ex instanceof RuntimeException || ex instanceof Error) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Processing of " + this.exporterNameClause + "remote call resulted in fatal exception: " +
|
||||
ClassUtils.getQualifiedMethodName(method), ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Processing of " + this.exporterNameClause + "remote call resulted in exception: " +
|
||||
ClassUtils.getQualifiedMethodName(method), ex);
|
||||
}
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.support;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* General utilities for handling remote invocations.
|
||||
*
|
||||
* <p>Mainly intended for use within the remoting framework.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public abstract class RemoteInvocationUtils {
|
||||
|
||||
/**
|
||||
* Fill the current client-side stack trace into the given exception.
|
||||
* <p>The given exception is typically thrown on the server and serialized
|
||||
* as-is, with the client wanting it to contain the client-side portion
|
||||
* of the stack trace as well. What we can do here is to update the
|
||||
* {@code StackTraceElement} array with the current client-side stack
|
||||
* trace, provided that we run on JDK 1.4+.
|
||||
* @param ex the exception to update
|
||||
* @see Throwable#getStackTrace()
|
||||
* @see Throwable#setStackTrace(StackTraceElement[])
|
||||
*/
|
||||
public static void fillInClientStackTraceIfPossible(Throwable ex) {
|
||||
if (ex != null) {
|
||||
StackTraceElement[] clientStack = new Throwable().getStackTrace();
|
||||
Set<Throwable> visitedExceptions = new HashSet<>();
|
||||
Throwable exToUpdate = ex;
|
||||
while (exToUpdate != null && !visitedExceptions.contains(exToUpdate)) {
|
||||
StackTraceElement[] serverStack = exToUpdate.getStackTrace();
|
||||
StackTraceElement[] combinedStack = new StackTraceElement[serverStack.length + clientStack.length];
|
||||
System.arraycopy(serverStack, 0, combinedStack, 0, serverStack.length);
|
||||
System.arraycopy(clientStack, 0, combinedStack, serverStack.length, clientStack.length);
|
||||
exToUpdate.setStackTrace(combinedStack);
|
||||
visitedExceptions.add(exToUpdate);
|
||||
exToUpdate = exToUpdate.getCause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.support;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Generic support base class for remote accessor and exporters,
|
||||
* providing common bean ClassLoader handling.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5.2
|
||||
*/
|
||||
public abstract class RemotingSupport implements BeanClassLoaderAware {
|
||||
|
||||
/** Logger available to subclasses. */
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
|
||||
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.beanClassLoader = classLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ClassLoader that this accessor operates in,
|
||||
* to be used for deserializing and for generating proxies.
|
||||
*/
|
||||
protected ClassLoader getBeanClassLoader() {
|
||||
return this.beanClassLoader;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Override the thread context ClassLoader with the environment's bean ClassLoader
|
||||
* if necessary, i.e. if the bean ClassLoader is not equivalent to the thread
|
||||
* context ClassLoader already.
|
||||
* @return the original thread context ClassLoader, or {@code null} if not overridden
|
||||
*/
|
||||
@Nullable
|
||||
protected ClassLoader overrideThreadContextClassLoader() {
|
||||
return ClassUtils.overrideThreadContextClassLoader(getBeanClassLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the original thread context ClassLoader if necessary.
|
||||
* @param original the original thread context ClassLoader,
|
||||
* or {@code null} if not overridden (and hence nothing to reset)
|
||||
*/
|
||||
protected void resetThreadContextClassLoader(@Nullable ClassLoader original) {
|
||||
if (original != null) {
|
||||
Thread.currentThread().setContextClassLoader(original);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.support;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import com.sun.net.httpserver.Authenticator;
|
||||
import com.sun.net.httpserver.Filter;
|
||||
import com.sun.net.httpserver.HttpContext;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.FactoryBean} that creates a simple
|
||||
* HTTP server, based on the HTTP server that is included in Sun's JRE 1.6.
|
||||
* Starts the HTTP server on initialization and stops it on destruction.
|
||||
* Exposes the resulting {@link com.sun.net.httpserver.HttpServer} object.
|
||||
*
|
||||
* <p>Allows for registering {@link com.sun.net.httpserver.HttpHandler HttpHandlers}
|
||||
* for specific {@link #setContexts context paths}. Alternatively,
|
||||
* register such context-specific handlers programmatically on the
|
||||
* {@link com.sun.net.httpserver.HttpServer} itself.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Arjen Poutsma
|
||||
* @since 2.5.1
|
||||
* @see #setPort
|
||||
* @see #setContexts
|
||||
* @deprecated as of Spring Framework 5.1, in favor of embedded Tomcat/Jetty/Undertow
|
||||
*/
|
||||
@Deprecated
|
||||
@org.springframework.lang.UsesSunHttpServer
|
||||
public class SimpleHttpServerFactoryBean implements FactoryBean<HttpServer>, InitializingBean, DisposableBean {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private int port = 8080;
|
||||
|
||||
private String hostname;
|
||||
|
||||
private int backlog = -1;
|
||||
|
||||
private int shutdownDelay = 0;
|
||||
|
||||
private Executor executor;
|
||||
|
||||
private Map<String, HttpHandler> contexts;
|
||||
|
||||
private List<Filter> filters;
|
||||
|
||||
private Authenticator authenticator;
|
||||
|
||||
private HttpServer server;
|
||||
|
||||
|
||||
/**
|
||||
* Specify the HTTP server's port. Default is 8080.
|
||||
*/
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the HTTP server's hostname to bind to. Default is localhost;
|
||||
* can be overridden with a specific network address to bind to.
|
||||
*/
|
||||
public void setHostname(String hostname) {
|
||||
this.hostname = hostname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the HTTP server's TCP backlog. Default is -1,
|
||||
* indicating the system's default value.
|
||||
*/
|
||||
public void setBacklog(int backlog) {
|
||||
this.backlog = backlog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the number of seconds to wait until HTTP exchanges have
|
||||
* completed when shutting down the HTTP server. Default is 0.
|
||||
*/
|
||||
public void setShutdownDelay(int shutdownDelay) {
|
||||
this.shutdownDelay = shutdownDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JDK concurrent executor to use for dispatching incoming requests.
|
||||
* @see com.sun.net.httpserver.HttpServer#setExecutor
|
||||
*/
|
||||
public void setExecutor(Executor executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register {@link com.sun.net.httpserver.HttpHandler HttpHandlers}
|
||||
* for specific context paths.
|
||||
* @param contexts a Map with context paths as keys and HttpHandler
|
||||
* objects as values
|
||||
* @see org.springframework.remoting.httpinvoker.SimpleHttpInvokerServiceExporter
|
||||
* @see org.springframework.remoting.caucho.SimpleHessianServiceExporter
|
||||
*/
|
||||
public void setContexts(Map<String, HttpHandler> contexts) {
|
||||
this.contexts = contexts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register common {@link com.sun.net.httpserver.Filter Filters} to be
|
||||
* applied to all locally registered {@link #setContexts contexts}.
|
||||
*/
|
||||
public void setFilters(List<Filter> filters) {
|
||||
this.filters = filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a common {@link com.sun.net.httpserver.Authenticator} to be
|
||||
* applied to all locally registered {@link #setContexts contexts}.
|
||||
*/
|
||||
public void setAuthenticator(Authenticator authenticator) {
|
||||
this.authenticator = authenticator;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws IOException {
|
||||
InetSocketAddress address = (this.hostname != null ?
|
||||
new InetSocketAddress(this.hostname, this.port) : new InetSocketAddress(this.port));
|
||||
this.server = HttpServer.create(address, this.backlog);
|
||||
if (this.executor != null) {
|
||||
this.server.setExecutor(this.executor);
|
||||
}
|
||||
if (this.contexts != null) {
|
||||
this.contexts.forEach((key, context) -> {
|
||||
HttpContext httpContext = this.server.createContext(key, context);
|
||||
if (this.filters != null) {
|
||||
httpContext.getFilters().addAll(this.filters);
|
||||
}
|
||||
if (this.authenticator != null) {
|
||||
httpContext.setAuthenticator(this.authenticator);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Starting HttpServer at address " + address);
|
||||
}
|
||||
this.server.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpServer getObject() {
|
||||
return this.server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends HttpServer> getObjectType() {
|
||||
return (this.server != null ? this.server.getClass() : HttpServer.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
logger.info("Stopping HttpServer");
|
||||
this.server.stop(this.shutdownDelay);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.support;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
/**
|
||||
* Abstract base class for classes that access remote services via URLs.
|
||||
* Provides a "serviceUrl" bean property, which is considered as required.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 15.12.2003
|
||||
*/
|
||||
public abstract class UrlBasedRemoteAccessor extends RemoteAccessor implements InitializingBean {
|
||||
|
||||
private String serviceUrl;
|
||||
|
||||
|
||||
/**
|
||||
* Set the URL of this remote accessor's target service.
|
||||
* The URL must be compatible with the rules of the particular remoting provider.
|
||||
*/
|
||||
public void setServiceUrl(String serviceUrl) {
|
||||
this.serviceUrl = serviceUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the URL of this remote accessor's target service.
|
||||
*/
|
||||
public String getServiceUrl() {
|
||||
return this.serviceUrl;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
if (getServiceUrl() == null) {
|
||||
throw new IllegalArgumentException("Property 'serviceUrl' is required");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
/**
|
||||
* Generic support classes for remoting implementations.
|
||||
* Provides abstract base classes for remote proxy factories.
|
||||
*/
|
||||
package org.springframework.remoting.support;
|
||||
@@ -1,445 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.rmi;
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.rmi.ConnectException;
|
||||
import java.rmi.ConnectIOException;
|
||||
import java.rmi.MarshalException;
|
||||
import java.rmi.NoSuchObjectException;
|
||||
import java.rmi.Remote;
|
||||
import java.rmi.RemoteException;
|
||||
import java.rmi.StubNotFoundException;
|
||||
import java.rmi.UnknownHostException;
|
||||
import java.rmi.UnmarshalException;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.remoting.RemoteAccessException;
|
||||
import org.springframework.remoting.RemoteConnectFailureException;
|
||||
import org.springframework.remoting.RemoteProxyFailureException;
|
||||
import org.springframework.remoting.support.RemoteInvocation;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
* @since 16.05.2003
|
||||
*/
|
||||
public class RmiSupportTests {
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBean() throws Exception {
|
||||
CountingRmiProxyFactoryBean factory = new CountingRmiProxyFactoryBean();
|
||||
factory.setServiceInterface(IRemoteBean.class);
|
||||
factory.setServiceUrl("rmi://localhost:1090/test");
|
||||
factory.afterPropertiesSet();
|
||||
assertThat(factory.isSingleton()).as("Correct singleton value").isTrue();
|
||||
boolean condition = factory.getObject() instanceof IRemoteBean;
|
||||
assertThat(condition).isTrue();
|
||||
IRemoteBean proxy = (IRemoteBean) factory.getObject();
|
||||
proxy.setName("myName");
|
||||
assertThat(RemoteBean.name).isEqualTo("myName");
|
||||
assertThat(factory.counter).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithRemoteException() throws Exception {
|
||||
doTestRmiProxyFactoryBeanWithException(RemoteException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithConnectException() throws Exception {
|
||||
doTestRmiProxyFactoryBeanWithException(ConnectException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithConnectIOException() throws Exception {
|
||||
doTestRmiProxyFactoryBeanWithException(ConnectIOException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithUnknownHostException() throws Exception {
|
||||
doTestRmiProxyFactoryBeanWithException(UnknownHostException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithNoSuchObjectException() throws Exception {
|
||||
doTestRmiProxyFactoryBeanWithException(NoSuchObjectException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithStubNotFoundException() throws Exception {
|
||||
doTestRmiProxyFactoryBeanWithException(StubNotFoundException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithMarshalException() throws Exception {
|
||||
doTestRmiProxyFactoryBeanWithException(MarshalException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithUnmarshalException() throws Exception {
|
||||
doTestRmiProxyFactoryBeanWithException(UnmarshalException.class);
|
||||
}
|
||||
|
||||
private void doTestRmiProxyFactoryBeanWithException(Class<? extends Throwable> exceptionClass) throws Exception {
|
||||
CountingRmiProxyFactoryBean factory = new CountingRmiProxyFactoryBean();
|
||||
factory.setServiceInterface(IRemoteBean.class);
|
||||
factory.setServiceUrl("rmi://localhost:1090/test");
|
||||
factory.afterPropertiesSet();
|
||||
boolean condition = factory.getObject() instanceof IRemoteBean;
|
||||
assertThat(condition).isTrue();
|
||||
IRemoteBean proxy = (IRemoteBean) factory.getObject();
|
||||
assertThatExceptionOfType(exceptionClass).isThrownBy(() ->
|
||||
proxy.setName(exceptionClass.getName()));
|
||||
assertThat(factory.counter).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithConnectExceptionAndRefresh() throws Exception {
|
||||
doTestRmiProxyFactoryBeanWithExceptionAndRefresh(ConnectException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithConnectIOExceptionAndRefresh() throws Exception {
|
||||
doTestRmiProxyFactoryBeanWithExceptionAndRefresh(ConnectIOException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithUnknownHostExceptionAndRefresh() throws Exception {
|
||||
doTestRmiProxyFactoryBeanWithExceptionAndRefresh(UnknownHostException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithNoSuchObjectExceptionAndRefresh() throws Exception {
|
||||
doTestRmiProxyFactoryBeanWithExceptionAndRefresh(NoSuchObjectException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithStubNotFoundExceptionAndRefresh() throws Exception {
|
||||
doTestRmiProxyFactoryBeanWithExceptionAndRefresh(StubNotFoundException.class);
|
||||
}
|
||||
|
||||
private void doTestRmiProxyFactoryBeanWithExceptionAndRefresh(Class<? extends Throwable> exceptionClass) throws Exception {
|
||||
CountingRmiProxyFactoryBean factory = new CountingRmiProxyFactoryBean();
|
||||
factory.setServiceInterface(IRemoteBean.class);
|
||||
factory.setServiceUrl("rmi://localhost:1090/test");
|
||||
factory.setRefreshStubOnConnectFailure(true);
|
||||
factory.afterPropertiesSet();
|
||||
boolean condition = factory.getObject() instanceof IRemoteBean;
|
||||
assertThat(condition).isTrue();
|
||||
IRemoteBean proxy = (IRemoteBean) factory.getObject();
|
||||
assertThatExceptionOfType(exceptionClass).isThrownBy(() ->
|
||||
proxy.setName(exceptionClass.getName()));
|
||||
assertThat(factory.counter).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithBusinessInterface() throws Exception {
|
||||
CountingRmiProxyFactoryBean factory = new CountingRmiProxyFactoryBean();
|
||||
factory.setServiceInterface(IBusinessBean.class);
|
||||
factory.setServiceUrl("rmi://localhost:1090/test");
|
||||
factory.afterPropertiesSet();
|
||||
boolean condition = factory.getObject() instanceof IBusinessBean;
|
||||
assertThat(condition).isTrue();
|
||||
IBusinessBean proxy = (IBusinessBean) factory.getObject();
|
||||
boolean condition1 = proxy instanceof IRemoteBean;
|
||||
assertThat(condition1).isFalse();
|
||||
proxy.setName("myName");
|
||||
assertThat(RemoteBean.name).isEqualTo("myName");
|
||||
assertThat(factory.counter).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithWrongBusinessInterface() throws Exception {
|
||||
CountingRmiProxyFactoryBean factory = new CountingRmiProxyFactoryBean();
|
||||
factory.setServiceInterface(IWrongBusinessBean.class);
|
||||
factory.setServiceUrl("rmi://localhost:1090/test");
|
||||
factory.afterPropertiesSet();
|
||||
boolean condition = factory.getObject() instanceof IWrongBusinessBean;
|
||||
assertThat(condition).isTrue();
|
||||
IWrongBusinessBean proxy = (IWrongBusinessBean) factory.getObject();
|
||||
boolean condition1 = proxy instanceof IRemoteBean;
|
||||
assertThat(condition1).isFalse();
|
||||
assertThatExceptionOfType(RemoteProxyFailureException.class).isThrownBy(() ->
|
||||
proxy.setOtherName("name"))
|
||||
.withCauseInstanceOf(NoSuchMethodException.class)
|
||||
.withMessageContaining("setOtherName")
|
||||
.withMessageContaining("IWrongBusinessBean");
|
||||
assertThat(factory.counter).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithBusinessInterfaceAndRemoteException() throws Exception {
|
||||
doTestRmiProxyFactoryBeanWithBusinessInterfaceAndException(
|
||||
RemoteException.class, RemoteAccessException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithBusinessInterfaceAndConnectException() throws Exception {
|
||||
doTestRmiProxyFactoryBeanWithBusinessInterfaceAndException(
|
||||
ConnectException.class, RemoteConnectFailureException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithBusinessInterfaceAndConnectIOException() throws Exception {
|
||||
doTestRmiProxyFactoryBeanWithBusinessInterfaceAndException(
|
||||
ConnectIOException.class, RemoteConnectFailureException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithBusinessInterfaceAndUnknownHostException() throws Exception {
|
||||
doTestRmiProxyFactoryBeanWithBusinessInterfaceAndException(
|
||||
UnknownHostException.class, RemoteConnectFailureException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithBusinessInterfaceAndNoSuchObjectExceptionException() throws Exception {
|
||||
doTestRmiProxyFactoryBeanWithBusinessInterfaceAndException(
|
||||
NoSuchObjectException.class, RemoteConnectFailureException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithBusinessInterfaceAndStubNotFoundException() throws Exception {
|
||||
doTestRmiProxyFactoryBeanWithBusinessInterfaceAndException(
|
||||
StubNotFoundException.class, RemoteConnectFailureException.class);
|
||||
}
|
||||
|
||||
private void doTestRmiProxyFactoryBeanWithBusinessInterfaceAndException(
|
||||
Class<?> rmiExceptionClass, Class<? extends Throwable> springExceptionClass) throws Exception {
|
||||
|
||||
CountingRmiProxyFactoryBean factory = new CountingRmiProxyFactoryBean();
|
||||
factory.setServiceInterface(IBusinessBean.class);
|
||||
factory.setServiceUrl("rmi://localhost:1090/test");
|
||||
factory.afterPropertiesSet();
|
||||
boolean condition = factory.getObject() instanceof IBusinessBean;
|
||||
assertThat(condition).isTrue();
|
||||
IBusinessBean proxy = (IBusinessBean) factory.getObject();
|
||||
boolean condition1 = proxy instanceof IRemoteBean;
|
||||
assertThat(condition1).isFalse();
|
||||
assertThatExceptionOfType(springExceptionClass).isThrownBy(() ->
|
||||
proxy.setName(rmiExceptionClass.getName()));
|
||||
assertThat(factory.counter).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithBusinessInterfaceAndRemoteExceptionAndRefresh() throws Exception {
|
||||
doTestRmiProxyFactoryBeanWithBusinessInterfaceAndExceptionAndRefresh(
|
||||
RemoteException.class, RemoteAccessException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithBusinessInterfaceAndConnectExceptionAndRefresh() throws Exception {
|
||||
doTestRmiProxyFactoryBeanWithBusinessInterfaceAndExceptionAndRefresh(
|
||||
ConnectException.class, RemoteConnectFailureException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithBusinessInterfaceAndConnectIOExceptionAndRefresh() throws Exception {
|
||||
doTestRmiProxyFactoryBeanWithBusinessInterfaceAndExceptionAndRefresh(
|
||||
ConnectIOException.class, RemoteConnectFailureException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithBusinessInterfaceAndUnknownHostExceptionAndRefresh() throws Exception {
|
||||
doTestRmiProxyFactoryBeanWithBusinessInterfaceAndExceptionAndRefresh(
|
||||
UnknownHostException.class, RemoteConnectFailureException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithBusinessInterfaceAndNoSuchObjectExceptionAndRefresh() throws Exception {
|
||||
doTestRmiProxyFactoryBeanWithBusinessInterfaceAndExceptionAndRefresh(
|
||||
NoSuchObjectException.class, RemoteConnectFailureException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiProxyFactoryBeanWithBusinessInterfaceAndStubNotFoundExceptionAndRefresh() throws Exception {
|
||||
doTestRmiProxyFactoryBeanWithBusinessInterfaceAndExceptionAndRefresh(
|
||||
StubNotFoundException.class, RemoteConnectFailureException.class);
|
||||
}
|
||||
|
||||
private void doTestRmiProxyFactoryBeanWithBusinessInterfaceAndExceptionAndRefresh(
|
||||
Class<?> rmiExceptionClass, Class<? extends Throwable> springExceptionClass) throws Exception {
|
||||
|
||||
CountingRmiProxyFactoryBean factory = new CountingRmiProxyFactoryBean();
|
||||
factory.setServiceInterface(IBusinessBean.class);
|
||||
factory.setServiceUrl("rmi://localhost:1090/test");
|
||||
factory.setRefreshStubOnConnectFailure(true);
|
||||
factory.afterPropertiesSet();
|
||||
boolean condition = factory.getObject() instanceof IBusinessBean;
|
||||
assertThat(condition).isTrue();
|
||||
IBusinessBean proxy = (IBusinessBean) factory.getObject();
|
||||
boolean condition1 = proxy instanceof IRemoteBean;
|
||||
assertThat(condition1).isFalse();
|
||||
assertThatExceptionOfType(springExceptionClass).isThrownBy(() ->
|
||||
proxy.setName(rmiExceptionClass.getName()));
|
||||
boolean isRemoteConnectFailure = RemoteConnectFailureException.class.isAssignableFrom(springExceptionClass);
|
||||
assertThat(factory.counter).isEqualTo(isRemoteConnectFailure ? 2 : 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiClientInterceptorRequiresUrl() throws Exception{
|
||||
RmiClientInterceptor client = new RmiClientInterceptor();
|
||||
client.setServiceInterface(IRemoteBean.class);
|
||||
assertThatIllegalArgumentException().isThrownBy(client::afterPropertiesSet);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void remoteInvocation() throws NoSuchMethodException {
|
||||
// let's see if the remote invocation object works
|
||||
|
||||
final RemoteBean rb = new RemoteBean();
|
||||
final Method setNameMethod = rb.getClass().getDeclaredMethod("setName", String.class);
|
||||
|
||||
MethodInvocation mi = new MethodInvocation() {
|
||||
@Override
|
||||
public Method getMethod() {
|
||||
return setNameMethod;
|
||||
}
|
||||
@Override
|
||||
public Object[] getArguments() {
|
||||
return new Object[] {"bla"};
|
||||
}
|
||||
@Override
|
||||
public Object proceed() throws Throwable {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
@Override
|
||||
public Object getThis() {
|
||||
return rb;
|
||||
}
|
||||
@Override
|
||||
public AccessibleObject getStaticPart() {
|
||||
return setNameMethod;
|
||||
}
|
||||
};
|
||||
|
||||
RemoteInvocation inv = new RemoteInvocation(mi);
|
||||
|
||||
assertThat(inv.getMethodName()).isEqualTo("setName");
|
||||
assertThat(inv.getArguments()[0]).isEqualTo("bla");
|
||||
assertThat(inv.getParameterTypes()[0]).isEqualTo(String.class);
|
||||
|
||||
// this is a bit BS, but we need to test it
|
||||
inv = new RemoteInvocation();
|
||||
inv.setArguments(new Object[] { "bla" });
|
||||
assertThat(inv.getArguments()[0]).isEqualTo("bla");
|
||||
inv.setMethodName("setName");
|
||||
assertThat(inv.getMethodName()).isEqualTo("setName");
|
||||
inv.setParameterTypes(new Class<?>[] {String.class});
|
||||
assertThat(inv.getParameterTypes()[0]).isEqualTo(String.class);
|
||||
|
||||
inv = new RemoteInvocation("setName", new Class<?>[] {String.class}, new Object[] {"bla"});
|
||||
assertThat(inv.getArguments()[0]).isEqualTo("bla");
|
||||
assertThat(inv.getMethodName()).isEqualTo("setName");
|
||||
assertThat(inv.getParameterTypes()[0]).isEqualTo(String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rmiInvokerWithSpecialLocalMethods() throws Exception {
|
||||
String serviceUrl = "rmi://localhost:1090/test";
|
||||
RmiProxyFactoryBean factory = new RmiProxyFactoryBean() {
|
||||
@Override
|
||||
protected Remote lookupStub() {
|
||||
return new RmiInvocationHandler() {
|
||||
@Override
|
||||
public String getTargetInterfaceName() {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public Object invoke(RemoteInvocation invocation) throws RemoteException {
|
||||
throw new RemoteException();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
factory.setServiceInterface(IBusinessBean.class);
|
||||
factory.setServiceUrl(serviceUrl);
|
||||
factory.afterPropertiesSet();
|
||||
IBusinessBean proxy = (IBusinessBean) factory.getObject();
|
||||
|
||||
// shouldn't go through to remote service
|
||||
assertThat(proxy.toString().contains("RMI invoker")).isTrue();
|
||||
assertThat(proxy.toString().contains(serviceUrl)).isTrue();
|
||||
assertThat(proxy.hashCode()).isEqualTo(proxy.hashCode());
|
||||
assertThat(proxy.equals(proxy)).isTrue();
|
||||
|
||||
// should go through
|
||||
assertThatExceptionOfType(RemoteAccessException.class).isThrownBy(() ->
|
||||
proxy.setName("test"));
|
||||
}
|
||||
|
||||
|
||||
private static class CountingRmiProxyFactoryBean extends RmiProxyFactoryBean {
|
||||
|
||||
private int counter = 0;
|
||||
|
||||
@Override
|
||||
protected Remote lookupStub() {
|
||||
counter++;
|
||||
return new RemoteBean();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface IBusinessBean {
|
||||
|
||||
void setName(String name);
|
||||
}
|
||||
|
||||
|
||||
public interface IWrongBusinessBean {
|
||||
|
||||
void setOtherName(String name);
|
||||
}
|
||||
|
||||
|
||||
public interface IRemoteBean extends Remote {
|
||||
|
||||
void setName(String name) throws RemoteException;
|
||||
}
|
||||
|
||||
|
||||
public static class RemoteBean implements IRemoteBean {
|
||||
|
||||
private static String name;
|
||||
|
||||
@Override
|
||||
public void setName(String nam) throws RemoteException {
|
||||
if (nam != null && nam.endsWith("Exception")) {
|
||||
RemoteException rex;
|
||||
try {
|
||||
Class<?> exClass = Class.forName(nam);
|
||||
Constructor<?> ctor = exClass.getConstructor(String.class);
|
||||
rex = (RemoteException) ctor.newInstance("myMessage");
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new RemoteException("Illegal exception class name: " + nam, ex);
|
||||
}
|
||||
throw rex;
|
||||
}
|
||||
name = nam;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.support;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Rick Evans
|
||||
*/
|
||||
public class RemoteInvocationUtilsTests {
|
||||
|
||||
@Test
|
||||
public void fillInClientStackTraceIfPossibleSunnyDay() throws Exception {
|
||||
try {
|
||||
throw new IllegalStateException("Mmm");
|
||||
}
|
||||
catch (Exception ex) {
|
||||
int originalStackTraceLngth = ex.getStackTrace().length;
|
||||
RemoteInvocationUtils.fillInClientStackTraceIfPossible(ex);
|
||||
assertThat(ex.getStackTrace().length > originalStackTraceLngth).as("Stack trace not being filled in").isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fillInClientStackTraceIfPossibleWithNullThrowable() throws Exception {
|
||||
// just want to ensure that it doesn't bomb
|
||||
RemoteInvocationUtils.fillInClientStackTraceIfPossible(null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,437 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.jms.remoting;
|
||||
|
||||
import javax.jms.Connection;
|
||||
import javax.jms.ConnectionFactory;
|
||||
import javax.jms.JMSException;
|
||||
import javax.jms.Message;
|
||||
import javax.jms.MessageConsumer;
|
||||
import javax.jms.MessageFormatException;
|
||||
import javax.jms.MessageProducer;
|
||||
import javax.jms.Queue;
|
||||
import javax.jms.Session;
|
||||
import javax.jms.TemporaryQueue;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.jms.connection.ConnectionFactoryUtils;
|
||||
import org.springframework.jms.support.JmsUtils;
|
||||
import org.springframework.jms.support.converter.MessageConverter;
|
||||
import org.springframework.jms.support.converter.SimpleMessageConverter;
|
||||
import org.springframework.jms.support.destination.DestinationResolver;
|
||||
import org.springframework.jms.support.destination.DynamicDestinationResolver;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.remoting.RemoteAccessException;
|
||||
import org.springframework.remoting.RemoteInvocationFailureException;
|
||||
import org.springframework.remoting.RemoteTimeoutException;
|
||||
import org.springframework.remoting.support.DefaultRemoteInvocationFactory;
|
||||
import org.springframework.remoting.support.RemoteInvocation;
|
||||
import org.springframework.remoting.support.RemoteInvocationFactory;
|
||||
import org.springframework.remoting.support.RemoteInvocationResult;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link org.aopalliance.intercept.MethodInterceptor} for accessing a
|
||||
* JMS-based remote service.
|
||||
*
|
||||
* <p>Serializes remote invocation objects and deserializes remote invocation
|
||||
* result objects. Uses Java serialization just like RMI, but with the JMS
|
||||
* provider as communication infrastructure.
|
||||
*
|
||||
* <p>To be configured with a {@link javax.jms.QueueConnectionFactory} and a
|
||||
* target queue (either as {@link javax.jms.Queue} reference or as queue name).
|
||||
*
|
||||
* <p>Thanks to James Strachan for the original prototype that this
|
||||
* JMS invoker mechanism was inspired by!
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author James Strachan
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0
|
||||
* @see #setConnectionFactory
|
||||
* @see #setQueue
|
||||
* @see #setQueueName
|
||||
* @see org.springframework.jms.remoting.JmsInvokerServiceExporter
|
||||
* @see org.springframework.jms.remoting.JmsInvokerProxyFactoryBean
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
public class JmsInvokerClientInterceptor implements MethodInterceptor, InitializingBean {
|
||||
|
||||
@Nullable
|
||||
private ConnectionFactory connectionFactory;
|
||||
|
||||
@Nullable
|
||||
private Object queue;
|
||||
|
||||
private DestinationResolver destinationResolver = new DynamicDestinationResolver();
|
||||
|
||||
private RemoteInvocationFactory remoteInvocationFactory = new DefaultRemoteInvocationFactory();
|
||||
|
||||
private MessageConverter messageConverter = new SimpleMessageConverter();
|
||||
|
||||
private long receiveTimeout = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Set the QueueConnectionFactory to use for obtaining JMS QueueConnections.
|
||||
*/
|
||||
public void setConnectionFactory(@Nullable ConnectionFactory connectionFactory) {
|
||||
this.connectionFactory = connectionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the QueueConnectionFactory to use for obtaining JMS QueueConnections.
|
||||
*/
|
||||
@Nullable
|
||||
protected ConnectionFactory getConnectionFactory() {
|
||||
return this.connectionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the target Queue to send invoker requests to.
|
||||
*/
|
||||
public void setQueue(Queue queue) {
|
||||
this.queue = queue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of target queue to send invoker requests to.
|
||||
* <p>The specified name will be dynamically resolved via the
|
||||
* {@link #setDestinationResolver DestinationResolver}.
|
||||
*/
|
||||
public void setQueueName(String queueName) {
|
||||
this.queue = queueName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the DestinationResolver that is to be used to resolve Queue
|
||||
* references for this accessor.
|
||||
* <p>The default resolver is a {@code DynamicDestinationResolver}. Specify a
|
||||
* {@code JndiDestinationResolver} for resolving destination names as JNDI locations.
|
||||
* @see org.springframework.jms.support.destination.DynamicDestinationResolver
|
||||
* @see org.springframework.jms.support.destination.JndiDestinationResolver
|
||||
*/
|
||||
public void setDestinationResolver(@Nullable DestinationResolver destinationResolver) {
|
||||
this.destinationResolver =
|
||||
(destinationResolver != null ? destinationResolver : new DynamicDestinationResolver());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link RemoteInvocationFactory} to use for this accessor.
|
||||
* <p>Default is a {@link DefaultRemoteInvocationFactory}.
|
||||
* <p>A custom invocation factory can add further context information
|
||||
* to the invocation, for example user credentials.
|
||||
*/
|
||||
public void setRemoteInvocationFactory(@Nullable RemoteInvocationFactory remoteInvocationFactory) {
|
||||
this.remoteInvocationFactory =
|
||||
(remoteInvocationFactory != null ? remoteInvocationFactory : new DefaultRemoteInvocationFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the {@link MessageConverter} to use for turning
|
||||
* {@link org.springframework.remoting.support.RemoteInvocation}
|
||||
* objects into request messages, as well as response messages into
|
||||
* {@link org.springframework.remoting.support.RemoteInvocationResult} objects.
|
||||
* <p>Default is a {@link SimpleMessageConverter}, using a standard JMS
|
||||
* {@link javax.jms.ObjectMessage} for each invocation / invocation result
|
||||
* object.
|
||||
* <p>Custom implementations may generally adapt {@link java.io.Serializable}
|
||||
* objects into special kinds of messages, or might be specifically tailored for
|
||||
* translating {@code RemoteInvocation(Result)s} into specific kinds of messages.
|
||||
*/
|
||||
public void setMessageConverter(@Nullable MessageConverter messageConverter) {
|
||||
this.messageConverter = (messageConverter != null ? messageConverter : new SimpleMessageConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the timeout to use for receiving the response message for a request
|
||||
* (in milliseconds).
|
||||
* <p>The default is 0, which indicates a blocking receive without timeout.
|
||||
* @see javax.jms.MessageConsumer#receive(long)
|
||||
* @see javax.jms.MessageConsumer#receive()
|
||||
*/
|
||||
public void setReceiveTimeout(long receiveTimeout) {
|
||||
this.receiveTimeout = receiveTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the timeout to use for receiving the response message for a request
|
||||
* (in milliseconds).
|
||||
*/
|
||||
protected long getReceiveTimeout() {
|
||||
return this.receiveTimeout;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
if (getConnectionFactory() == null) {
|
||||
throw new IllegalArgumentException("Property 'connectionFactory' is required");
|
||||
}
|
||||
if (this.queue == null) {
|
||||
throw new IllegalArgumentException("'queue' or 'queueName' is required");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
|
||||
if (AopUtils.isToStringMethod(methodInvocation.getMethod())) {
|
||||
return "JMS invoker proxy for queue [" + this.queue + "]";
|
||||
}
|
||||
|
||||
RemoteInvocation invocation = createRemoteInvocation(methodInvocation);
|
||||
RemoteInvocationResult result;
|
||||
try {
|
||||
result = executeRequest(invocation);
|
||||
}
|
||||
catch (JMSException ex) {
|
||||
throw convertJmsInvokerAccessException(ex);
|
||||
}
|
||||
try {
|
||||
return recreateRemoteInvocationResult(result);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
if (result.hasInvocationTargetException()) {
|
||||
throw ex;
|
||||
}
|
||||
else {
|
||||
throw new RemoteInvocationFailureException("Invocation of method [" + methodInvocation.getMethod() +
|
||||
"] failed in JMS invoker remote service at queue [" + this.queue + "]", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code RemoteInvocation} object for the given AOP method invocation.
|
||||
* <p>The default implementation delegates to the {@link RemoteInvocationFactory}.
|
||||
* <p>Can be overridden in subclasses to provide custom {@code RemoteInvocation}
|
||||
* subclasses, containing additional invocation parameters like user credentials.
|
||||
* Note that it is preferable to use a custom {@code RemoteInvocationFactory} which
|
||||
* is a reusable strategy.
|
||||
* @param methodInvocation the current AOP method invocation
|
||||
* @return the RemoteInvocation object
|
||||
* @see RemoteInvocationFactory#createRemoteInvocation
|
||||
*/
|
||||
protected RemoteInvocation createRemoteInvocation(MethodInvocation methodInvocation) {
|
||||
return this.remoteInvocationFactory.createRemoteInvocation(methodInvocation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given remote invocation, sending an invoker request message
|
||||
* to this accessor's target queue and waiting for a corresponding response.
|
||||
* @param invocation the RemoteInvocation to execute
|
||||
* @return the RemoteInvocationResult object
|
||||
* @throws JMSException in case of JMS failure
|
||||
* @see #doExecuteRequest
|
||||
*/
|
||||
protected RemoteInvocationResult executeRequest(RemoteInvocation invocation) throws JMSException {
|
||||
Connection con = createConnection();
|
||||
Session session = null;
|
||||
try {
|
||||
session = createSession(con);
|
||||
Queue queueToUse = resolveQueue(session);
|
||||
Message requestMessage = createRequestMessage(session, invocation);
|
||||
con.start();
|
||||
Message responseMessage = doExecuteRequest(session, queueToUse, requestMessage);
|
||||
if (responseMessage != null) {
|
||||
return extractInvocationResult(responseMessage);
|
||||
}
|
||||
else {
|
||||
return onReceiveTimeout(invocation);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
JmsUtils.closeSession(session);
|
||||
ConnectionFactoryUtils.releaseConnection(con, getConnectionFactory(), true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new JMS Connection for this JMS invoker.
|
||||
*/
|
||||
protected Connection createConnection() throws JMSException {
|
||||
ConnectionFactory connectionFactory = getConnectionFactory();
|
||||
Assert.state(connectionFactory != null, "No ConnectionFactory set");
|
||||
return connectionFactory.createConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new JMS Session for this JMS invoker.
|
||||
*/
|
||||
protected Session createSession(Connection con) throws JMSException {
|
||||
return con.createSession(false, Session.AUTO_ACKNOWLEDGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve this accessor's target queue.
|
||||
* @param session the current JMS Session
|
||||
* @return the resolved target Queue
|
||||
* @throws JMSException if resolution failed
|
||||
*/
|
||||
protected Queue resolveQueue(Session session) throws JMSException {
|
||||
if (this.queue instanceof Queue) {
|
||||
return (Queue) this.queue;
|
||||
}
|
||||
else if (this.queue instanceof String) {
|
||||
return resolveQueueName(session, (String) this.queue);
|
||||
}
|
||||
else {
|
||||
throw new javax.jms.IllegalStateException(
|
||||
"Queue object [" + this.queue + "] is neither a [javax.jms.Queue] nor a queue name String");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the given queue name into a JMS {@link javax.jms.Queue},
|
||||
* via this accessor's {@link DestinationResolver}.
|
||||
* @param session the current JMS Session
|
||||
* @param queueName the name of the queue
|
||||
* @return the located Queue
|
||||
* @throws JMSException if resolution failed
|
||||
* @see #setDestinationResolver
|
||||
*/
|
||||
protected Queue resolveQueueName(Session session, String queueName) throws JMSException {
|
||||
return (Queue) this.destinationResolver.resolveDestinationName(session, queueName, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the invoker request message.
|
||||
* <p>The default implementation creates a JMS {@link javax.jms.ObjectMessage}
|
||||
* for the given RemoteInvocation object.
|
||||
* @param session the current JMS Session
|
||||
* @param invocation the remote invocation to send
|
||||
* @return the JMS Message to send
|
||||
* @throws JMSException if the message could not be created
|
||||
*/
|
||||
protected Message createRequestMessage(Session session, RemoteInvocation invocation) throws JMSException {
|
||||
return this.messageConverter.toMessage(invocation, session);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually execute the given request, sending the invoker request message
|
||||
* to the specified target queue and waiting for a corresponding response.
|
||||
* <p>The default implementation is based on standard JMS send/receive,
|
||||
* using a {@link javax.jms.TemporaryQueue} for receiving the response.
|
||||
* @param session the JMS Session to use
|
||||
* @param queue the resolved target Queue to send to
|
||||
* @param requestMessage the JMS Message to send
|
||||
* @return the RemoteInvocationResult object
|
||||
* @throws JMSException in case of JMS failure
|
||||
*/
|
||||
@Nullable
|
||||
protected Message doExecuteRequest(Session session, Queue queue, Message requestMessage) throws JMSException {
|
||||
TemporaryQueue responseQueue = null;
|
||||
MessageProducer producer = null;
|
||||
MessageConsumer consumer = null;
|
||||
try {
|
||||
responseQueue = session.createTemporaryQueue();
|
||||
producer = session.createProducer(queue);
|
||||
consumer = session.createConsumer(responseQueue);
|
||||
requestMessage.setJMSReplyTo(responseQueue);
|
||||
producer.send(requestMessage);
|
||||
long timeout = getReceiveTimeout();
|
||||
return (timeout > 0 ? consumer.receive(timeout) : consumer.receive());
|
||||
}
|
||||
finally {
|
||||
JmsUtils.closeMessageConsumer(consumer);
|
||||
JmsUtils.closeMessageProducer(producer);
|
||||
if (responseQueue != null) {
|
||||
responseQueue.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the invocation result from the response message.
|
||||
* <p>The default implementation expects a JMS {@link javax.jms.ObjectMessage}
|
||||
* carrying a {@link RemoteInvocationResult} object. If an invalid response
|
||||
* message is encountered, the {@code onInvalidResponse} callback gets invoked.
|
||||
* @param responseMessage the response message
|
||||
* @return the invocation result
|
||||
* @throws JMSException is thrown if a JMS exception occurs
|
||||
* @see #onInvalidResponse
|
||||
*/
|
||||
protected RemoteInvocationResult extractInvocationResult(Message responseMessage) throws JMSException {
|
||||
Object content = this.messageConverter.fromMessage(responseMessage);
|
||||
if (content instanceof RemoteInvocationResult) {
|
||||
return (RemoteInvocationResult) content;
|
||||
}
|
||||
return onInvalidResponse(responseMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback that is invoked by {@link #executeRequest} when the receive
|
||||
* timeout has expired for the specified {@link RemoteInvocation}.
|
||||
* <p>By default, an {@link RemoteTimeoutException} is thrown. Sub-classes
|
||||
* can choose to either throw a more dedicated exception or even return
|
||||
* a default {@link RemoteInvocationResult} as a fallback.
|
||||
* @param invocation the invocation
|
||||
* @return a default result when the receive timeout has expired
|
||||
*/
|
||||
protected RemoteInvocationResult onReceiveTimeout(RemoteInvocation invocation) {
|
||||
throw new RemoteTimeoutException("Receive timeout after " + this.receiveTimeout + " ms for " + invocation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback that is invoked by {@link #extractInvocationResult} when
|
||||
* it encounters an invalid response message.
|
||||
* <p>The default implementation throws a {@link MessageFormatException}.
|
||||
* @param responseMessage the invalid response message
|
||||
* @return an alternative invocation result that should be returned to
|
||||
* the caller (if desired)
|
||||
* @throws JMSException if the invalid response should lead to an
|
||||
* infrastructure exception propagated to the caller
|
||||
* @see #extractInvocationResult
|
||||
*/
|
||||
protected RemoteInvocationResult onInvalidResponse(Message responseMessage) throws JMSException {
|
||||
throw new MessageFormatException("Invalid response message: " + responseMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recreate the invocation result contained in the given {@link RemoteInvocationResult}
|
||||
* object.
|
||||
* <p>The default implementation calls the default {@code recreate()} method.
|
||||
* <p>Can be overridden in subclasses to provide custom recreation, potentially
|
||||
* processing the returned result object.
|
||||
* @param result the RemoteInvocationResult to recreate
|
||||
* @return a return value if the invocation result is a successful return
|
||||
* @throws Throwable if the invocation result is an exception
|
||||
* @see org.springframework.remoting.support.RemoteInvocationResult#recreate()
|
||||
*/
|
||||
@Nullable
|
||||
protected Object recreateRemoteInvocationResult(RemoteInvocationResult result) throws Throwable {
|
||||
return result.recreate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given JMS invoker access exception to an appropriate
|
||||
* Spring {@link RemoteAccessException}.
|
||||
* @param ex the exception to convert
|
||||
* @return the RemoteAccessException to throw
|
||||
*/
|
||||
protected RemoteAccessException convertJmsInvokerAccessException(JMSException ex) {
|
||||
return new RemoteAccessException("Could not access JMS invoker queue [" + this.queue + "]", ex);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.jms.remoting;
|
||||
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* FactoryBean for JMS invoker proxies. Exposes the proxied service for use
|
||||
* as a bean reference, using the specified service interface.
|
||||
*
|
||||
* <p>Serializes remote invocation objects and deserializes remote invocation
|
||||
* result objects. Uses Java serialization just like RMI, but with the JMS
|
||||
* provider as communication infrastructure.
|
||||
*
|
||||
* <p>To be configured with a {@link javax.jms.QueueConnectionFactory} and a
|
||||
* target queue (either as {@link javax.jms.Queue} reference or as queue name).
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see #setConnectionFactory
|
||||
* @see #setQueueName
|
||||
* @see #setServiceInterface
|
||||
* @see org.springframework.jms.remoting.JmsInvokerClientInterceptor
|
||||
* @see org.springframework.jms.remoting.JmsInvokerServiceExporter
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
public class JmsInvokerProxyFactoryBean extends JmsInvokerClientInterceptor
|
||||
implements FactoryBean<Object>, BeanClassLoaderAware {
|
||||
|
||||
@Nullable
|
||||
private Class<?> serviceInterface;
|
||||
|
||||
@Nullable
|
||||
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
|
||||
|
||||
@Nullable
|
||||
private Object serviceProxy;
|
||||
|
||||
|
||||
/**
|
||||
* Set the interface that the proxy must implement.
|
||||
* @param serviceInterface the interface that the proxy must implement
|
||||
* @throws IllegalArgumentException if the supplied {@code serviceInterface}
|
||||
* is not an interface type
|
||||
*/
|
||||
public void setServiceInterface(Class<?> serviceInterface) {
|
||||
Assert.notNull(serviceInterface, "'serviceInterface' must not be null");
|
||||
Assert.isTrue(serviceInterface.isInterface(), "'serviceInterface' must be an interface");
|
||||
this.serviceInterface = serviceInterface;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.beanClassLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
super.afterPropertiesSet();
|
||||
Assert.notNull(this.serviceInterface, "Property 'serviceInterface' is required");
|
||||
this.serviceProxy = new ProxyFactory(this.serviceInterface, this).getProxy(this.beanClassLoader);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object getObject() {
|
||||
return this.serviceProxy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return this.serviceInterface;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,199 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.jms.remoting;
|
||||
|
||||
import javax.jms.JMSException;
|
||||
import javax.jms.Message;
|
||||
import javax.jms.MessageFormatException;
|
||||
import javax.jms.MessageProducer;
|
||||
import javax.jms.Session;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.jms.listener.SessionAwareMessageListener;
|
||||
import org.springframework.jms.support.JmsUtils;
|
||||
import org.springframework.jms.support.converter.MessageConverter;
|
||||
import org.springframework.jms.support.converter.SimpleMessageConverter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.remoting.support.RemoteInvocation;
|
||||
import org.springframework.remoting.support.RemoteInvocationBasedExporter;
|
||||
import org.springframework.remoting.support.RemoteInvocationResult;
|
||||
|
||||
/**
|
||||
* JMS message listener that exports the specified service bean as a
|
||||
* JMS service endpoint, accessible via a JMS invoker proxy.
|
||||
*
|
||||
* <p>Note that this class implements Spring's
|
||||
* {@link org.springframework.jms.listener.SessionAwareMessageListener}
|
||||
* interface, since it requires access to the active JMS Session.
|
||||
* Hence, this class can only be used with message listener containers
|
||||
* which support the SessionAwareMessageListener interface (e.g. Spring's
|
||||
* {@link org.springframework.jms.listener.DefaultMessageListenerContainer}).
|
||||
*
|
||||
* <p>Thanks to James Strachan for the original prototype that this
|
||||
* JMS invoker mechanism was inspired by!
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author James Strachan
|
||||
* @since 2.0
|
||||
* @see JmsInvokerClientInterceptor
|
||||
* @see JmsInvokerProxyFactoryBean
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
public class JmsInvokerServiceExporter extends RemoteInvocationBasedExporter
|
||||
implements SessionAwareMessageListener<Message>, InitializingBean {
|
||||
|
||||
private MessageConverter messageConverter = new SimpleMessageConverter();
|
||||
|
||||
private boolean ignoreInvalidRequests = true;
|
||||
|
||||
@Nullable
|
||||
private Object proxy;
|
||||
|
||||
|
||||
/**
|
||||
* Specify the MessageConverter to use for turning request messages into
|
||||
* {@link org.springframework.remoting.support.RemoteInvocation} objects,
|
||||
* as well as {@link org.springframework.remoting.support.RemoteInvocationResult}
|
||||
* objects into response messages.
|
||||
* <p>Default is a {@link org.springframework.jms.support.converter.SimpleMessageConverter},
|
||||
* using a standard JMS {@link javax.jms.ObjectMessage} for each invocation /
|
||||
* invocation result object.
|
||||
* <p>Custom implementations may generally adapt Serializables into
|
||||
* special kinds of messages, or might be specifically tailored for
|
||||
* translating RemoteInvocation(Result)s into specific kinds of messages.
|
||||
*/
|
||||
public void setMessageConverter(@Nullable MessageConverter messageConverter) {
|
||||
this.messageConverter = (messageConverter != null ? messageConverter : new SimpleMessageConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether invalidly formatted messages should be discarded.
|
||||
* Default is "true".
|
||||
* <p>Switch this flag to "false" to throw an exception back to the
|
||||
* listener container. This will typically lead to redelivery of
|
||||
* the message, which is usually undesirable - since the message
|
||||
* content will be the same (that is, still invalid).
|
||||
*/
|
||||
public void setIgnoreInvalidRequests(boolean ignoreInvalidRequests) {
|
||||
this.ignoreInvalidRequests = ignoreInvalidRequests;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
this.proxy = getProxyForService();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onMessage(Message requestMessage, Session session) throws JMSException {
|
||||
RemoteInvocation invocation = readRemoteInvocation(requestMessage);
|
||||
if (invocation != null) {
|
||||
RemoteInvocationResult result = invokeAndCreateResult(invocation, this.proxy);
|
||||
writeRemoteInvocationResult(requestMessage, session, result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a RemoteInvocation from the given JMS message.
|
||||
* @param requestMessage current request message
|
||||
* @return the RemoteInvocation object (or {@code null}
|
||||
* in case of an invalid message that will simply be ignored)
|
||||
* @throws javax.jms.JMSException in case of message access failure
|
||||
*/
|
||||
@Nullable
|
||||
protected RemoteInvocation readRemoteInvocation(Message requestMessage) throws JMSException {
|
||||
Object content = this.messageConverter.fromMessage(requestMessage);
|
||||
if (content instanceof RemoteInvocation) {
|
||||
return (RemoteInvocation) content;
|
||||
}
|
||||
return onInvalidRequest(requestMessage);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send the given RemoteInvocationResult as a JMS message to the originator.
|
||||
* @param requestMessage current request message
|
||||
* @param session the JMS Session to use
|
||||
* @param result the RemoteInvocationResult object
|
||||
* @throws javax.jms.JMSException if thrown by trying to send the message
|
||||
*/
|
||||
protected void writeRemoteInvocationResult(
|
||||
Message requestMessage, Session session, RemoteInvocationResult result) throws JMSException {
|
||||
|
||||
Message response = createResponseMessage(requestMessage, session, result);
|
||||
MessageProducer producer = session.createProducer(requestMessage.getJMSReplyTo());
|
||||
try {
|
||||
producer.send(response);
|
||||
}
|
||||
finally {
|
||||
JmsUtils.closeMessageProducer(producer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the invocation result response message.
|
||||
* <p>The default implementation creates a JMS ObjectMessage for the given
|
||||
* RemoteInvocationResult object. It sets the response's correlation id
|
||||
* to the request message's correlation id, if any; otherwise to the
|
||||
* request message id.
|
||||
* @param request the original request message
|
||||
* @param session the JMS session to use
|
||||
* @param result the invocation result
|
||||
* @return the message response to send
|
||||
* @throws javax.jms.JMSException if creating the message failed
|
||||
*/
|
||||
protected Message createResponseMessage(Message request, Session session, RemoteInvocationResult result)
|
||||
throws JMSException {
|
||||
|
||||
Message response = this.messageConverter.toMessage(result, session);
|
||||
String correlation = request.getJMSCorrelationID();
|
||||
if (correlation == null) {
|
||||
correlation = request.getJMSMessageID();
|
||||
}
|
||||
response.setJMSCorrelationID(correlation);
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback that is invoked by {@link #readRemoteInvocation}
|
||||
* when it encounters an invalid request message.
|
||||
* <p>The default implementation either discards the invalid message or
|
||||
* throws a MessageFormatException - according to the "ignoreInvalidRequests"
|
||||
* flag, which is set to "true" (that is, discard invalid messages) by default.
|
||||
* @param requestMessage the invalid request message
|
||||
* @return the RemoteInvocation to expose for the invalid request (typically
|
||||
* {@code null} in case of an invalid message that will simply be ignored)
|
||||
* @throws javax.jms.JMSException in case of the invalid request supposed
|
||||
* to lead to an exception (instead of ignoring it)
|
||||
* @see #readRemoteInvocation
|
||||
* @see #setIgnoreInvalidRequests
|
||||
*/
|
||||
@Nullable
|
||||
protected RemoteInvocation onInvalidRequest(Message requestMessage) throws JMSException {
|
||||
if (this.ignoreInvalidRequests) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Invalid request message will be discarded: " + requestMessage);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
throw new MessageFormatException("Invalid request message: " + requestMessage);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
/**
|
||||
* Remoting classes for transparent Java-to-Java remoting via a JMS provider.
|
||||
*
|
||||
* <p>Allows the target service to be load-balanced across a number of queue
|
||||
* receivers, and provides a level of indirection between the client and the
|
||||
* service: They only need to agree on a queue name and a service interface.
|
||||
*/
|
||||
@NonNullApi
|
||||
@NonNullFields
|
||||
package org.springframework.jms.remoting;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
import org.springframework.lang.NonNullFields;
|
||||
@@ -1,511 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.jms.remoting;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import javax.jms.CompletionListener;
|
||||
import javax.jms.Destination;
|
||||
import javax.jms.JMSException;
|
||||
import javax.jms.Message;
|
||||
import javax.jms.MessageProducer;
|
||||
import javax.jms.ObjectMessage;
|
||||
import javax.jms.Queue;
|
||||
import javax.jms.QueueConnection;
|
||||
import javax.jms.QueueConnectionFactory;
|
||||
import javax.jms.QueueSession;
|
||||
import javax.jms.Session;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.testfixture.beans.ITestBean;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.jms.support.converter.MessageConversionException;
|
||||
import org.springframework.jms.support.converter.SimpleMessageConverter;
|
||||
import org.springframework.remoting.RemoteTimeoutException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class JmsInvokerTests {
|
||||
|
||||
private QueueConnectionFactory mockConnectionFactory = mock(QueueConnectionFactory.class);
|
||||
|
||||
private QueueConnection mockConnection = mock(QueueConnection.class);
|
||||
|
||||
private QueueSession mockSession = mock(QueueSession.class);
|
||||
|
||||
private Queue mockQueue = mock(Queue.class);
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void setUpMocks() throws Exception {
|
||||
given(mockConnectionFactory.createConnection()).willReturn(mockConnection);
|
||||
given(mockConnection.createSession(false, Session.AUTO_ACKNOWLEDGE)).willReturn(mockSession);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void jmsInvokerProxyFactoryBeanAndServiceExporter() throws Throwable {
|
||||
doTestJmsInvokerProxyFactoryBeanAndServiceExporter(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void jmsInvokerProxyFactoryBeanAndServiceExporterWithDynamicQueue() throws Throwable {
|
||||
given(mockSession.createQueue("myQueue")).willReturn(mockQueue);
|
||||
doTestJmsInvokerProxyFactoryBeanAndServiceExporter(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation")
|
||||
void receiveTimeoutExpired() {
|
||||
JmsInvokerProxyFactoryBean pfb = new JmsInvokerProxyFactoryBean() {
|
||||
@Override
|
||||
protected Message doExecuteRequest(Session session, Queue queue, Message requestMessage) throws JMSException {
|
||||
return null; // faking no message received
|
||||
}
|
||||
};
|
||||
pfb.setServiceInterface(ITestBean.class);
|
||||
pfb.setConnectionFactory(this.mockConnectionFactory);
|
||||
pfb.setQueue(this.mockQueue);
|
||||
pfb.setReceiveTimeout(1500);
|
||||
pfb.afterPropertiesSet();
|
||||
ITestBean proxy = (ITestBean) pfb.getObject();
|
||||
|
||||
assertThatExceptionOfType(RemoteTimeoutException.class).isThrownBy(() ->
|
||||
proxy.getAge())
|
||||
.withMessageContaining("1500 ms")
|
||||
.withMessageContaining("getAge");
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void doTestJmsInvokerProxyFactoryBeanAndServiceExporter(boolean dynamicQueue) throws Throwable {
|
||||
TestBean target = new TestBean("myname", 99);
|
||||
|
||||
final JmsInvokerServiceExporter exporter = new JmsInvokerServiceExporter();
|
||||
exporter.setServiceInterface(ITestBean.class);
|
||||
exporter.setService(target);
|
||||
exporter.setMessageConverter(new MockSimpleMessageConverter());
|
||||
exporter.afterPropertiesSet();
|
||||
|
||||
JmsInvokerProxyFactoryBean pfb = new JmsInvokerProxyFactoryBean() {
|
||||
@Override
|
||||
protected Message doExecuteRequest(Session session, Queue queue, Message requestMessage) throws JMSException {
|
||||
Session mockExporterSession = mock(Session.class);
|
||||
ResponseStoringProducer mockProducer = new ResponseStoringProducer();
|
||||
given(mockExporterSession.createProducer(requestMessage.getJMSReplyTo())).willReturn(mockProducer);
|
||||
exporter.onMessage(requestMessage, mockExporterSession);
|
||||
assertThat(mockProducer.closed).isTrue();
|
||||
return mockProducer.response;
|
||||
}
|
||||
};
|
||||
pfb.setServiceInterface(ITestBean.class);
|
||||
pfb.setConnectionFactory(this.mockConnectionFactory);
|
||||
if (dynamicQueue) {
|
||||
pfb.setQueueName("myQueue");
|
||||
}
|
||||
else {
|
||||
pfb.setQueue(this.mockQueue);
|
||||
}
|
||||
pfb.setMessageConverter(new MockSimpleMessageConverter());
|
||||
|
||||
pfb.afterPropertiesSet();
|
||||
ITestBean proxy = (ITestBean) pfb.getObject();
|
||||
|
||||
assertThat(proxy.getName()).isEqualTo("myname");
|
||||
assertThat(proxy.getAge()).isEqualTo(99);
|
||||
proxy.setAge(50);
|
||||
assertThat(proxy.getAge()).isEqualTo(50);
|
||||
proxy.setStringArray(new String[] {"str1", "str2"});
|
||||
assertThat(Arrays.equals(new String[] {"str1", "str2"}, proxy.getStringArray())).isTrue();
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
proxy.exceptional(new IllegalStateException()));
|
||||
assertThatExceptionOfType(IllegalAccessException.class).isThrownBy(() ->
|
||||
proxy.exceptional(new IllegalAccessException()));
|
||||
}
|
||||
|
||||
|
||||
private static class ResponseStoringProducer implements MessageProducer {
|
||||
|
||||
Message response;
|
||||
|
||||
boolean closed = false;
|
||||
|
||||
@Override
|
||||
public void setDisableMessageID(boolean b) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDisableMessageID() throws JMSException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisableMessageTimestamp(boolean b) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDisableMessageTimestamp() throws JMSException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDeliveryMode(int i) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDeliveryMode() throws JMSException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPriority(int i) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority() throws JMSException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTimeToLive(long l) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimeToLive() throws JMSException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDeliveryDelay(long deliveryDelay) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDeliveryDelay() throws JMSException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Destination getDestination() throws JMSException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws JMSException {
|
||||
this.closed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Message message) throws JMSException {
|
||||
this.response = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Message message, int i, int i1, long l) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Destination destination, Message message) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Destination destination, Message message, int i, int i1, long l) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Message message, CompletionListener completionListener) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Message message, int deliveryMode, int priority, long timeToLive, CompletionListener completionListener) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Destination destination, Message message, CompletionListener completionListener) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive, CompletionListener completionListener) throws JMSException {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class MockObjectMessage implements ObjectMessage {
|
||||
|
||||
private Serializable serializable;
|
||||
|
||||
private Destination replyTo;
|
||||
|
||||
public MockObjectMessage(Serializable serializable) {
|
||||
this.serializable = serializable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setObject(Serializable serializable) throws JMSException {
|
||||
this.serializable = serializable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable getObject() throws JMSException {
|
||||
return serializable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJMSMessageID() throws JMSException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setJMSMessageID(String string) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getJMSTimestamp() throws JMSException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setJMSTimestamp(long l) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getJMSCorrelationIDAsBytes() throws JMSException {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setJMSCorrelationIDAsBytes(byte[] bytes) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setJMSCorrelationID(String string) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJMSCorrelationID() throws JMSException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Destination getJMSReplyTo() throws JMSException {
|
||||
return replyTo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setJMSReplyTo(Destination destination) throws JMSException {
|
||||
this.replyTo = destination;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Destination getJMSDestination() throws JMSException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setJMSDestination(Destination destination) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getJMSDeliveryMode() throws JMSException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setJMSDeliveryMode(int i) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getJMSRedelivered() throws JMSException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setJMSRedelivered(boolean b) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJMSType() throws JMSException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setJMSType(String string) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getJMSExpiration() throws JMSException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setJMSExpiration(long l) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getJMSPriority() throws JMSException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setJMSPriority(int i) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getJMSDeliveryTime() throws JMSException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setJMSDeliveryTime(long deliveryTime) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getBody(Class<T> c) throws JMSException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public boolean isBodyAssignableTo(Class c) throws JMSException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearProperties() throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean propertyExists(String string) throws JMSException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBooleanProperty(String string) throws JMSException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getByteProperty(String string) throws JMSException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getShortProperty(String string) throws JMSException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntProperty(String string) throws JMSException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLongProperty(String string) throws JMSException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFloatProperty(String string) throws JMSException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDoubleProperty(String string) throws JMSException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStringProperty(String string) throws JMSException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObjectProperty(String string) throws JMSException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Enumeration getPropertyNames() throws JMSException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBooleanProperty(String string, boolean b) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setByteProperty(String string, byte b) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShortProperty(String string, short i) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIntProperty(String string, int i) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLongProperty(String string, long l) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFloatProperty(String string, float v) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDoubleProperty(String string, double v) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStringProperty(String string, String string1) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setObjectProperty(String string, Object object) throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acknowledge() throws JMSException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearBody() throws JMSException {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class MockSimpleMessageConverter extends SimpleMessageConverter {
|
||||
|
||||
@Override
|
||||
public Message toMessage(Object object, Session session) throws JMSException, MessageConversionException {
|
||||
return new MockObjectMessage((Serializable) object);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,302 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.HessianException;
|
||||
import com.caucho.hessian.client.HessianConnectionException;
|
||||
import com.caucho.hessian.client.HessianConnectionFactory;
|
||||
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.lang.Nullable;
|
||||
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://hessian.caucho.com">Hessian website</a>
|
||||
* <b>Note: As of Spring 4.0, this client requires Hessian 4.0 or above.</b>
|
||||
*
|
||||
* <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
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
public class HessianClientInterceptor extends UrlBasedRemoteAccessor implements MethodInterceptor {
|
||||
|
||||
private HessianProxyFactory proxyFactory = new HessianProxyFactory();
|
||||
|
||||
@Nullable
|
||||
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(@Nullable 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},
|
||||
* 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 to allow non-serializable types as Hessian arguments
|
||||
* and return values. Default is "true".
|
||||
*/
|
||||
public void setAllowNonSerializable(boolean allowNonSerializable) {
|
||||
this.proxyFactory.getSerializerFactory().setAllowNonSerializable(allowNonSerializable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a custom HessianConnectionFactory to use for the Hessian client.
|
||||
*/
|
||||
public void setConnectionFactory(HessianConnectionFactory connectionFactory) {
|
||||
this.proxyFactory.setConnectionFactory(connectionFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the socket connect timeout to use for the Hessian client.
|
||||
* @see com.caucho.hessian.client.HessianProxyFactory#setConnectTimeout
|
||||
*/
|
||||
public void setConnectTimeout(long timeout) {
|
||||
this.proxyFactory.setConnectTimeout(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
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(), getBeanClassLoader());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
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) {
|
||||
Throwable targetEx = ex.getTargetException();
|
||||
// Hessian 4.0 check: another layer of InvocationTargetException.
|
||||
if (targetEx instanceof InvocationTargetException) {
|
||||
targetEx = ((InvocationTargetException) targetEx).getTargetException();
|
||||
}
|
||||
if (targetEx instanceof HessianConnectionException) {
|
||||
throw convertHessianAccessException(targetEx);
|
||||
}
|
||||
else if (targetEx instanceof HessianException || targetEx instanceof HessianRuntimeException) {
|
||||
Throwable cause = targetEx.getCause();
|
||||
throw convertHessianAccessException(cause != null ? cause : targetEx);
|
||||
}
|
||||
else if (targetEx instanceof UndeclaredThrowableException) {
|
||||
UndeclaredThrowableException utex = (UndeclaredThrowableException) targetEx;
|
||||
throw convertHessianAccessException(utex.getUndeclaredThrowable());
|
||||
}
|
||||
else {
|
||||
throw targetEx;
|
||||
}
|
||||
}
|
||||
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 HessianConnectionException || 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,254 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import com.caucho.hessian.io.AbstractHessianInput;
|
||||
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.HessianInput;
|
||||
import com.caucho.hessian.io.HessianOutput;
|
||||
import com.caucho.hessian.io.HessianRemoteResolver;
|
||||
import com.caucho.hessian.io.SerializerFactory;
|
||||
import com.caucho.hessian.server.HessianSkeleton;
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.remoting.support.RemoteExporter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CommonsLogWriter;
|
||||
|
||||
/**
|
||||
* 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://hessian.caucho.com">Hessian website</a>.
|
||||
* <b>Note: As of Spring 4.0, this exporter requires Hessian 4.0 or above.</b>
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5.1
|
||||
* @see #invoke(java.io.InputStream, java.io.OutputStream)
|
||||
* @see HessianServiceExporter
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
public class HessianExporter extends RemoteExporter implements InitializingBean {
|
||||
|
||||
/**
|
||||
* The content type for hessian ({@code application/x-hessian}).
|
||||
*/
|
||||
public static final String CONTENT_TYPE_HESSIAN = "application/x-hessian";
|
||||
|
||||
|
||||
private SerializerFactory serializerFactory = new SerializerFactory();
|
||||
|
||||
@Nullable
|
||||
private HessianRemoteResolver remoteResolver;
|
||||
|
||||
@Nullable
|
||||
private Log debugLogger;
|
||||
|
||||
@Nullable
|
||||
private HessianSkeleton skeleton;
|
||||
|
||||
|
||||
/**
|
||||
* 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},
|
||||
* with custom bean property values applied.
|
||||
*/
|
||||
public void setSerializerFactory(@Nullable 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 to allow non-serializable types as Hessian arguments
|
||||
* and return values. Default is "true".
|
||||
*/
|
||||
public void setAllowNonSerializable(boolean allowNonSerializable) {
|
||||
this.serializerFactory.setAllowNonSerializable(allowNonSerializable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a custom HessianRemoteResolver to use for resolving remote
|
||||
* object references.
|
||||
*/
|
||||
public void setRemoteResolver(HessianRemoteResolver remoteResolver) {
|
||||
this.remoteResolver = remoteResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
prepare();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize this exporter.
|
||||
*/
|
||||
public void prepare() {
|
||||
checkService();
|
||||
checkServiceInterface();
|
||||
this.skeleton = new HessianSkeleton(getProxyForService(), getServiceInterface());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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, "Hessian exporter has not been initialized");
|
||||
doInvoke(this.skeleton, inputStream, outputStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually invoke the skeleton with the given streams.
|
||||
* @param skeleton the skeleton to invoke
|
||||
* @param inputStream the request stream
|
||||
* @param outputStream the response stream
|
||||
* @throws Throwable if invocation failed
|
||||
*/
|
||||
protected void doInvoke(HessianSkeleton skeleton, InputStream inputStream, OutputStream outputStream)
|
||||
throws Throwable {
|
||||
|
||||
ClassLoader originalClassLoader = overrideThreadContextClassLoader();
|
||||
try {
|
||||
InputStream isToUse = inputStream;
|
||||
OutputStream osToUse = outputStream;
|
||||
|
||||
if (this.debugLogger != null && this.debugLogger.isDebugEnabled()) {
|
||||
try (PrintWriter debugWriter = new PrintWriter(new CommonsLogWriter(this.debugLogger))){
|
||||
@SuppressWarnings("resource")
|
||||
HessianDebugInputStream dis = new HessianDebugInputStream(inputStream, debugWriter);
|
||||
@SuppressWarnings("resource")
|
||||
HessianDebugOutputStream dos = new HessianDebugOutputStream(outputStream, debugWriter);
|
||||
dis.startTop2();
|
||||
dos.startTop2();
|
||||
isToUse = dis;
|
||||
osToUse = dos;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isToUse.markSupported()) {
|
||||
isToUse = new BufferedInputStream(isToUse);
|
||||
isToUse.mark(1);
|
||||
}
|
||||
|
||||
int code = isToUse.read();
|
||||
int major;
|
||||
int minor;
|
||||
|
||||
AbstractHessianInput in;
|
||||
AbstractHessianOutput out;
|
||||
|
||||
if (code == 'H') {
|
||||
// Hessian 2.0 stream
|
||||
major = isToUse.read();
|
||||
minor = isToUse.read();
|
||||
if (major != 0x02) {
|
||||
throw new IOException("Version " + major + '.' + minor + " is not understood");
|
||||
}
|
||||
in = new Hessian2Input(isToUse);
|
||||
out = new Hessian2Output(osToUse);
|
||||
in.readCall();
|
||||
}
|
||||
else if (code == 'C') {
|
||||
// Hessian 2.0 call... for some reason not handled in HessianServlet!
|
||||
isToUse.reset();
|
||||
in = new Hessian2Input(isToUse);
|
||||
out = new Hessian2Output(osToUse);
|
||||
in.readCall();
|
||||
}
|
||||
else if (code == 'c') {
|
||||
// Hessian 1.0 call
|
||||
major = isToUse.read();
|
||||
minor = isToUse.read();
|
||||
in = new HessianInput(isToUse);
|
||||
if (major >= 2) {
|
||||
out = new Hessian2Output(osToUse);
|
||||
}
|
||||
else {
|
||||
out = new HessianOutput(osToUse);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IOException("Expected 'H'/'C' (Hessian 2.0) or 'c' (Hessian 1.0) in hessian input at " + code);
|
||||
}
|
||||
|
||||
in.setSerializerFactory(this.serializerFactory);
|
||||
out.setSerializerFactory(this.serializerFactory);
|
||||
if (this.remoteResolver != null) {
|
||||
in.setRemoteResolver(this.remoteResolver);
|
||||
}
|
||||
|
||||
try {
|
||||
skeleton.invoke(in, out);
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
in.close();
|
||||
isToUse.close();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
out.close();
|
||||
osToUse.close();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
resetThreadContextClassLoader(originalClassLoader);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* {@link 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://hessian.caucho.com">Hessian website</a>
|
||||
* <b>Note: As of Spring 4.0, this proxy factory requires Hessian 4.0 or above.</b>
|
||||
*
|
||||
* <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.httpinvoker.HttpInvokerProxyFactoryBean
|
||||
* @see org.springframework.remoting.rmi.RmiProxyFactoryBean
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
public class HessianProxyFactoryBean extends HessianClientInterceptor implements FactoryBean<Object> {
|
||||
|
||||
@Nullable
|
||||
private Object serviceProxy;
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
super.afterPropertiesSet();
|
||||
this.serviceProxy = new ProxyFactory(getServiceInterface(), this).getProxy(getBeanClassLoader());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object getObject() {
|
||||
return this.serviceProxy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return getServiceInterface();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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>Hessian is a slim, binary RPC protocol.
|
||||
* For information on Hessian, see the
|
||||
* <a href="http://hessian.caucho.com">Hessian website</a>.
|
||||
* <b>Note: As of Spring 4.0, this exporter requires Hessian 4.0 or above.</b>
|
||||
*
|
||||
* <p>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.httpinvoker.HttpInvokerServiceExporter
|
||||
* @see org.springframework.remoting.rmi.RmiServiceExporter
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
public class HessianServiceExporter extends HessianExporter implements HttpRequestHandler {
|
||||
|
||||
/**
|
||||
* Processes the incoming Hessian request and creates a Hessian response.
|
||||
*/
|
||||
@Override
|
||||
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");
|
||||
}
|
||||
|
||||
response.setContentType(CONTENT_TYPE_HESSIAN);
|
||||
try {
|
||||
invoke(request.getInputStream(), response.getOutputStream());
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new NestedServletException("Hessian skeleton invocation failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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://hessian.caucho.com">Hessian website</a>.
|
||||
* <b>Note: As of Spring 4.0, this exporter requires Hessian 4.0 or above.</b>
|
||||
*
|
||||
* <p>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
|
||||
* @deprecated as of Spring Framework 5.1, in favor of {@link HessianServiceExporter}
|
||||
*/
|
||||
@Deprecated
|
||||
@org.springframework.lang.UsesSunHttpServer
|
||||
public class SimpleHessianServiceExporter extends HessianExporter implements HttpHandler {
|
||||
|
||||
/**
|
||||
* Processes the incoming Hessian request and creates a Hessian response.
|
||||
*/
|
||||
@Override
|
||||
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);
|
||||
logger.error("Hessian skeleton invocation failed", ex);
|
||||
return;
|
||||
}
|
||||
|
||||
exchange.getResponseHeaders().set("Content-Type", CONTENT_TYPE_HESSIAN);
|
||||
exchange.sendResponseHeaders(200, output.size());
|
||||
FileCopyUtils.copy(output.toByteArray(), exchange.getResponseBody());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
/**
|
||||
* This package provides remoting classes for Caucho's Hessian protocol:
|
||||
* a proxy factory for accessing Hessian services, and an exporter for
|
||||
* making beans available to Hessian clients.
|
||||
*
|
||||
* <p>Hessian is a slim, binary RPC protocol over HTTP.
|
||||
* For information on Hessian, see the
|
||||
* <a href="http://hessian.caucho.com">Hessian website</a>
|
||||
*/
|
||||
@NonNullApi
|
||||
@NonNullFields
|
||||
package org.springframework.remoting.caucho;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
import org.springframework.lang.NonNullFields;
|
||||
@@ -1,295 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.lang.Nullable;
|
||||
import org.springframework.remoting.support.RemoteInvocation;
|
||||
import org.springframework.remoting.support.RemoteInvocationResult;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
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";
|
||||
|
||||
private static final int SERIALIZED_INVOCATION_BYTE_ARRAY_INITIAL_SIZE = 1024;
|
||||
|
||||
|
||||
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";
|
||||
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private String contentType = CONTENT_TYPE_SERIALIZED_OBJECT;
|
||||
|
||||
private boolean acceptGzipEncoding = true;
|
||||
|
||||
@Nullable
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.beanClassLoader = classLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bean ClassLoader that this executor is supposed to use.
|
||||
*/
|
||||
@Nullable
|
||||
protected ClassLoader getBeanClassLoader() {
|
||||
return this.beanClassLoader;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
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} a chance
|
||||
* to decorate the stream first (for example, for custom encryption or compression).
|
||||
* Creates an {@code ObjectOutputStream} for the final stream and calls
|
||||
* {@code doWriteRemoteInvocation} 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 {
|
||||
try (ObjectOutputStream oos = new ObjectOutputStream(decorateOutputStream(os))) {
|
||||
doWriteRemoteInvocation(invocation, oos);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
* 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}
|
||||
* 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} a chance to decorate the stream
|
||||
* first (for example, for custom encryption or compression). Creates an
|
||||
* {@code ObjectInputStream} via {@code createObjectInputStream} and
|
||||
* calls {@code doReadRemoteInvocationResult} 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, @Nullable String codebaseUrl)
|
||||
throws IOException, ClassNotFoundException {
|
||||
|
||||
try (ObjectInputStream ois = createObjectInputStream(decorateInputStream(is), codebaseUrl)) {
|
||||
return doReadRemoteInvocationResult(ois);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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})
|
||||
* @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, @Nullable String codebaseUrl) throws IOException {
|
||||
return new org.springframework.remoting.rmi.CodebaseAwareObjectInputStream(is, getBeanClassLoader(), codebaseUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the actual reading of an invocation object from the
|
||||
* given ObjectInputStream.
|
||||
* <p>The default implementation simply calls {@code readObject}.
|
||||
* 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() + "]: " + ClassUtils.getDescriptiveType(obj));
|
||||
}
|
||||
return (RemoteInvocationResult) obj;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,370 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.Locale;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.NoHttpResponseException;
|
||||
import org.apache.http.StatusLine;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.Configurable;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.config.Registry;
|
||||
import org.apache.http.config.RegistryBuilder;
|
||||
import org.apache.http.conn.socket.ConnectionSocketFactory;
|
||||
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||
import org.apache.http.entity.ByteArrayEntity;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
|
||||
import org.springframework.context.i18n.LocaleContext;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.remoting.support.RemoteInvocationResult;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.remoting.httpinvoker.HttpInvokerRequestExecutor} implementation that uses
|
||||
* <a href="https://hc.apache.org/httpcomponents-client-ga/httpclient/">Apache HttpComponents HttpClient</a>
|
||||
* to execute POST requests.
|
||||
*
|
||||
* <p>Allows to use a pre-configured {@link org.apache.http.client.HttpClient}
|
||||
* instance, potentially with authentication, HTTP connection pooling, etc.
|
||||
* Also designed for easy subclassing, providing specific template methods.
|
||||
*
|
||||
* <p>As of Spring 4.1, this request executor requires Apache HttpComponents 4.3 or higher.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Stephane Nicoll
|
||||
* @since 3.1
|
||||
* @see org.springframework.remoting.httpinvoker.SimpleHttpInvokerRequestExecutor
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
public class HttpComponentsHttpInvokerRequestExecutor extends AbstractHttpInvokerRequestExecutor {
|
||||
|
||||
private static final int DEFAULT_MAX_TOTAL_CONNECTIONS = 100;
|
||||
|
||||
private static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 5;
|
||||
|
||||
private static final int DEFAULT_READ_TIMEOUT_MILLISECONDS = (60 * 1000);
|
||||
|
||||
|
||||
private HttpClient httpClient;
|
||||
|
||||
@Nullable
|
||||
private RequestConfig requestConfig;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new instance of the HttpComponentsHttpInvokerRequestExecutor with a default
|
||||
* {@link HttpClient} that uses a default {@code org.apache.http.impl.conn.PoolingClientConnectionManager}.
|
||||
*/
|
||||
public HttpComponentsHttpInvokerRequestExecutor() {
|
||||
this(createDefaultHttpClient(), RequestConfig.custom()
|
||||
.setSocketTimeout(DEFAULT_READ_TIMEOUT_MILLISECONDS).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of the HttpComponentsClientHttpRequestFactory
|
||||
* with the given {@link HttpClient} instance.
|
||||
* @param httpClient the HttpClient instance to use for this request executor
|
||||
*/
|
||||
public HttpComponentsHttpInvokerRequestExecutor(HttpClient httpClient) {
|
||||
this(httpClient, null);
|
||||
}
|
||||
|
||||
private HttpComponentsHttpInvokerRequestExecutor(HttpClient httpClient, @Nullable RequestConfig requestConfig) {
|
||||
this.httpClient = httpClient;
|
||||
this.requestConfig = requestConfig;
|
||||
}
|
||||
|
||||
|
||||
private static HttpClient createDefaultHttpClient() {
|
||||
Registry<ConnectionSocketFactory> schemeRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
|
||||
.register("http", PlainConnectionSocketFactory.getSocketFactory())
|
||||
.register("https", SSLConnectionSocketFactory.getSocketFactory())
|
||||
.build();
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(schemeRegistry);
|
||||
connectionManager.setMaxTotal(DEFAULT_MAX_TOTAL_CONNECTIONS);
|
||||
connectionManager.setDefaultMaxPerRoute(DEFAULT_MAX_CONNECTIONS_PER_ROUTE);
|
||||
|
||||
return HttpClientBuilder.create().setConnectionManager(connectionManager).build();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the {@link HttpClient} instance to use for this request executor.
|
||||
*/
|
||||
public void setHttpClient(HttpClient httpClient) {
|
||||
this.httpClient = httpClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link HttpClient} instance that this request executor uses.
|
||||
*/
|
||||
public HttpClient getHttpClient() {
|
||||
return this.httpClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the connection timeout for the underlying HttpClient.
|
||||
* A timeout value of 0 specifies an infinite timeout.
|
||||
* <p>Additional properties can be configured by specifying a
|
||||
* {@link RequestConfig} instance on a custom {@link HttpClient}.
|
||||
* @param timeout the timeout value in milliseconds
|
||||
* @see RequestConfig#getConnectTimeout()
|
||||
*/
|
||||
public void setConnectTimeout(int timeout) {
|
||||
Assert.isTrue(timeout >= 0, "Timeout must be a non-negative value");
|
||||
this.requestConfig = cloneRequestConfig().setConnectTimeout(timeout).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the timeout in milliseconds used when requesting a connection from the connection
|
||||
* manager using the underlying HttpClient.
|
||||
* A timeout value of 0 specifies an infinite timeout.
|
||||
* <p>Additional properties can be configured by specifying a
|
||||
* {@link RequestConfig} instance on a custom {@link HttpClient}.
|
||||
* @param connectionRequestTimeout the timeout value to request a connection in milliseconds
|
||||
* @see RequestConfig#getConnectionRequestTimeout()
|
||||
*/
|
||||
public void setConnectionRequestTimeout(int connectionRequestTimeout) {
|
||||
this.requestConfig = cloneRequestConfig().setConnectionRequestTimeout(connectionRequestTimeout).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the socket read timeout for the underlying HttpClient.
|
||||
* A timeout value of 0 specifies an infinite timeout.
|
||||
* <p>Additional properties can be configured by specifying a
|
||||
* {@link RequestConfig} instance on a custom {@link HttpClient}.
|
||||
* @param timeout the timeout value in milliseconds
|
||||
* @see #DEFAULT_READ_TIMEOUT_MILLISECONDS
|
||||
* @see RequestConfig#getSocketTimeout()
|
||||
*/
|
||||
public void setReadTimeout(int timeout) {
|
||||
Assert.isTrue(timeout >= 0, "Timeout must be a non-negative value");
|
||||
this.requestConfig = cloneRequestConfig().setSocketTimeout(timeout).build();
|
||||
}
|
||||
|
||||
private RequestConfig.Builder cloneRequestConfig() {
|
||||
return (this.requestConfig != null ? RequestConfig.copy(this.requestConfig) : RequestConfig.custom());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execute the given request through the HttpClient.
|
||||
* <p>This method implements the basic processing workflow:
|
||||
* The actual work happens in this class's template methods.
|
||||
* @see #createHttpPost
|
||||
* @see #setRequestBody
|
||||
* @see #executeHttpPost
|
||||
* @see #validateResponse
|
||||
* @see #getResponseBody
|
||||
*/
|
||||
@Override
|
||||
protected RemoteInvocationResult doExecuteRequest(
|
||||
HttpInvokerClientConfiguration config, ByteArrayOutputStream baos)
|
||||
throws IOException, ClassNotFoundException {
|
||||
|
||||
HttpPost postMethod = createHttpPost(config);
|
||||
setRequestBody(config, postMethod, baos);
|
||||
try {
|
||||
HttpResponse response = executeHttpPost(config, getHttpClient(), postMethod);
|
||||
validateResponse(config, response);
|
||||
InputStream responseBody = getResponseBody(config, response);
|
||||
return readRemoteInvocationResult(responseBody, config.getCodebaseUrl());
|
||||
}
|
||||
finally {
|
||||
postMethod.releaseConnection();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an HttpPost for the given configuration.
|
||||
* <p>The default implementation creates a standard HttpPost with
|
||||
* "application/x-java-serialized-object" as "Content-Type" header.
|
||||
* @param config the HTTP invoker configuration that specifies the
|
||||
* target service
|
||||
* @return the HttpPost instance
|
||||
* @throws java.io.IOException if thrown by I/O methods
|
||||
*/
|
||||
protected HttpPost createHttpPost(HttpInvokerClientConfiguration config) throws IOException {
|
||||
HttpPost httpPost = new HttpPost(config.getServiceUrl());
|
||||
|
||||
RequestConfig requestConfig = createRequestConfig(config);
|
||||
if (requestConfig != null) {
|
||||
httpPost.setConfig(requestConfig);
|
||||
}
|
||||
|
||||
LocaleContext localeContext = LocaleContextHolder.getLocaleContext();
|
||||
if (localeContext != null) {
|
||||
Locale locale = localeContext.getLocale();
|
||||
if (locale != null) {
|
||||
httpPost.addHeader(HTTP_HEADER_ACCEPT_LANGUAGE, locale.toLanguageTag());
|
||||
}
|
||||
}
|
||||
|
||||
if (isAcceptGzipEncoding()) {
|
||||
httpPost.addHeader(HTTP_HEADER_ACCEPT_ENCODING, ENCODING_GZIP);
|
||||
}
|
||||
|
||||
return httpPost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link RequestConfig} for the given configuration. Can return {@code null}
|
||||
* to indicate that no custom request config should be set and the defaults of the
|
||||
* {@link HttpClient} should be used.
|
||||
* <p>The default implementation tries to merge the defaults of the client with the
|
||||
* local customizations of the instance, if any.
|
||||
* @param config the HTTP invoker configuration that specifies the
|
||||
* target service
|
||||
* @return the RequestConfig to use
|
||||
*/
|
||||
@Nullable
|
||||
protected RequestConfig createRequestConfig(HttpInvokerClientConfiguration config) {
|
||||
HttpClient client = getHttpClient();
|
||||
if (client instanceof Configurable) {
|
||||
RequestConfig clientRequestConfig = ((Configurable) client).getConfig();
|
||||
return mergeRequestConfig(clientRequestConfig);
|
||||
}
|
||||
return this.requestConfig;
|
||||
}
|
||||
|
||||
private RequestConfig mergeRequestConfig(RequestConfig defaultRequestConfig) {
|
||||
if (this.requestConfig == null) { // nothing to merge
|
||||
return defaultRequestConfig;
|
||||
}
|
||||
|
||||
RequestConfig.Builder builder = RequestConfig.copy(defaultRequestConfig);
|
||||
int connectTimeout = this.requestConfig.getConnectTimeout();
|
||||
if (connectTimeout >= 0) {
|
||||
builder.setConnectTimeout(connectTimeout);
|
||||
}
|
||||
int connectionRequestTimeout = this.requestConfig.getConnectionRequestTimeout();
|
||||
if (connectionRequestTimeout >= 0) {
|
||||
builder.setConnectionRequestTimeout(connectionRequestTimeout);
|
||||
}
|
||||
int socketTimeout = this.requestConfig.getSocketTimeout();
|
||||
if (socketTimeout >= 0) {
|
||||
builder.setSocketTimeout(socketTimeout);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the given serialized remote invocation as request body.
|
||||
* <p>The default implementation simply sets the serialized invocation as the
|
||||
* HttpPost's request body. This can be overridden, for example, to write a
|
||||
* specific encoding and to potentially set appropriate HTTP request headers.
|
||||
* @param config the HTTP invoker configuration that specifies the target service
|
||||
* @param httpPost the HttpPost to set the request body on
|
||||
* @param baos the ByteArrayOutputStream that contains the serialized
|
||||
* RemoteInvocation object
|
||||
* @throws java.io.IOException if thrown by I/O methods
|
||||
*/
|
||||
protected void setRequestBody(
|
||||
HttpInvokerClientConfiguration config, HttpPost httpPost, ByteArrayOutputStream baos)
|
||||
throws IOException {
|
||||
|
||||
ByteArrayEntity entity = new ByteArrayEntity(baos.toByteArray());
|
||||
entity.setContentType(getContentType());
|
||||
httpPost.setEntity(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given HttpPost instance.
|
||||
* @param config the HTTP invoker configuration that specifies the target service
|
||||
* @param httpClient the HttpClient to execute on
|
||||
* @param httpPost the HttpPost to execute
|
||||
* @return the resulting HttpResponse
|
||||
* @throws java.io.IOException if thrown by I/O methods
|
||||
*/
|
||||
protected HttpResponse executeHttpPost(
|
||||
HttpInvokerClientConfiguration config, HttpClient httpClient, HttpPost httpPost)
|
||||
throws IOException {
|
||||
|
||||
return httpClient.execute(httpPost);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the given response as contained in the HttpPost 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 response the resulting HttpResponse to validate
|
||||
* @throws java.io.IOException if validation failed
|
||||
*/
|
||||
protected void validateResponse(HttpInvokerClientConfiguration config, HttpResponse response)
|
||||
throws IOException {
|
||||
|
||||
StatusLine status = response.getStatusLine();
|
||||
if (status.getStatusCode() >= 300) {
|
||||
throw new NoHttpResponseException(
|
||||
"Did not receive successful HTTP response: status code = " + status.getStatusCode() +
|
||||
", status message = [" + status.getReasonPhrase() + "]");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the response body from the given executed remote invocation request.
|
||||
* <p>The default implementation simply fetches the HttpPost'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 httpResponse the resulting HttpResponse to read the response body from
|
||||
* @return an InputStream for the response body
|
||||
* @throws java.io.IOException if thrown by I/O methods
|
||||
* @see #isGzipResponse
|
||||
* @see java.util.zip.GZIPInputStream
|
||||
*/
|
||||
protected InputStream getResponseBody(HttpInvokerClientConfiguration config, HttpResponse httpResponse)
|
||||
throws IOException {
|
||||
|
||||
if (isGzipResponse(httpResponse)) {
|
||||
return new GZIPInputStream(httpResponse.getEntity().getContent());
|
||||
}
|
||||
else {
|
||||
return httpResponse.getEntity().getContent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given response indicates a GZIP response.
|
||||
* <p>The default implementation checks whether the HTTP "Content-Encoding"
|
||||
* header contains "gzip" (in any casing).
|
||||
* @param httpResponse the resulting HttpResponse to check
|
||||
* @return whether the given response indicates a GZIP response
|
||||
*/
|
||||
protected boolean isGzipResponse(HttpResponse httpResponse) {
|
||||
Header encodingHeader = httpResponse.getFirstHeader(HTTP_HEADER_CONTENT_ENCODING);
|
||||
return (encodingHeader != null && encodingHeader.getValue() != null &&
|
||||
encodingHeader.getValue().toLowerCase().contains(ENCODING_GZIP));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Configuration interface for executing HTTP invoker requests.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
* @see HttpInvokerRequestExecutor
|
||||
* @see HttpInvokerClientInterceptor
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
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} if none
|
||||
* @see java.rmi.server.RMIClassLoader
|
||||
*/
|
||||
@Nullable
|
||||
String getCodebaseUrl();
|
||||
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.lang.Nullable;
|
||||
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 protocol.
|
||||
*
|
||||
* <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.)
|
||||
*
|
||||
* <p><b>WARNING: Be aware of vulnerabilities due to unsafe Java deserialization:
|
||||
* Manipulated input streams could lead to unwanted code execution on the server
|
||||
* during the deserialization step. As a consequence, do not expose HTTP invoker
|
||||
* endpoints to untrusted clients but rather just between your own services.</b>
|
||||
* In general, we strongly recommend any other message format (e.g. JSON) instead.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
* @see #setServiceUrl
|
||||
* @see #setCodebaseUrl
|
||||
* @see #setRemoteInvocationFactory
|
||||
* @see #setHttpInvokerRequestExecutor
|
||||
* @see HttpInvokerServiceExporter
|
||||
* @see HttpInvokerProxyFactoryBean
|
||||
* @see java.rmi.server.RMIClassLoader
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
public class HttpInvokerClientInterceptor extends RemoteInvocationBasedAccessor
|
||||
implements MethodInterceptor, HttpInvokerClientConfiguration {
|
||||
|
||||
@Nullable
|
||||
private String codebaseUrl;
|
||||
|
||||
@Nullable
|
||||
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(@Nullable String codebaseUrl) {
|
||||
this.codebaseUrl = codebaseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the codebase URL to download classes from if not found locally.
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
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 HttpComponentsHttpInvokerRequestExecutor} for more
|
||||
* sophisticated needs.
|
||||
* @see SimpleHttpInvokerRequestExecutor
|
||||
* @see HttpComponentsHttpInvokerRequestExecutor
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
super.afterPropertiesSet();
|
||||
|
||||
// Eagerly initialize the default HttpInvokerRequestExecutor, if needed.
|
||||
getHttpInvokerRequestExecutor();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
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;
|
||||
|
||||
try {
|
||||
result = executeRequest(invocation, methodInvocation);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
RemoteAccessException rae = convertHttpInvokerAccessException(ex);
|
||||
throw (rae != null ? rae : 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 {@link 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 {@link 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 {@link RemoteAccessException}.
|
||||
* @param ex the exception to convert
|
||||
* @return the RemoteAccessException to throw, or {@code null} to have the
|
||||
* original exception propagated to the caller
|
||||
*/
|
||||
@Nullable
|
||||
protected RemoteAccessException convertHttpInvokerAccessException(Throwable ex) {
|
||||
if (ex instanceof ConnectException) {
|
||||
return new RemoteConnectFailureException(
|
||||
"Could not connect to HTTP invoker remote service at [" + getServiceUrl() + "]", ex);
|
||||
}
|
||||
|
||||
if (ex instanceof ClassNotFoundException || ex instanceof NoClassDefFoundError ||
|
||||
ex instanceof InvalidClassException) {
|
||||
return new RemoteAccessException(
|
||||
"Could not deserialize result from HTTP invoker remote service [" + getServiceUrl() + "]", ex);
|
||||
}
|
||||
|
||||
if (ex instanceof Exception) {
|
||||
return new RemoteAccessException(
|
||||
"Could not access HTTP invoker remote service at [" + getServiceUrl() + "]", ex);
|
||||
}
|
||||
|
||||
// For any other Throwable, e.g. OutOfMemoryError: let it get propagated as-is.
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link 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 protocol.
|
||||
*
|
||||
* <p><b>HTTP invoker is the recommended protocol for Java-to-Java remoting.</b>
|
||||
* It is more powerful and more extensible than Hessian, at the expense of
|
||||
* being tied to Java. Nevertheless, it is as easy to set up as Hessian,
|
||||
* which is its main advantage compared to RMI.
|
||||
*
|
||||
* <p><b>WARNING: Be aware of vulnerabilities due to unsafe Java deserialization:
|
||||
* Manipulated input streams could lead to unwanted code execution on the server
|
||||
* during the deserialization step. As a consequence, do not expose HTTP invoker
|
||||
* endpoints to untrusted clients but rather just between your own services.</b>
|
||||
* In general, we strongly recommend any other message format (e.g. JSON) instead.
|
||||
*
|
||||
* @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
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
public class HttpInvokerProxyFactoryBean extends HttpInvokerClientInterceptor implements FactoryBean<Object> {
|
||||
|
||||
@Nullable
|
||||
private Object serviceProxy;
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
super.afterPropertiesSet();
|
||||
Class<?> ifc = getServiceInterface();
|
||||
Assert.notNull(ifc, "Property 'serviceInterface' is required");
|
||||
this.serviceProxy = new ProxyFactory(ifc, this).getProxy(getBeanClassLoader());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object getObject() {
|
||||
return this.serviceProxy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return getServiceInterface();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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>{@code SimpleHttpInvokerRequestExecutor}:</b>
|
||||
* Uses JDK facilities to execute POST requests, without support
|
||||
* for HTTP authentication or advanced configuration options.
|
||||
* <li><b>{@code HttpComponentsHttpInvokerRequestExecutor}:</b>
|
||||
* Uses Apache'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
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
@FunctionalInterface
|
||||
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;
|
||||
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.FilterOutputStream;
|
||||
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.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>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 protocol.
|
||||
*
|
||||
* <p><b>HTTP invoker is the recommended protocol for Java-to-Java remoting.</b>
|
||||
* It is more powerful and more extensible than Hessian, at the expense of
|
||||
* being tied to Java. Nevertheless, it is as easy to set up as Hessian,
|
||||
* which is its main advantage compared to RMI.
|
||||
*
|
||||
* <p><b>WARNING: Be aware of vulnerabilities due to unsafe Java deserialization:
|
||||
* Manipulated input streams could lead to unwanted code execution on the server
|
||||
* during the deserialization step. As a consequence, do not expose HTTP invoker
|
||||
* endpoints to untrusted clients but rather just between your own services.</b>
|
||||
* In general, we strongly recommend any other message format (e.g. JSON) instead.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
* @see HttpInvokerClientInterceptor
|
||||
* @see HttpInvokerProxyFactoryBean
|
||||
* @see org.springframework.remoting.rmi.RmiServiceExporter
|
||||
* @see org.springframework.remoting.caucho.HessianServiceExporter
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
public class HttpInvokerServiceExporter extends org.springframework.remoting.rmi.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)
|
||||
*/
|
||||
@Override
|
||||
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(HttpServletRequest, InputStream)} with
|
||||
* the {@link HttpServletRequest#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 {
|
||||
|
||||
try (ObjectInputStream ois = createObjectInputStream(decorateInputStream(request, 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 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 {
|
||||
|
||||
try (ObjectOutputStream oos =
|
||||
createObjectOutputStream(new FlushGuardedOutputStream(decorateOutputStream(request, response, os)))) {
|
||||
doWriteRemoteInvocationResult(result, oos);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decorate an {@code OutputStream} to guard against {@code flush()} calls,
|
||||
* which are turned into no-ops.
|
||||
* <p>Because {@link ObjectOutputStream#close()} will in fact flush/drain
|
||||
* the underlying stream twice, this {@link FilterOutputStream} will
|
||||
* guard against individual flush calls. Multiple flush calls can lead
|
||||
* to performance issues, since writes aren't gathered as they should be.
|
||||
* @see <a href="https://jira.spring.io/browse/SPR-14040">SPR-14040</a>
|
||||
*/
|
||||
private static class FlushGuardedOutputStream extends FilterOutputStream {
|
||||
|
||||
public FlushGuardedOutputStream(OutputStream out) {
|
||||
super(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
// Do nothing on flush
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,233 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.Locale;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import org.springframework.context.i18n.LocaleContext;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.remoting.support.RemoteInvocationResult;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.remoting.httpinvoker.HttpInvokerRequestExecutor} implementation
|
||||
* that uses standard Java 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 {@code HttpComponentsHttpInvokerRequestExecutor} for more sophisticated needs:
|
||||
* The standard {@link HttpURLConnection} class is rather limited in its capabilities.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
* @see java.net.HttpURLConnection
|
||||
* @deprecated as of 5.3 (phasing out serialization-based remoting)
|
||||
*/
|
||||
@Deprecated
|
||||
public class SimpleHttpInvokerRequestExecutor extends AbstractHttpInvokerRequestExecutor {
|
||||
|
||||
private int connectTimeout = -1;
|
||||
|
||||
private int readTimeout = -1;
|
||||
|
||||
|
||||
/**
|
||||
* Set the underlying URLConnection's connect timeout (in milliseconds).
|
||||
* A timeout value of 0 specifies an infinite timeout.
|
||||
* <p>Default is the system's default timeout.
|
||||
* @see URLConnection#setConnectTimeout(int)
|
||||
*/
|
||||
public void setConnectTimeout(int connectTimeout) {
|
||||
this.connectTimeout = connectTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the underlying URLConnection's read timeout (in milliseconds).
|
||||
* A timeout value of 0 specifies an infinite timeout.
|
||||
* <p>Default is the system's default timeout.
|
||||
* @see URLConnection#setReadTimeout(int)
|
||||
*/
|
||||
public void setReadTimeout(int readTimeout) {
|
||||
this.readTimeout = readTimeout;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execute the given request through a standard {@link 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
|
||||
*/
|
||||
@Override
|
||||
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 {@link 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() + "] does not resolve to an HTTP connection");
|
||||
}
|
||||
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 connection 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 connection, int contentLength) throws IOException {
|
||||
if (this.connectTimeout >= 0) {
|
||||
connection.setConnectTimeout(this.connectTimeout);
|
||||
}
|
||||
if (this.readTimeout >= 0) {
|
||||
connection.setReadTimeout(this.readTimeout);
|
||||
}
|
||||
|
||||
connection.setDoOutput(true);
|
||||
connection.setRequestMethod(HTTP_METHOD_POST);
|
||||
connection.setRequestProperty(HTTP_HEADER_CONTENT_TYPE, getContentType());
|
||||
connection.setRequestProperty(HTTP_HEADER_CONTENT_LENGTH, Integer.toString(contentLength));
|
||||
|
||||
LocaleContext localeContext = LocaleContextHolder.getLocaleContext();
|
||||
if (localeContext != null) {
|
||||
Locale locale = localeContext.getLocale();
|
||||
if (locale != null) {
|
||||
connection.setRequestProperty(HTTP_HEADER_ACCEPT_LANGUAGE, locale.toLanguageTag());
|
||||
}
|
||||
}
|
||||
|
||||
if (isAcceptGzipEncoding()) {
|
||||
connection.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 {@link 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().contains(ENCODING_GZIP));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.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 protocol.
|
||||
*
|
||||
* <p><b>HTTP invoker is the recommended protocol for Java-to-Java remoting.</b>
|
||||
* It is more powerful and more extensible than Hessian, at the expense of
|
||||
* being tied to Java. Nevertheless, it is as easy to set up as Hessian,
|
||||
* which is its main advantage compared to RMI.
|
||||
*
|
||||
* <p><b>WARNING: Be aware of vulnerabilities due to unsafe Java deserialization:
|
||||
* Manipulated input streams could lead to unwanted code execution on the server
|
||||
* during the deserialization step. As a consequence, do not expose HTTP invoker
|
||||
* endpoints to untrusted clients but rather just between your own services.</b>
|
||||
* In general, we strongly recommend any other message format (e.g. JSON) instead.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5.1
|
||||
* @see org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor
|
||||
* @see org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean
|
||||
* @deprecated as of Spring Framework 5.1, in favor of {@link HttpInvokerServiceExporter}
|
||||
*/
|
||||
@Deprecated
|
||||
@org.springframework.lang.UsesSunHttpServer
|
||||
public class SimpleHttpInvokerServiceExporter extends org.springframework.remoting.rmi.RemoteInvocationSerializingExporter implements HttpHandler {
|
||||
|
||||
/**
|
||||
* Reads a remote invocation from the request, executes it,
|
||||
* and writes the remote invocation result to the response.
|
||||
* @see #readRemoteInvocation(HttpExchange)
|
||||
* @see #invokeAndCreateResult(RemoteInvocation, Object)
|
||||
* @see #writeRemoteInvocationResult(HttpExchange, RemoteInvocationResult)
|
||||
*/
|
||||
@Override
|
||||
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) {
|
||||
exchange.sendResponseHeaders(500, -1);
|
||||
logger.error("Class not found during deserialization", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a RemoteInvocation from the given HTTP request.
|
||||
* <p>Delegates to {@link #readRemoteInvocation(HttpExchange, InputStream)}
|
||||
* with the {@link 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
/**
|
||||
* 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 protocol.
|
||||
*
|
||||
* <p><b>HTTP invoker is the recommended protocol for Java-to-Java remoting.</b>
|
||||
* It is more powerful and more extensible than Hessian, at the expense of
|
||||
* being tied to Java. Nevertheless, it is as easy to set up as Hessian,
|
||||
* which is its main advantage compared to RMI.
|
||||
*/
|
||||
@NonNullApi
|
||||
@NonNullFields
|
||||
package org.springframework.remoting.httpinvoker;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
import org.springframework.lang.NonNullFields;
|
||||
@@ -1,214 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.Collections;
|
||||
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 javax.xml.ws.WebServiceFeature;
|
||||
import javax.xml.ws.WebServiceProvider;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.CannotLoadBeanClassException;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Abstract exporter for JAX-WS services, autodetecting annotated service beans
|
||||
* (through the JAX-WS {@link javax.jws.WebService} annotation).
|
||||
*
|
||||
* <p>Subclasses need to implement the {@link #publishEndpoint} template methods
|
||||
* for actual endpoint exposure.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5.5
|
||||
* @see javax.jws.WebService
|
||||
* @see javax.xml.ws.Endpoint
|
||||
* @see SimpleJaxWsServiceExporter
|
||||
*/
|
||||
public abstract class AbstractJaxWsServiceExporter implements BeanFactoryAware, InitializingBean, DisposableBean {
|
||||
|
||||
@Nullable
|
||||
private Map<String, Object> endpointProperties;
|
||||
|
||||
@Nullable
|
||||
private Executor executor;
|
||||
|
||||
@Nullable
|
||||
private String bindingType;
|
||||
|
||||
@Nullable
|
||||
private WebServiceFeature[] endpointFeatures;
|
||||
|
||||
@Nullable
|
||||
private ListableBeanFactory beanFactory;
|
||||
|
||||
private final Set<Endpoint> publishedEndpoints = new LinkedHashSet<>();
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the binding type to use, overriding the value of
|
||||
* the JAX-WS {@link javax.xml.ws.BindingType} annotation.
|
||||
*/
|
||||
public void setBindingType(String bindingType) {
|
||||
this.bindingType = bindingType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify WebServiceFeature objects (e.g. as inner bean definitions)
|
||||
* to apply to JAX-WS endpoint creation.
|
||||
* @since 4.0
|
||||
*/
|
||||
public void setEndpointFeatures(WebServiceFeature... endpointFeatures) {
|
||||
this.endpointFeatures = endpointFeatures;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains all web service beans and publishes them as JAX-WS endpoints.
|
||||
*/
|
||||
@Override
|
||||
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()
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
publishEndpoints();
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish all {@link javax.jws.WebService} annotated beans in the
|
||||
* containing BeanFactory.
|
||||
* @see #publishEndpoint
|
||||
*/
|
||||
public void publishEndpoints() {
|
||||
Assert.state(this.beanFactory != null, "No BeanFactory set");
|
||||
|
||||
Set<String> beanNames = new LinkedHashSet<>(this.beanFactory.getBeanDefinitionCount());
|
||||
Collections.addAll(beanNames, this.beanFactory.getBeanDefinitionNames());
|
||||
if (this.beanFactory instanceof ConfigurableBeanFactory) {
|
||||
Collections.addAll(beanNames, ((ConfigurableBeanFactory) this.beanFactory).getSingletonNames());
|
||||
}
|
||||
|
||||
for (String beanName : beanNames) {
|
||||
try {
|
||||
Class<?> type = this.beanFactory.getType(beanName);
|
||||
if (type != null && !type.isInterface()) {
|
||||
WebService wsAnnotation = type.getAnnotation(WebService.class);
|
||||
WebServiceProvider wsProviderAnnotation = type.getAnnotation(WebServiceProvider.class);
|
||||
if (wsAnnotation != null || wsProviderAnnotation != null) {
|
||||
Endpoint endpoint = createEndpoint(this.beanFactory.getBean(beanName));
|
||||
if (this.endpointProperties != null) {
|
||||
endpoint.setProperties(this.endpointProperties);
|
||||
}
|
||||
if (this.executor != null) {
|
||||
endpoint.setExecutor(this.executor);
|
||||
}
|
||||
if (wsAnnotation != null) {
|
||||
publishEndpoint(endpoint, wsAnnotation);
|
||||
}
|
||||
else {
|
||||
publishEndpoint(endpoint, wsProviderAnnotation);
|
||||
}
|
||||
this.publishedEndpoints.add(endpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (CannotLoadBeanClassException ex) {
|
||||
// ignore beans where the class is not resolvable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the actual Endpoint instance.
|
||||
* @param bean the service object to wrap
|
||||
* @return the Endpoint instance
|
||||
* @see Endpoint#create(Object)
|
||||
* @see Endpoint#create(String, Object)
|
||||
*/
|
||||
protected Endpoint createEndpoint(Object bean) {
|
||||
return (this.endpointFeatures != null ?
|
||||
Endpoint.create(this.bindingType, bean, this.endpointFeatures) :
|
||||
Endpoint.create(this.bindingType, bean));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Actually publish the given provider endpoint. To be implemented by subclasses.
|
||||
* @param endpoint the JAX-WS Provider Endpoint object
|
||||
* @param annotation the service bean's WebServiceProvider annotation
|
||||
*/
|
||||
protected abstract void publishEndpoint(Endpoint endpoint, WebServiceProvider annotation);
|
||||
|
||||
|
||||
/**
|
||||
* Stops all published endpoints, taking the web services offline.
|
||||
*/
|
||||
@Override
|
||||
public void destroy() {
|
||||
for (Endpoint endpoint : this.publishedEndpoints) {
|
||||
endpoint.stop();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,561 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.remoting.jaxws;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.jws.WebService;
|
||||
import javax.xml.namespace.QName;
|
||||
import javax.xml.ws.BindingProvider;
|
||||
import javax.xml.ws.ProtocolException;
|
||||
import javax.xml.ws.Service;
|
||||
import javax.xml.ws.WebServiceException;
|
||||
import javax.xml.ws.WebServiceFeature;
|
||||
import javax.xml.ws.soap.SOAPFaultException;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.remoting.RemoteAccessException;
|
||||
import org.springframework.remoting.RemoteConnectFailureException;
|
||||
import org.springframework.remoting.RemoteLookupFailureException;
|
||||
import org.springframework.remoting.RemoteProxyFailureException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link org.aopalliance.intercept.MethodInterceptor} for accessing a
|
||||
* specific port of a JAX-WS service.
|
||||
*
|
||||
* <p>Uses either {@link LocalJaxWsServiceFactory}'s facilities underneath,
|
||||
* or takes an explicit reference to an existing JAX-WS Service instance
|
||||
* (e.g. obtained via {@link org.springframework.jndi.JndiObjectFactoryBean}).
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see #setPortName
|
||||
* @see #setServiceInterface
|
||||
* @see javax.xml.ws.Service#getPort
|
||||
* @see org.springframework.remoting.RemoteAccessException
|
||||
* @see org.springframework.jndi.JndiObjectFactoryBean
|
||||
*/
|
||||
public class JaxWsPortClientInterceptor extends LocalJaxWsServiceFactory
|
||||
implements MethodInterceptor, BeanClassLoaderAware, InitializingBean {
|
||||
|
||||
@Nullable
|
||||
private Service jaxWsService;
|
||||
|
||||
@Nullable
|
||||
private String portName;
|
||||
|
||||
@Nullable
|
||||
private String username;
|
||||
|
||||
@Nullable
|
||||
private String password;
|
||||
|
||||
@Nullable
|
||||
private String endpointAddress;
|
||||
|
||||
private boolean maintainSession;
|
||||
|
||||
private boolean useSoapAction;
|
||||
|
||||
@Nullable
|
||||
private String soapActionUri;
|
||||
|
||||
@Nullable
|
||||
private Map<String, Object> customProperties;
|
||||
|
||||
@Nullable
|
||||
private WebServiceFeature[] portFeatures;
|
||||
|
||||
@Nullable
|
||||
private Class<?> serviceInterface;
|
||||
|
||||
private boolean lookupServiceOnStartup = true;
|
||||
|
||||
@Nullable
|
||||
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
|
||||
|
||||
@Nullable
|
||||
private QName portQName;
|
||||
|
||||
@Nullable
|
||||
private Object portStub;
|
||||
|
||||
private final Object preparationMonitor = new Object();
|
||||
|
||||
|
||||
/**
|
||||
* Set a reference to an existing JAX-WS Service instance,
|
||||
* for example obtained via {@link org.springframework.jndi.JndiObjectFactoryBean}.
|
||||
* If not set, {@link LocalJaxWsServiceFactory}'s properties have to be specified.
|
||||
* @see #setWsdlDocumentUrl
|
||||
* @see #setNamespaceUri
|
||||
* @see #setServiceName
|
||||
* @see org.springframework.jndi.JndiObjectFactoryBean
|
||||
*/
|
||||
public void setJaxWsService(@Nullable Service jaxWsService) {
|
||||
this.jaxWsService = jaxWsService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a reference to an existing JAX-WS Service instance, if any.
|
||||
*/
|
||||
@Nullable
|
||||
public Service getJaxWsService() {
|
||||
return this.jaxWsService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the port.
|
||||
* Corresponds to the "wsdl:port" name.
|
||||
*/
|
||||
public void setPortName(@Nullable String portName) {
|
||||
this.portName = portName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the port.
|
||||
*/
|
||||
@Nullable
|
||||
public String getPortName() {
|
||||
return this.portName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the username to specify on the stub.
|
||||
* @see javax.xml.ws.BindingProvider#USERNAME_PROPERTY
|
||||
*/
|
||||
public void setUsername(@Nullable String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the username to specify on the stub.
|
||||
*/
|
||||
@Nullable
|
||||
public String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the password to specify on the stub.
|
||||
* @see javax.xml.ws.BindingProvider#PASSWORD_PROPERTY
|
||||
*/
|
||||
public void setPassword(@Nullable String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the password to specify on the stub.
|
||||
*/
|
||||
@Nullable
|
||||
public String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the endpoint address to specify on the stub.
|
||||
* @see javax.xml.ws.BindingProvider#ENDPOINT_ADDRESS_PROPERTY
|
||||
*/
|
||||
public void setEndpointAddress(@Nullable String endpointAddress) {
|
||||
this.endpointAddress = endpointAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the endpoint address to specify on the stub.
|
||||
*/
|
||||
@Nullable
|
||||
public String getEndpointAddress() {
|
||||
return this.endpointAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the "session.maintain" flag to specify on the stub.
|
||||
* @see javax.xml.ws.BindingProvider#SESSION_MAINTAIN_PROPERTY
|
||||
*/
|
||||
public void setMaintainSession(boolean maintainSession) {
|
||||
this.maintainSession = maintainSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the "session.maintain" flag to specify on the stub.
|
||||
*/
|
||||
public boolean isMaintainSession() {
|
||||
return this.maintainSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the "soapaction.use" flag to specify on the stub.
|
||||
* @see javax.xml.ws.BindingProvider#SOAPACTION_USE_PROPERTY
|
||||
*/
|
||||
public void setUseSoapAction(boolean useSoapAction) {
|
||||
this.useSoapAction = useSoapAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the "soapaction.use" flag to specify on the stub.
|
||||
*/
|
||||
public boolean isUseSoapAction() {
|
||||
return this.useSoapAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the SOAP action URI to specify on the stub.
|
||||
* @see javax.xml.ws.BindingProvider#SOAPACTION_URI_PROPERTY
|
||||
*/
|
||||
public void setSoapActionUri(@Nullable String soapActionUri) {
|
||||
this.soapActionUri = soapActionUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SOAP action URI to specify on the stub.
|
||||
*/
|
||||
@Nullable
|
||||
public String getSoapActionUri() {
|
||||
return this.soapActionUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set custom properties to be set on the stub.
|
||||
* <p>Can be populated with a String "value" (parsed via PropertiesEditor)
|
||||
* or a "props" element in XML bean definitions.
|
||||
* @see javax.xml.ws.BindingProvider#getRequestContext()
|
||||
*/
|
||||
public void setCustomProperties(Map<String, Object> customProperties) {
|
||||
this.customProperties = customProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow Map access to the custom properties to be set on the stub,
|
||||
* with the option to add or override specific entries.
|
||||
* <p>Useful for specifying entries directly, for example via
|
||||
* "customProperties[myKey]". This is particularly useful for
|
||||
* adding or overriding entries in child bean definitions.
|
||||
*/
|
||||
public Map<String, Object> getCustomProperties() {
|
||||
if (this.customProperties == null) {
|
||||
this.customProperties = new HashMap<>();
|
||||
}
|
||||
return this.customProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a custom property to this JAX-WS BindingProvider.
|
||||
* @param name the name of the attribute to expose
|
||||
* @param value the attribute value to expose
|
||||
* @see javax.xml.ws.BindingProvider#getRequestContext()
|
||||
*/
|
||||
public void addCustomProperty(String name, Object value) {
|
||||
getCustomProperties().put(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify WebServiceFeature objects (e.g. as inner bean definitions)
|
||||
* to apply to JAX-WS port stub creation.
|
||||
* @since 4.0
|
||||
* @see Service#getPort(Class, javax.xml.ws.WebServiceFeature...)
|
||||
* @see #setServiceFeatures
|
||||
*/
|
||||
public void setPortFeatures(WebServiceFeature... features) {
|
||||
this.portFeatures = features;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the interface of the service that this factory should create a proxy for.
|
||||
*/
|
||||
public void setServiceInterface(@Nullable Class<?> serviceInterface) {
|
||||
if (serviceInterface != null) {
|
||||
Assert.isTrue(serviceInterface.isInterface(), "'serviceInterface' must be an interface");
|
||||
}
|
||||
this.serviceInterface = serviceInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the interface of the service that this factory should create a proxy for.
|
||||
*/
|
||||
@Nullable
|
||||
public Class<?> getServiceInterface() {
|
||||
return this.serviceInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to look up the JAX-WS service on startup.
|
||||
* <p>Default is "true". Turn this flag off to allow for late start
|
||||
* of the target server. In this case, the JAX-WS service will be
|
||||
* lazily fetched on first access.
|
||||
*/
|
||||
public void setLookupServiceOnStartup(boolean lookupServiceOnStartup) {
|
||||
this.lookupServiceOnStartup = lookupServiceOnStartup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the bean ClassLoader to use for this interceptor: primarily for
|
||||
* building a client proxy in the {@link JaxWsPortProxyFactoryBean} subclass.
|
||||
*/
|
||||
@Override
|
||||
public void setBeanClassLoader(@Nullable ClassLoader classLoader) {
|
||||
this.beanClassLoader = classLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bean ClassLoader to use for this interceptor.
|
||||
*/
|
||||
@Nullable
|
||||
protected ClassLoader getBeanClassLoader() {
|
||||
return this.beanClassLoader;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
if (this.lookupServiceOnStartup) {
|
||||
prepare();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the JAX-WS port for this interceptor.
|
||||
*/
|
||||
public void prepare() {
|
||||
Class<?> ifc = getServiceInterface();
|
||||
Assert.notNull(ifc, "Property 'serviceInterface' is required");
|
||||
|
||||
WebService ann = ifc.getAnnotation(WebService.class);
|
||||
if (ann != null) {
|
||||
applyDefaultsFromAnnotation(ann);
|
||||
}
|
||||
|
||||
Service serviceToUse = getJaxWsService();
|
||||
if (serviceToUse == null) {
|
||||
serviceToUse = createJaxWsService();
|
||||
}
|
||||
|
||||
this.portQName = getQName(getPortName() != null ? getPortName() : ifc.getName());
|
||||
Object stub = getPortStub(serviceToUse, (getPortName() != null ? this.portQName : null));
|
||||
preparePortStub(stub);
|
||||
this.portStub = stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize this client interceptor's properties from the given WebService annotation,
|
||||
* if necessary and possible (i.e. if "wsdlDocumentUrl", "namespaceUri", "serviceName"
|
||||
* and "portName" haven't been set but corresponding values are declared at the
|
||||
* annotation level of the specified service interface).
|
||||
* @param ann the WebService annotation found on the specified service interface
|
||||
*/
|
||||
protected void applyDefaultsFromAnnotation(WebService ann) {
|
||||
if (getWsdlDocumentUrl() == null) {
|
||||
String wsdl = ann.wsdlLocation();
|
||||
if (StringUtils.hasText(wsdl)) {
|
||||
try {
|
||||
setWsdlDocumentUrl(new URL(wsdl));
|
||||
}
|
||||
catch (MalformedURLException ex) {
|
||||
throw new IllegalStateException(
|
||||
"Encountered invalid @Service wsdlLocation value [" + wsdl + "]", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (getNamespaceUri() == null) {
|
||||
String ns = ann.targetNamespace();
|
||||
if (StringUtils.hasText(ns)) {
|
||||
setNamespaceUri(ns);
|
||||
}
|
||||
}
|
||||
if (getServiceName() == null) {
|
||||
String sn = ann.serviceName();
|
||||
if (StringUtils.hasText(sn)) {
|
||||
setServiceName(sn);
|
||||
}
|
||||
}
|
||||
if (getPortName() == null) {
|
||||
String pn = ann.portName();
|
||||
if (StringUtils.hasText(pn)) {
|
||||
setPortName(pn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this client interceptor has already been prepared,
|
||||
* i.e. has already looked up the JAX-WS service and port.
|
||||
*/
|
||||
protected boolean isPrepared() {
|
||||
synchronized (this.preparationMonitor) {
|
||||
return (this.portStub != null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the prepared QName for the port.
|
||||
* @see #setPortName
|
||||
* @see #getQName
|
||||
*/
|
||||
@Nullable
|
||||
protected final QName getPortQName() {
|
||||
return this.portQName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the port stub from the given JAX-WS Service.
|
||||
* @param service the Service object to obtain the port from
|
||||
* @param portQName the name of the desired port, if specified
|
||||
* @return the corresponding port object as returned from
|
||||
* {@code Service.getPort(...)}
|
||||
*/
|
||||
protected Object getPortStub(Service service, @Nullable QName portQName) {
|
||||
if (this.portFeatures != null) {
|
||||
return (portQName != null ? service.getPort(portQName, getServiceInterface(), this.portFeatures) :
|
||||
service.getPort(getServiceInterface(), this.portFeatures));
|
||||
}
|
||||
else {
|
||||
return (portQName != null ? service.getPort(portQName, getServiceInterface()) :
|
||||
service.getPort(getServiceInterface()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the given JAX-WS port stub, applying properties to it.
|
||||
* Called by {@link #prepare}.
|
||||
* @param stub the current JAX-WS port stub
|
||||
* @see #setUsername
|
||||
* @see #setPassword
|
||||
* @see #setEndpointAddress
|
||||
* @see #setMaintainSession
|
||||
* @see #setCustomProperties
|
||||
*/
|
||||
protected void preparePortStub(Object stub) {
|
||||
Map<String, Object> stubProperties = new HashMap<>();
|
||||
String username = getUsername();
|
||||
if (username != null) {
|
||||
stubProperties.put(BindingProvider.USERNAME_PROPERTY, username);
|
||||
}
|
||||
String password = getPassword();
|
||||
if (password != null) {
|
||||
stubProperties.put(BindingProvider.PASSWORD_PROPERTY, password);
|
||||
}
|
||||
String endpointAddress = getEndpointAddress();
|
||||
if (endpointAddress != null) {
|
||||
stubProperties.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpointAddress);
|
||||
}
|
||||
if (isMaintainSession()) {
|
||||
stubProperties.put(BindingProvider.SESSION_MAINTAIN_PROPERTY, Boolean.TRUE);
|
||||
}
|
||||
if (isUseSoapAction()) {
|
||||
stubProperties.put(BindingProvider.SOAPACTION_USE_PROPERTY, Boolean.TRUE);
|
||||
}
|
||||
String soapActionUri = getSoapActionUri();
|
||||
if (soapActionUri != null) {
|
||||
stubProperties.put(BindingProvider.SOAPACTION_URI_PROPERTY, soapActionUri);
|
||||
}
|
||||
stubProperties.putAll(getCustomProperties());
|
||||
if (!stubProperties.isEmpty()) {
|
||||
if (!(stub instanceof BindingProvider)) {
|
||||
throw new RemoteLookupFailureException("Port stub of class [" + stub.getClass().getName() +
|
||||
"] is not a customizable JAX-WS stub: it does not implement interface [javax.xml.ws.BindingProvider]");
|
||||
}
|
||||
((BindingProvider) stub).getRequestContext().putAll(stubProperties);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying JAX-WS port stub that this interceptor delegates to
|
||||
* for each method invocation on the proxy.
|
||||
*/
|
||||
@Nullable
|
||||
protected Object getPortStub() {
|
||||
return this.portStub;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
if (AopUtils.isToStringMethod(invocation.getMethod())) {
|
||||
return "JAX-WS proxy for port [" + getPortName() + "] of service [" + getServiceName() + "]";
|
||||
}
|
||||
// Lazily prepare service and stub if necessary.
|
||||
synchronized (this.preparationMonitor) {
|
||||
if (!isPrepared()) {
|
||||
prepare();
|
||||
}
|
||||
}
|
||||
return doInvoke(invocation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a JAX-WS service invocation based on the given method invocation.
|
||||
* @param invocation the AOP method invocation
|
||||
* @return the invocation result, if any
|
||||
* @throws Throwable in case of invocation failure
|
||||
* @see #getPortStub()
|
||||
* @see #doInvoke(org.aopalliance.intercept.MethodInvocation, Object)
|
||||
*/
|
||||
@Nullable
|
||||
protected Object doInvoke(MethodInvocation invocation) throws Throwable {
|
||||
try {
|
||||
return doInvoke(invocation, getPortStub());
|
||||
}
|
||||
catch (SOAPFaultException ex) {
|
||||
throw new JaxWsSoapFaultException(ex);
|
||||
}
|
||||
catch (ProtocolException ex) {
|
||||
throw new RemoteConnectFailureException(
|
||||
"Could not connect to remote service [" + getEndpointAddress() + "]", ex);
|
||||
}
|
||||
catch (WebServiceException ex) {
|
||||
throw new RemoteAccessException(
|
||||
"Could not access remote service at [" + getEndpointAddress() + "]", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a JAX-WS service invocation on the given port stub.
|
||||
* @param invocation the AOP method invocation
|
||||
* @param portStub the RMI port stub to invoke
|
||||
* @return the invocation result, if any
|
||||
* @throws Throwable in case of invocation failure
|
||||
* @see #getPortStub()
|
||||
*/
|
||||
@Nullable
|
||||
protected Object doInvoke(MethodInvocation invocation, @Nullable Object portStub) throws Throwable {
|
||||
Method method = invocation.getMethod();
|
||||
try {
|
||||
return method.invoke(portStub, invocation.getArguments());
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
throw ex.getTargetException();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new RemoteProxyFailureException("Invocation of stub method failed: " + method, ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.remoting.jaxws;
|
||||
|
||||
import javax.xml.ws.BindingProvider;
|
||||
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.FactoryBean} for a specific port of a
|
||||
* JAX-WS service. Exposes a proxy for the port, to be used for bean references.
|
||||
* Inherits configuration properties from {@link JaxWsPortClientInterceptor}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see #setServiceInterface
|
||||
* @see LocalJaxWsServiceFactoryBean
|
||||
*/
|
||||
public class JaxWsPortProxyFactoryBean extends JaxWsPortClientInterceptor implements FactoryBean<Object> {
|
||||
|
||||
@Nullable
|
||||
private Object serviceProxy;
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
super.afterPropertiesSet();
|
||||
|
||||
Class<?> ifc = getServiceInterface();
|
||||
Assert.notNull(ifc, "Property 'serviceInterface' is required");
|
||||
|
||||
// Build a proxy that also exposes the JAX-WS BindingProvider interface.
|
||||
ProxyFactory pf = new ProxyFactory();
|
||||
pf.addInterface(ifc);
|
||||
pf.addInterface(BindingProvider.class);
|
||||
pf.addAdvice(this);
|
||||
this.serviceProxy = pf.getProxy(getBeanClassLoader());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object getObject() {
|
||||
return this.serviceProxy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return getServiceInterface();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.remoting.jaxws;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
import javax.xml.soap.SOAPFault;
|
||||
import javax.xml.ws.soap.SOAPFaultException;
|
||||
|
||||
import org.springframework.remoting.soap.SoapFaultException;
|
||||
|
||||
/**
|
||||
* Spring SoapFaultException adapter for the JAX-WS
|
||||
* {@link javax.xml.ws.soap.SOAPFaultException} class.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class JaxWsSoapFaultException extends SoapFaultException {
|
||||
|
||||
/**
|
||||
* Constructor for JaxWsSoapFaultException.
|
||||
* @param original the original JAX-WS SOAPFaultException to wrap
|
||||
*/
|
||||
public JaxWsSoapFaultException(SOAPFaultException original) {
|
||||
super(original.getMessage(), original);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the wrapped JAX-WS SOAPFault.
|
||||
*/
|
||||
public final SOAPFault getFault() {
|
||||
return ((SOAPFaultException) getCause()).getFault();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getFaultCode() {
|
||||
return getFault().getFaultCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public QName getFaultCodeAsQName() {
|
||||
return getFault().getFaultCodeAsQName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFaultString() {
|
||||
return getFault().getFaultString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFaultActor() {
|
||||
return getFault().getFaultActor();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
import javax.xml.ws.Service;
|
||||
import javax.xml.ws.WebServiceFeature;
|
||||
import javax.xml.ws.handler.HandlerResolver;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Factory for locally defined JAX-WS {@link javax.xml.ws.Service} references.
|
||||
* Uses the JAX-WS {@link javax.xml.ws.Service#create} factory API underneath.
|
||||
*
|
||||
* <p>Serves as base class for {@link LocalJaxWsServiceFactoryBean} as well as
|
||||
* {@link JaxWsPortClientInterceptor} and {@link JaxWsPortProxyFactoryBean}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see javax.xml.ws.Service
|
||||
* @see LocalJaxWsServiceFactoryBean
|
||||
* @see JaxWsPortClientInterceptor
|
||||
* @see JaxWsPortProxyFactoryBean
|
||||
*/
|
||||
public class LocalJaxWsServiceFactory {
|
||||
|
||||
@Nullable
|
||||
private URL wsdlDocumentUrl;
|
||||
|
||||
@Nullable
|
||||
private String namespaceUri;
|
||||
|
||||
@Nullable
|
||||
private String serviceName;
|
||||
|
||||
@Nullable
|
||||
private WebServiceFeature[] serviceFeatures;
|
||||
|
||||
@Nullable
|
||||
private Executor executor;
|
||||
|
||||
@Nullable
|
||||
private HandlerResolver handlerResolver;
|
||||
|
||||
|
||||
/**
|
||||
* Set the URL of the WSDL document that describes the service.
|
||||
* @see #setWsdlDocumentResource(Resource)
|
||||
*/
|
||||
public void setWsdlDocumentUrl(@Nullable URL wsdlDocumentUrl) {
|
||||
this.wsdlDocumentUrl = wsdlDocumentUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the WSDL document URL as a {@link Resource}.
|
||||
* @since 3.2
|
||||
*/
|
||||
public void setWsdlDocumentResource(Resource wsdlDocumentResource) throws IOException {
|
||||
Assert.notNull(wsdlDocumentResource, "WSDL Resource must not be null");
|
||||
this.wsdlDocumentUrl = wsdlDocumentResource.getURL();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the URL of the WSDL document that describes the service.
|
||||
*/
|
||||
@Nullable
|
||||
public URL getWsdlDocumentUrl() {
|
||||
return this.wsdlDocumentUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the namespace URI of the service.
|
||||
* Corresponds to the WSDL "targetNamespace".
|
||||
*/
|
||||
public void setNamespaceUri(@Nullable String namespaceUri) {
|
||||
this.namespaceUri = (namespaceUri != null ? namespaceUri.trim() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the namespace URI of the service.
|
||||
*/
|
||||
@Nullable
|
||||
public String getNamespaceUri() {
|
||||
return this.namespaceUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the service to look up.
|
||||
* Corresponds to the "wsdl:service" name.
|
||||
*/
|
||||
public void setServiceName(@Nullable String serviceName) {
|
||||
this.serviceName = serviceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the service.
|
||||
*/
|
||||
@Nullable
|
||||
public String getServiceName() {
|
||||
return this.serviceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify WebServiceFeature objects (e.g. as inner bean definitions)
|
||||
* to apply to JAX-WS service creation.
|
||||
* @since 4.0
|
||||
* @see Service#create(QName, WebServiceFeature...)
|
||||
*/
|
||||
public void setServiceFeatures(WebServiceFeature... serviceFeatures) {
|
||||
this.serviceFeatures = serviceFeatures;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JDK concurrent executor to use for asynchronous executions
|
||||
* that require callbacks.
|
||||
* @see javax.xml.ws.Service#setExecutor
|
||||
*/
|
||||
public void setExecutor(Executor executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JAX-WS HandlerResolver to use for all proxies and dispatchers
|
||||
* created through this factory.
|
||||
* @see javax.xml.ws.Service#setHandlerResolver
|
||||
*/
|
||||
public void setHandlerResolver(HandlerResolver handlerResolver) {
|
||||
this.handlerResolver = handlerResolver;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a JAX-WS Service according to the parameters of this factory.
|
||||
* @see #setServiceName
|
||||
* @see #setWsdlDocumentUrl
|
||||
*/
|
||||
public Service createJaxWsService() {
|
||||
Assert.notNull(this.serviceName, "No service name specified");
|
||||
Service service;
|
||||
|
||||
if (this.serviceFeatures != null) {
|
||||
service = (this.wsdlDocumentUrl != null ?
|
||||
Service.create(this.wsdlDocumentUrl, getQName(this.serviceName), this.serviceFeatures) :
|
||||
Service.create(getQName(this.serviceName), this.serviceFeatures));
|
||||
}
|
||||
else {
|
||||
service = (this.wsdlDocumentUrl != null ?
|
||||
Service.create(this.wsdlDocumentUrl, getQName(this.serviceName)) :
|
||||
Service.create(getQName(this.serviceName)));
|
||||
}
|
||||
|
||||
if (this.executor != null) {
|
||||
service.setExecutor(this.executor);
|
||||
}
|
||||
if (this.handlerResolver != null) {
|
||||
service.setHandlerResolver(this.handlerResolver);
|
||||
}
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a QName for the given name, relative to the namespace URI
|
||||
* of this factory, if given.
|
||||
* @see #setNamespaceUri
|
||||
*/
|
||||
protected QName getQName(String name) {
|
||||
return (getNamespaceUri() != null ? new QName(getNamespaceUri(), name) : new QName(name));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.remoting.jaxws;
|
||||
|
||||
import javax.xml.ws.Service;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.FactoryBean} for locally
|
||||
* defined JAX-WS Service references.
|
||||
* Uses {@link LocalJaxWsServiceFactory}'s facilities underneath.
|
||||
*
|
||||
* <p>Alternatively, JAX-WS Service references can be looked up
|
||||
* in the JNDI environment of the Java EE container.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see javax.xml.ws.Service
|
||||
* @see org.springframework.jndi.JndiObjectFactoryBean
|
||||
* @see JaxWsPortProxyFactoryBean
|
||||
*/
|
||||
public class LocalJaxWsServiceFactoryBean extends LocalJaxWsServiceFactory
|
||||
implements FactoryBean<Service>, InitializingBean {
|
||||
|
||||
@Nullable
|
||||
private Service service;
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
this.service = createJaxWsService();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Service getObject() {
|
||||
return this.service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Service> getObjectType() {
|
||||
return (this.service != null ? this.service.getClass() : Service.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,228 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.remoting.jaxws;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
|
||||
import javax.jws.WebService;
|
||||
import javax.xml.ws.Endpoint;
|
||||
import javax.xml.ws.WebServiceProvider;
|
||||
|
||||
import com.sun.net.httpserver.Authenticator;
|
||||
import com.sun.net.httpserver.Filter;
|
||||
import com.sun.net.httpserver.HttpContext;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple exporter for JAX-WS services, autodetecting annotated service beans
|
||||
* (through the JAX-WS {@link javax.jws.WebService} annotation) and exporting
|
||||
* them through the HTTP server included in Sun's JDK 1.6. The full address
|
||||
* for each service will consist of the server's base address with the
|
||||
* service name appended (e.g. "http://localhost:8080/OrderService").
|
||||
*
|
||||
* <p>Note that this exporter will only work on Sun's JDK 1.6 or higher, as well
|
||||
* as on JDKs that ship Sun's entire class library as included in the Sun JDK.
|
||||
* For a portable JAX-WS exporter, have a look at {@link SimpleJaxWsServiceExporter}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5.5
|
||||
* @see javax.jws.WebService
|
||||
* @see javax.xml.ws.Endpoint#publish(Object)
|
||||
* @see SimpleJaxWsServiceExporter
|
||||
* @deprecated as of Spring Framework 5.1, in favor of {@link SimpleJaxWsServiceExporter}
|
||||
*/
|
||||
@Deprecated
|
||||
@org.springframework.lang.UsesSunHttpServer
|
||||
public class SimpleHttpServerJaxWsServiceExporter extends AbstractJaxWsServiceExporter {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@Nullable
|
||||
private HttpServer server;
|
||||
|
||||
private int port = 8080;
|
||||
|
||||
@Nullable
|
||||
private String hostname;
|
||||
|
||||
private int backlog = -1;
|
||||
|
||||
private int shutdownDelay = 0;
|
||||
|
||||
private String basePath = "/";
|
||||
|
||||
@Nullable
|
||||
private List<Filter> filters;
|
||||
|
||||
@Nullable
|
||||
private Authenticator authenticator;
|
||||
|
||||
private boolean localServer = false;
|
||||
|
||||
|
||||
/**
|
||||
* Specify an existing HTTP server to register the web service contexts
|
||||
* with. This will typically be a server managed by the general Spring
|
||||
* {@link org.springframework.remoting.support.SimpleHttpServerFactoryBean}.
|
||||
* <p>Alternatively, configure a local HTTP server through the
|
||||
* {@link #setPort "port"}, {@link #setHostname "hostname"} and
|
||||
* {@link #setBacklog "backlog"} properties (or rely on the defaults there).
|
||||
*/
|
||||
public void setServer(HttpServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the HTTP server's port. Default is 8080.
|
||||
* <p>Only applicable for a locally configured HTTP server.
|
||||
* Ignored when the {@link #setServer "server"} property has been specified.
|
||||
*/
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the HTTP server's hostname to bind to. Default is localhost;
|
||||
* can be overridden with a specific network address to bind to.
|
||||
* <p>Only applicable for a locally configured HTTP server.
|
||||
* Ignored when the {@link #setServer "server"} property has been specified.
|
||||
*/
|
||||
public void setHostname(String hostname) {
|
||||
this.hostname = hostname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the HTTP server's TCP backlog. Default is -1,
|
||||
* indicating the system's default value.
|
||||
* <p>Only applicable for a locally configured HTTP server.
|
||||
* Ignored when the {@link #setServer "server"} property has been specified.
|
||||
*/
|
||||
public void setBacklog(int backlog) {
|
||||
this.backlog = backlog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the number of seconds to wait until HTTP exchanges have
|
||||
* completed when shutting down the HTTP server. Default is 0.
|
||||
* <p>Only applicable for a locally configured HTTP server.
|
||||
* Ignored when the {@link #setServer "server"} property has been specified.
|
||||
*/
|
||||
public void setShutdownDelay(int shutdownDelay) {
|
||||
this.shutdownDelay = shutdownDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the base path for context publication. Default is "/".
|
||||
* <p>For each context publication path, the service name will be
|
||||
* appended to this base address. E.g. service name "OrderService"
|
||||
* -> "/OrderService".
|
||||
* @see javax.xml.ws.Endpoint#publish(Object)
|
||||
* @see javax.jws.WebService#serviceName()
|
||||
*/
|
||||
public void setBasePath(String basePath) {
|
||||
this.basePath = basePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register common {@link com.sun.net.httpserver.Filter Filters} to be
|
||||
* applied to all detected {@link javax.jws.WebService} annotated beans.
|
||||
*/
|
||||
public void setFilters(List<Filter> filters) {
|
||||
this.filters = filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a common {@link com.sun.net.httpserver.Authenticator} to be
|
||||
* applied to all detected {@link javax.jws.WebService} annotated beans.
|
||||
*/
|
||||
public void setAuthenticator(Authenticator authenticator) {
|
||||
this.authenticator = authenticator;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (this.server == null) {
|
||||
InetSocketAddress address = (this.hostname != null ?
|
||||
new InetSocketAddress(this.hostname, this.port) : new InetSocketAddress(this.port));
|
||||
HttpServer server = HttpServer.create(address, this.backlog);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Starting HttpServer at address " + address);
|
||||
}
|
||||
server.start();
|
||||
this.server = server;
|
||||
this.localServer = true;
|
||||
}
|
||||
super.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void publishEndpoint(Endpoint endpoint, WebService annotation) {
|
||||
endpoint.publish(buildHttpContext(endpoint, annotation.serviceName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void publishEndpoint(Endpoint endpoint, WebServiceProvider annotation) {
|
||||
endpoint.publish(buildHttpContext(endpoint, annotation.serviceName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the HttpContext for the given endpoint.
|
||||
* @param endpoint the JAX-WS Provider Endpoint object
|
||||
* @param serviceName the given service name
|
||||
* @return the fully populated HttpContext
|
||||
*/
|
||||
protected HttpContext buildHttpContext(Endpoint endpoint, String serviceName) {
|
||||
Assert.state(this.server != null, "No HttpServer available");
|
||||
String fullPath = calculateEndpointPath(endpoint, serviceName);
|
||||
HttpContext httpContext = this.server.createContext(fullPath);
|
||||
if (this.filters != null) {
|
||||
httpContext.getFilters().addAll(this.filters);
|
||||
}
|
||||
if (this.authenticator != null) {
|
||||
httpContext.setAuthenticator(this.authenticator);
|
||||
}
|
||||
return httpContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the full endpoint path for the given endpoint.
|
||||
* @param endpoint the JAX-WS Provider Endpoint object
|
||||
* @param serviceName the given service name
|
||||
* @return the full endpoint path
|
||||
*/
|
||||
protected String calculateEndpointPath(Endpoint endpoint, String serviceName) {
|
||||
return this.basePath + serviceName;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
super.destroy();
|
||||
if (this.server != null && this.localServer) {
|
||||
logger.info("Stopping HttpServer");
|
||||
this.server.stop(this.shutdownDelay);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.remoting.jaxws;
|
||||
|
||||
import javax.jws.WebService;
|
||||
import javax.xml.ws.Endpoint;
|
||||
import javax.xml.ws.WebServiceProvider;
|
||||
|
||||
/**
|
||||
* Simple exporter for JAX-WS services, autodetecting annotated service beans
|
||||
* (through the JAX-WS {@link javax.jws.WebService} annotation) and exporting
|
||||
* them with a configured base address (by default "http://localhost:8080/")
|
||||
* using the JAX-WS provider's built-in publication support. The full address
|
||||
* for each service will consist of the base address with the service name
|
||||
* appended (e.g. "http://localhost:8080/OrderService").
|
||||
*
|
||||
* <p>Note that this exporter will only work if the JAX-WS runtime actually
|
||||
* supports publishing with an address argument, i.e. if the JAX-WS runtime
|
||||
* ships an internal HTTP server.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see javax.jws.WebService
|
||||
* @see javax.xml.ws.Endpoint#publish(String)
|
||||
*/
|
||||
public class SimpleJaxWsServiceExporter extends AbstractJaxWsServiceExporter {
|
||||
|
||||
/**
|
||||
* The default base address.
|
||||
*/
|
||||
public static final String DEFAULT_BASE_ADDRESS = "http://localhost:8080/";
|
||||
|
||||
private String baseAddress = DEFAULT_BASE_ADDRESS;
|
||||
|
||||
|
||||
/**
|
||||
* Set the base address for exported services.
|
||||
* Default is "http://localhost:8080/".
|
||||
* <p>For each actual publication address, the service name will be
|
||||
* appended to this base address. E.g. service name "OrderService"
|
||||
* -> "http://localhost:8080/OrderService".
|
||||
* @see javax.xml.ws.Endpoint#publish(String)
|
||||
* @see javax.jws.WebService#serviceName()
|
||||
*/
|
||||
public void setBaseAddress(String baseAddress) {
|
||||
this.baseAddress = baseAddress;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void publishEndpoint(Endpoint endpoint, WebService annotation) {
|
||||
endpoint.publish(calculateEndpointAddress(endpoint, annotation.serviceName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void publishEndpoint(Endpoint endpoint, WebServiceProvider annotation) {
|
||||
endpoint.publish(calculateEndpointAddress(endpoint, annotation.serviceName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the full endpoint address for the given endpoint.
|
||||
* @param endpoint the JAX-WS Provider Endpoint object
|
||||
* @param serviceName the given service name
|
||||
* @return the full endpoint address
|
||||
*/
|
||||
protected String calculateEndpointAddress(Endpoint endpoint, String serviceName) {
|
||||
String fullAddress = this.baseAddress + serviceName;
|
||||
if (endpoint.getClass().getName().startsWith("weblogic.")) {
|
||||
// Workaround for WebLogic 10.3
|
||||
fullAddress = fullAddress + "/";
|
||||
}
|
||||
return fullAddress;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@NonNullApi
|
||||
@NonNullFields
|
||||
package org.springframework.remoting.jaxws;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
import org.springframework.lang.NonNullFields;
|
||||
@@ -1,163 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.net.InetSocketAddress;
|
||||
|
||||
import com.caucho.hessian.client.HessianProxyFactory;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.beans.testfixture.beans.ITestBean;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.remoting.RemoteAccessException;
|
||||
import org.springframework.util.SocketUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 16.05.2003
|
||||
*/
|
||||
public class CauchoRemotingTests {
|
||||
|
||||
@Test
|
||||
public void hessianProxyFactoryBeanWithClassInsteadOfInterface() throws Exception {
|
||||
HessianProxyFactoryBean factory = new HessianProxyFactoryBean();
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
factory.setServiceInterface(TestBean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hessianProxyFactoryBeanWithAccessError() throws Exception {
|
||||
HessianProxyFactoryBean factory = new HessianProxyFactoryBean();
|
||||
factory.setServiceInterface(ITestBean.class);
|
||||
factory.setServiceUrl("http://localhosta/testbean");
|
||||
factory.afterPropertiesSet();
|
||||
|
||||
assertThat(factory.isSingleton()).as("Correct singleton value").isTrue();
|
||||
boolean condition = factory.getObject() instanceof ITestBean;
|
||||
assertThat(condition).isTrue();
|
||||
ITestBean bean = (ITestBean) factory.getObject();
|
||||
|
||||
assertThatExceptionOfType(RemoteAccessException.class).isThrownBy(() ->
|
||||
bean.setName("test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hessianProxyFactoryBeanWithAuthenticationAndAccessError() throws Exception {
|
||||
HessianProxyFactoryBean factory = new HessianProxyFactoryBean();
|
||||
factory.setServiceInterface(ITestBean.class);
|
||||
factory.setServiceUrl("http://localhosta/testbean");
|
||||
factory.setUsername("test");
|
||||
factory.setPassword("bean");
|
||||
factory.setOverloadEnabled(true);
|
||||
factory.afterPropertiesSet();
|
||||
|
||||
assertThat(factory.isSingleton()).as("Correct singleton value").isTrue();
|
||||
boolean condition = factory.getObject() instanceof ITestBean;
|
||||
assertThat(condition).isTrue();
|
||||
ITestBean bean = (ITestBean) factory.getObject();
|
||||
|
||||
assertThatExceptionOfType(RemoteAccessException.class).isThrownBy(() ->
|
||||
bean.setName("test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hessianProxyFactoryBeanWithCustomProxyFactory() throws Exception {
|
||||
TestHessianProxyFactory proxyFactory = new TestHessianProxyFactory();
|
||||
HessianProxyFactoryBean factory = new HessianProxyFactoryBean();
|
||||
factory.setServiceInterface(ITestBean.class);
|
||||
factory.setServiceUrl("http://localhosta/testbean");
|
||||
factory.setProxyFactory(proxyFactory);
|
||||
factory.setUsername("test");
|
||||
factory.setPassword("bean");
|
||||
factory.setOverloadEnabled(true);
|
||||
factory.afterPropertiesSet();
|
||||
assertThat(factory.isSingleton()).as("Correct singleton value").isTrue();
|
||||
boolean condition = factory.getObject() instanceof ITestBean;
|
||||
assertThat(condition).isTrue();
|
||||
ITestBean bean = (ITestBean) factory.getObject();
|
||||
|
||||
assertThat(proxyFactory.user).isEqualTo("test");
|
||||
assertThat(proxyFactory.password).isEqualTo("bean");
|
||||
assertThat(proxyFactory.overloadEnabled).isTrue();
|
||||
|
||||
assertThatExceptionOfType(RemoteAccessException.class).isThrownBy(() ->
|
||||
bean.setName("test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation")
|
||||
public void simpleHessianServiceExporter() throws IOException {
|
||||
final int port = SocketUtils.findAvailableTcpPort();
|
||||
|
||||
TestBean tb = new TestBean("tb");
|
||||
SimpleHessianServiceExporter exporter = new SimpleHessianServiceExporter();
|
||||
exporter.setService(tb);
|
||||
exporter.setServiceInterface(ITestBean.class);
|
||||
exporter.setDebug(true);
|
||||
exporter.prepare();
|
||||
|
||||
HttpServer server = HttpServer.create(new InetSocketAddress(port), -1);
|
||||
server.createContext("/hessian", exporter);
|
||||
server.start();
|
||||
try {
|
||||
HessianClientInterceptor client = new HessianClientInterceptor();
|
||||
client.setServiceUrl("http://localhost:" + port + "/hessian");
|
||||
client.setServiceInterface(ITestBean.class);
|
||||
//client.setHessian2(true);
|
||||
client.prepare();
|
||||
ITestBean proxy = ProxyFactory.getProxy(ITestBean.class, client);
|
||||
assertThat(proxy.getName()).isEqualTo("tb");
|
||||
proxy.setName("test");
|
||||
assertThat(proxy.getName()).isEqualTo("test");
|
||||
}
|
||||
finally {
|
||||
server.stop(Integer.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class TestHessianProxyFactory extends HessianProxyFactory {
|
||||
|
||||
private String user;
|
||||
private String password;
|
||||
private boolean overloadEnabled;
|
||||
|
||||
@Override
|
||||
public void setUser(String user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOverloadEnabled(boolean overloadEnabled) {
|
||||
this.overloadEnabled = overloadEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.Configurable;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.withSettings;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class HttpComponentsHttpInvokerRequestExecutorTests {
|
||||
|
||||
@Test
|
||||
public void customizeConnectionTimeout() throws IOException {
|
||||
HttpComponentsHttpInvokerRequestExecutor executor = new HttpComponentsHttpInvokerRequestExecutor();
|
||||
executor.setConnectTimeout(5000);
|
||||
|
||||
HttpInvokerClientConfiguration config = mockHttpInvokerClientConfiguration("https://fake-service");
|
||||
HttpPost httpPost = executor.createHttpPost(config);
|
||||
assertThat(httpPost.getConfig().getConnectTimeout()).isEqualTo(5000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customizeConnectionRequestTimeout() throws IOException {
|
||||
HttpComponentsHttpInvokerRequestExecutor executor = new HttpComponentsHttpInvokerRequestExecutor();
|
||||
executor.setConnectionRequestTimeout(7000);
|
||||
|
||||
HttpInvokerClientConfiguration config = mockHttpInvokerClientConfiguration("https://fake-service");
|
||||
HttpPost httpPost = executor.createHttpPost(config);
|
||||
assertThat(httpPost.getConfig().getConnectionRequestTimeout()).isEqualTo(7000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customizeReadTimeout() throws IOException {
|
||||
HttpComponentsHttpInvokerRequestExecutor executor = new HttpComponentsHttpInvokerRequestExecutor();
|
||||
executor.setReadTimeout(10000);
|
||||
|
||||
HttpInvokerClientConfiguration config = mockHttpInvokerClientConfiguration("https://fake-service");
|
||||
HttpPost httpPost = executor.createHttpPost(config);
|
||||
assertThat(httpPost.getConfig().getSocketTimeout()).isEqualTo(10000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultSettingsOfHttpClientMergedOnExecutorCustomization() throws IOException {
|
||||
RequestConfig defaultConfig = RequestConfig.custom().setConnectTimeout(1234).build();
|
||||
CloseableHttpClient client = mock(CloseableHttpClient.class,
|
||||
withSettings().extraInterfaces(Configurable.class));
|
||||
Configurable configurable = (Configurable) client;
|
||||
given(configurable.getConfig()).willReturn(defaultConfig);
|
||||
|
||||
HttpComponentsHttpInvokerRequestExecutor executor =
|
||||
new HttpComponentsHttpInvokerRequestExecutor(client);
|
||||
HttpInvokerClientConfiguration config = mockHttpInvokerClientConfiguration("https://fake-service");
|
||||
HttpPost httpPost = executor.createHttpPost(config);
|
||||
assertThat(httpPost.getConfig()).as("Default client configuration is expected").isSameAs(defaultConfig);
|
||||
|
||||
executor.setConnectionRequestTimeout(4567);
|
||||
HttpPost httpPost2 = executor.createHttpPost(config);
|
||||
assertThat(httpPost2.getConfig()).isNotNull();
|
||||
assertThat(httpPost2.getConfig().getConnectionRequestTimeout()).isEqualTo(4567);
|
||||
// Default connection timeout merged
|
||||
assertThat(httpPost2.getConfig().getConnectTimeout()).isEqualTo(1234);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void localSettingsOverrideClientDefaultSettings() throws Exception {
|
||||
RequestConfig defaultConfig = RequestConfig.custom()
|
||||
.setConnectTimeout(1234).setConnectionRequestTimeout(6789).build();
|
||||
CloseableHttpClient client = mock(CloseableHttpClient.class,
|
||||
withSettings().extraInterfaces(Configurable.class));
|
||||
Configurable configurable = (Configurable) client;
|
||||
given(configurable.getConfig()).willReturn(defaultConfig);
|
||||
|
||||
HttpComponentsHttpInvokerRequestExecutor executor =
|
||||
new HttpComponentsHttpInvokerRequestExecutor(client);
|
||||
executor.setConnectTimeout(5000);
|
||||
|
||||
HttpInvokerClientConfiguration config = mockHttpInvokerClientConfiguration("https://fake-service");
|
||||
HttpPost httpPost = executor.createHttpPost(config);
|
||||
RequestConfig requestConfig = httpPost.getConfig();
|
||||
assertThat(requestConfig.getConnectTimeout()).isEqualTo(5000);
|
||||
assertThat(requestConfig.getConnectionRequestTimeout()).isEqualTo(6789);
|
||||
assertThat(requestConfig.getSocketTimeout()).isEqualTo(-1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mergeBasedOnCurrentHttpClient() throws Exception {
|
||||
RequestConfig defaultConfig = RequestConfig.custom()
|
||||
.setSocketTimeout(1234).build();
|
||||
final CloseableHttpClient client = mock(CloseableHttpClient.class,
|
||||
withSettings().extraInterfaces(Configurable.class));
|
||||
Configurable configurable = (Configurable) client;
|
||||
given(configurable.getConfig()).willReturn(defaultConfig);
|
||||
|
||||
HttpComponentsHttpInvokerRequestExecutor executor =
|
||||
new HttpComponentsHttpInvokerRequestExecutor() {
|
||||
@Override
|
||||
public HttpClient getHttpClient() {
|
||||
return client;
|
||||
}
|
||||
};
|
||||
executor.setReadTimeout(5000);
|
||||
HttpInvokerClientConfiguration config = mockHttpInvokerClientConfiguration("https://fake-service");
|
||||
HttpPost httpPost = executor.createHttpPost(config);
|
||||
RequestConfig requestConfig = httpPost.getConfig();
|
||||
assertThat(requestConfig.getConnectTimeout()).isEqualTo(-1);
|
||||
assertThat(requestConfig.getConnectionRequestTimeout()).isEqualTo(-1);
|
||||
assertThat(requestConfig.getSocketTimeout()).isEqualTo(5000);
|
||||
|
||||
// Update the Http client so that it returns an updated config
|
||||
RequestConfig updatedDefaultConfig = RequestConfig.custom()
|
||||
.setConnectTimeout(1234).build();
|
||||
given(configurable.getConfig()).willReturn(updatedDefaultConfig);
|
||||
executor.setReadTimeout(7000);
|
||||
HttpPost httpPost2 = executor.createHttpPost(config);
|
||||
RequestConfig requestConfig2 = httpPost2.getConfig();
|
||||
assertThat(requestConfig2.getConnectTimeout()).isEqualTo(1234);
|
||||
assertThat(requestConfig2.getConnectionRequestTimeout()).isEqualTo(-1);
|
||||
assertThat(requestConfig2.getSocketTimeout()).isEqualTo(7000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ignoreFactorySettings() throws IOException {
|
||||
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
|
||||
HttpComponentsHttpInvokerRequestExecutor executor = new HttpComponentsHttpInvokerRequestExecutor(httpClient) {
|
||||
@Override
|
||||
protected RequestConfig createRequestConfig(HttpInvokerClientConfiguration config) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
HttpInvokerClientConfiguration config = mockHttpInvokerClientConfiguration("https://fake-service");
|
||||
HttpPost httpPost = executor.createHttpPost(config);
|
||||
assertThat(httpPost.getConfig()).as("custom request config should not be set").isNull();
|
||||
}
|
||||
|
||||
private HttpInvokerClientConfiguration mockHttpInvokerClientConfiguration(String serviceUrl) {
|
||||
HttpInvokerClientConfiguration config = mock(HttpInvokerClientConfiguration.class);
|
||||
given(config.getServiceUrl()).willReturn(serviceUrl);
|
||||
return config;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.remoting.support.RemoteInvocationResult;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class HttpInvokerFactoryBeanIntegrationTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("resource")
|
||||
void testLoadedConfigClass() {
|
||||
ApplicationContext context = new AnnotationConfigApplicationContext(InvokerAutowiringConfig.class);
|
||||
MyBean myBean = context.getBean("myBean", MyBean.class);
|
||||
assertThat(myBean.myService).isSameAs(context.getBean("myService"));
|
||||
myBean.myService.handle();
|
||||
myBean.myService.handleAsync();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("resource")
|
||||
void testNonLoadedConfigClass() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
context.registerBeanDefinition("config", new RootBeanDefinition(InvokerAutowiringConfig.class.getName()));
|
||||
context.refresh();
|
||||
MyBean myBean = context.getBean("myBean", MyBean.class);
|
||||
assertThat(myBean.myService).isSameAs(context.getBean("myService"));
|
||||
myBean.myService.handle();
|
||||
myBean.myService.handleAsync();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("resource")
|
||||
void withConfigurationClassWithPlainFactoryBean() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
context.register(ConfigWithPlainFactoryBean.class);
|
||||
context.refresh();
|
||||
MyBean myBean = context.getBean("myBean", MyBean.class);
|
||||
assertThat(myBean.myService).isSameAs(context.getBean("myService"));
|
||||
myBean.myService.handle();
|
||||
myBean.myService.handleAsync();
|
||||
}
|
||||
|
||||
|
||||
interface MyService {
|
||||
|
||||
void handle();
|
||||
|
||||
@Async
|
||||
public void handleAsync();
|
||||
}
|
||||
|
||||
|
||||
@Component("myBean")
|
||||
static class MyBean {
|
||||
|
||||
@Autowired
|
||||
MyService myService;
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
@Lazy
|
||||
static class InvokerAutowiringConfig {
|
||||
|
||||
@Bean
|
||||
AsyncAnnotationBeanPostProcessor aabpp() {
|
||||
return new AsyncAnnotationBeanPostProcessor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("deprecation")
|
||||
HttpInvokerProxyFactoryBean myService() {
|
||||
HttpInvokerProxyFactoryBean factory = new HttpInvokerProxyFactoryBean();
|
||||
factory.setServiceUrl("/svc/dummy");
|
||||
factory.setServiceInterface(MyService.class);
|
||||
factory.setHttpInvokerRequestExecutor((config, invocation) -> new RemoteInvocationResult());
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
FactoryBean<String> myOtherService() {
|
||||
throw new IllegalStateException("Don't ever call me");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class ConfigWithPlainFactoryBean {
|
||||
|
||||
@Autowired
|
||||
Environment env;
|
||||
|
||||
@Bean
|
||||
MyBean myBean() {
|
||||
return new MyBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("deprecation")
|
||||
HttpInvokerProxyFactoryBean myService() {
|
||||
String name = env.getProperty("testbean.name");
|
||||
HttpInvokerProxyFactoryBean factory = new HttpInvokerProxyFactoryBean();
|
||||
factory.setServiceUrl("/svc/" + name);
|
||||
factory.setServiceInterface(MyService.class);
|
||||
factory.setHttpInvokerRequestExecutor((config, invocation) -> new RemoteInvocationResult());
|
||||
return factory;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,453 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.ByteArrayInputStream;
|
||||
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.io.Serializable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Arrays;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.testfixture.beans.ITestBean;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.remoting.RemoteAccessException;
|
||||
import org.springframework.remoting.support.DefaultRemoteInvocationExecutor;
|
||||
import org.springframework.remoting.support.RemoteInvocation;
|
||||
import org.springframework.remoting.support.RemoteInvocationResult;
|
||||
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
|
||||
import org.springframework.web.testfixture.servlet.MockHttpServletResponse;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
* @since 09.08.2004
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
class HttpInvokerTests {
|
||||
|
||||
@Test
|
||||
void httpInvokerProxyFactoryBeanAndServiceExporter() {
|
||||
doTestHttpInvokerProxyFactoryBeanAndServiceExporter(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void httpInvokerProxyFactoryBeanAndServiceExporterWithExplicitClassLoader() {
|
||||
doTestHttpInvokerProxyFactoryBeanAndServiceExporter(true);
|
||||
}
|
||||
|
||||
private void doTestHttpInvokerProxyFactoryBeanAndServiceExporter(boolean explicitClassLoader) {
|
||||
TestBean target = new TestBean("myname", 99);
|
||||
|
||||
final HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter();
|
||||
exporter.setServiceInterface(ITestBean.class);
|
||||
exporter.setService(target);
|
||||
exporter.afterPropertiesSet();
|
||||
|
||||
HttpInvokerProxyFactoryBean pfb = new HttpInvokerProxyFactoryBean();
|
||||
pfb.setServiceInterface(ITestBean.class);
|
||||
pfb.setServiceUrl("https://myurl");
|
||||
|
||||
pfb.setHttpInvokerRequestExecutor(new AbstractHttpInvokerRequestExecutor() {
|
||||
@Override
|
||||
protected RemoteInvocationResult doExecuteRequest(
|
||||
HttpInvokerClientConfiguration config, ByteArrayOutputStream baos) throws Exception {
|
||||
assertThat(config.getServiceUrl()).isEqualTo("https://myurl");
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
request.setContent(baos.toByteArray());
|
||||
exporter.handleRequest(request, response);
|
||||
return readRemoteInvocationResult(
|
||||
new ByteArrayInputStream(response.getContentAsByteArray()), config.getCodebaseUrl());
|
||||
}
|
||||
});
|
||||
if (explicitClassLoader) {
|
||||
((BeanClassLoaderAware) pfb.getHttpInvokerRequestExecutor()).setBeanClassLoader(getClass().getClassLoader());
|
||||
}
|
||||
|
||||
pfb.afterPropertiesSet();
|
||||
ITestBean proxy = (ITestBean) pfb.getObject();
|
||||
assertThat(proxy.getName()).isEqualTo("myname");
|
||||
assertThat(proxy.getAge()).isEqualTo(99);
|
||||
proxy.setAge(50);
|
||||
assertThat(proxy.getAge()).isEqualTo(50);
|
||||
proxy.setStringArray(new String[] {"str1", "str2"});
|
||||
assertThat(Arrays.equals(new String[] {"str1", "str2"}, proxy.getStringArray())).isTrue();
|
||||
proxy.setSomeIntegerArray(new Integer[] {1, 2, 3});
|
||||
assertThat(Arrays.equals(new Integer[] {1, 2, 3}, proxy.getSomeIntegerArray())).isTrue();
|
||||
proxy.setNestedIntegerArray(new Integer[][] {{1, 2, 3}, {4, 5, 6}});
|
||||
Integer[][] integerArray = proxy.getNestedIntegerArray();
|
||||
assertThat(Arrays.equals(new Integer[] {1, 2, 3}, integerArray[0])).isTrue();
|
||||
assertThat(Arrays.equals(new Integer[] {4, 5, 6}, integerArray[1])).isTrue();
|
||||
proxy.setSomeIntArray(new int[] {1, 2, 3});
|
||||
assertThat(Arrays.equals(new int[] {1, 2, 3}, proxy.getSomeIntArray())).isTrue();
|
||||
proxy.setNestedIntArray(new int[][] {{1, 2, 3}, {4, 5, 6}});
|
||||
int[][] intArray = proxy.getNestedIntArray();
|
||||
assertThat(Arrays.equals(new int[] {1, 2, 3}, intArray[0])).isTrue();
|
||||
assertThat(Arrays.equals(new int[] {4, 5, 6}, intArray[1])).isTrue();
|
||||
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
proxy.exceptional(new IllegalStateException()));
|
||||
assertThatExceptionOfType(IllegalAccessException.class).isThrownBy(() ->
|
||||
proxy.exceptional(new IllegalAccessException()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void httpInvokerProxyFactoryBeanAndServiceExporterWithIOException() throws Exception {
|
||||
TestBean target = new TestBean("myname", 99);
|
||||
|
||||
final HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter();
|
||||
exporter.setServiceInterface(ITestBean.class);
|
||||
exporter.setService(target);
|
||||
exporter.afterPropertiesSet();
|
||||
|
||||
HttpInvokerProxyFactoryBean pfb = new HttpInvokerProxyFactoryBean();
|
||||
pfb.setServiceInterface(ITestBean.class);
|
||||
pfb.setServiceUrl("https://myurl");
|
||||
|
||||
pfb.setHttpInvokerRequestExecutor((config, invocation) -> { throw new IOException("argh"); });
|
||||
|
||||
pfb.afterPropertiesSet();
|
||||
ITestBean proxy = (ITestBean) pfb.getObject();
|
||||
assertThatExceptionOfType(RemoteAccessException.class)
|
||||
.isThrownBy(() -> proxy.setAge(50))
|
||||
.withCauseInstanceOf(IOException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void httpInvokerProxyFactoryBeanAndServiceExporterWithGzipCompression() {
|
||||
TestBean target = new TestBean("myname", 99);
|
||||
|
||||
final HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter() {
|
||||
@Override
|
||||
protected InputStream decorateInputStream(HttpServletRequest request, InputStream is) throws IOException {
|
||||
if ("gzip".equals(request.getHeader("Compression"))) {
|
||||
return new GZIPInputStream(is);
|
||||
}
|
||||
else {
|
||||
return is;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected OutputStream decorateOutputStream(
|
||||
HttpServletRequest request, HttpServletResponse response, OutputStream os) throws IOException {
|
||||
if ("gzip".equals(request.getHeader("Compression"))) {
|
||||
return new GZIPOutputStream(os);
|
||||
}
|
||||
else {
|
||||
return os;
|
||||
}
|
||||
}
|
||||
};
|
||||
exporter.setServiceInterface(ITestBean.class);
|
||||
exporter.setService(target);
|
||||
exporter.afterPropertiesSet();
|
||||
|
||||
HttpInvokerProxyFactoryBean pfb = new HttpInvokerProxyFactoryBean();
|
||||
pfb.setServiceInterface(ITestBean.class);
|
||||
pfb.setServiceUrl("https://myurl");
|
||||
|
||||
pfb.setHttpInvokerRequestExecutor(new AbstractHttpInvokerRequestExecutor() {
|
||||
@Override
|
||||
protected RemoteInvocationResult doExecuteRequest(
|
||||
HttpInvokerClientConfiguration config, ByteArrayOutputStream baos)
|
||||
throws IOException, ClassNotFoundException {
|
||||
assertThat(config.getServiceUrl()).isEqualTo("https://myurl");
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.addHeader("Compression", "gzip");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
request.setContent(baos.toByteArray());
|
||||
try {
|
||||
exporter.handleRequest(request, response);
|
||||
}
|
||||
catch (ServletException ex) {
|
||||
throw new IOException(ex.toString());
|
||||
}
|
||||
return readRemoteInvocationResult(
|
||||
new ByteArrayInputStream(response.getContentAsByteArray()), config.getCodebaseUrl());
|
||||
}
|
||||
@Override
|
||||
protected OutputStream decorateOutputStream(OutputStream os) throws IOException {
|
||||
return new GZIPOutputStream(os);
|
||||
}
|
||||
@Override
|
||||
protected InputStream decorateInputStream(InputStream is) throws IOException {
|
||||
return new GZIPInputStream(is);
|
||||
}
|
||||
});
|
||||
|
||||
pfb.afterPropertiesSet();
|
||||
ITestBean proxy = (ITestBean) pfb.getObject();
|
||||
assertThat(proxy.getName()).isEqualTo("myname");
|
||||
assertThat(proxy.getAge()).isEqualTo(99);
|
||||
proxy.setAge(50);
|
||||
assertThat(proxy.getAge()).isEqualTo(50);
|
||||
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
proxy.exceptional(new IllegalStateException()));
|
||||
assertThatExceptionOfType(IllegalAccessException.class).isThrownBy(() ->
|
||||
proxy.exceptional(new IllegalAccessException()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void httpInvokerProxyFactoryBeanAndServiceExporterWithWrappedInvocations() {
|
||||
TestBean target = new TestBean("myname", 99);
|
||||
|
||||
final HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter() {
|
||||
@Override
|
||||
protected RemoteInvocation doReadRemoteInvocation(ObjectInputStream ois)
|
||||
throws IOException, ClassNotFoundException {
|
||||
Object obj = ois.readObject();
|
||||
if (!(obj instanceof TestRemoteInvocationWrapper)) {
|
||||
throw new IOException("Deserialized object needs to be assignable to type [" +
|
||||
TestRemoteInvocationWrapper.class.getName() + "]: " + obj);
|
||||
}
|
||||
return ((TestRemoteInvocationWrapper) obj).remoteInvocation;
|
||||
}
|
||||
@Override
|
||||
protected void doWriteRemoteInvocationResult(RemoteInvocationResult result, ObjectOutputStream oos)
|
||||
throws IOException {
|
||||
oos.writeObject(new TestRemoteInvocationResultWrapper(result));
|
||||
}
|
||||
};
|
||||
exporter.setServiceInterface(ITestBean.class);
|
||||
exporter.setService(target);
|
||||
exporter.afterPropertiesSet();
|
||||
|
||||
HttpInvokerProxyFactoryBean pfb = new HttpInvokerProxyFactoryBean();
|
||||
pfb.setServiceInterface(ITestBean.class);
|
||||
pfb.setServiceUrl("https://myurl");
|
||||
|
||||
pfb.setHttpInvokerRequestExecutor(new AbstractHttpInvokerRequestExecutor() {
|
||||
@Override
|
||||
protected RemoteInvocationResult doExecuteRequest(
|
||||
HttpInvokerClientConfiguration config, ByteArrayOutputStream baos) throws Exception {
|
||||
assertThat(config.getServiceUrl()).isEqualTo("https://myurl");
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
request.setContent(baos.toByteArray());
|
||||
exporter.handleRequest(request, response);
|
||||
return readRemoteInvocationResult(
|
||||
new ByteArrayInputStream(response.getContentAsByteArray()), config.getCodebaseUrl());
|
||||
}
|
||||
@Override
|
||||
protected void doWriteRemoteInvocation(RemoteInvocation invocation, ObjectOutputStream oos) throws IOException {
|
||||
oos.writeObject(new TestRemoteInvocationWrapper(invocation));
|
||||
}
|
||||
@Override
|
||||
protected RemoteInvocationResult doReadRemoteInvocationResult(ObjectInputStream ois)
|
||||
throws IOException, ClassNotFoundException {
|
||||
Object obj = ois.readObject();
|
||||
if (!(obj instanceof TestRemoteInvocationResultWrapper)) {
|
||||
throw new IOException("Deserialized object needs to be assignable to type ["
|
||||
+ TestRemoteInvocationResultWrapper.class.getName() + "]: " + obj);
|
||||
}
|
||||
return ((TestRemoteInvocationResultWrapper) obj).remoteInvocationResult;
|
||||
}
|
||||
});
|
||||
|
||||
pfb.afterPropertiesSet();
|
||||
ITestBean proxy = (ITestBean) pfb.getObject();
|
||||
assertThat(proxy.getName()).isEqualTo("myname");
|
||||
assertThat(proxy.getAge()).isEqualTo(99);
|
||||
proxy.setAge(50);
|
||||
assertThat(proxy.getAge()).isEqualTo(50);
|
||||
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
proxy.exceptional(new IllegalStateException()));
|
||||
assertThatExceptionOfType(IllegalAccessException.class).isThrownBy(() ->
|
||||
proxy.exceptional(new IllegalAccessException()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void httpInvokerProxyFactoryBeanAndServiceExporterWithInvocationAttributes() {
|
||||
TestBean target = new TestBean("myname", 99);
|
||||
|
||||
final HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter();
|
||||
exporter.setServiceInterface(ITestBean.class);
|
||||
exporter.setService(target);
|
||||
exporter.setRemoteInvocationExecutor(new DefaultRemoteInvocationExecutor() {
|
||||
@Override
|
||||
public Object invoke(RemoteInvocation invocation, Object targetObject)
|
||||
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
assertThat(invocation.getAttributes()).isNotNull();
|
||||
assertThat(invocation.getAttributes().size()).isEqualTo(1);
|
||||
assertThat(invocation.getAttributes().get("myKey")).isEqualTo("myValue");
|
||||
assertThat(invocation.getAttribute("myKey")).isEqualTo("myValue");
|
||||
return super.invoke(invocation, targetObject);
|
||||
}
|
||||
});
|
||||
exporter.afterPropertiesSet();
|
||||
|
||||
HttpInvokerProxyFactoryBean pfb = new HttpInvokerProxyFactoryBean();
|
||||
pfb.setServiceInterface(ITestBean.class);
|
||||
pfb.setServiceUrl("https://myurl");
|
||||
pfb.setRemoteInvocationFactory(methodInvocation -> {
|
||||
RemoteInvocation invocation = new RemoteInvocation(methodInvocation);
|
||||
invocation.addAttribute("myKey", "myValue");
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
invocation.addAttribute("myKey", "myValue"));
|
||||
assertThat(invocation.getAttributes()).isNotNull();
|
||||
assertThat(invocation.getAttributes().size()).isEqualTo(1);
|
||||
assertThat(invocation.getAttributes().get("myKey")).isEqualTo("myValue");
|
||||
assertThat(invocation.getAttribute("myKey")).isEqualTo("myValue");
|
||||
return invocation;
|
||||
});
|
||||
|
||||
pfb.setHttpInvokerRequestExecutor(new AbstractHttpInvokerRequestExecutor() {
|
||||
@Override
|
||||
protected RemoteInvocationResult doExecuteRequest(
|
||||
HttpInvokerClientConfiguration config, ByteArrayOutputStream baos) throws Exception {
|
||||
assertThat(config.getServiceUrl()).isEqualTo("https://myurl");
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
request.setContent(baos.toByteArray());
|
||||
exporter.handleRequest(request, response);
|
||||
return readRemoteInvocationResult(
|
||||
new ByteArrayInputStream(response.getContentAsByteArray()), config.getCodebaseUrl());
|
||||
}
|
||||
});
|
||||
|
||||
pfb.afterPropertiesSet();
|
||||
ITestBean proxy = (ITestBean) pfb.getObject();
|
||||
assertThat(proxy.getName()).isEqualTo("myname");
|
||||
assertThat(proxy.getAge()).isEqualTo(99);
|
||||
}
|
||||
|
||||
@Test
|
||||
void httpInvokerProxyFactoryBeanAndServiceExporterWithCustomInvocationObject() {
|
||||
TestBean target = new TestBean("myname", 99);
|
||||
|
||||
final HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter();
|
||||
exporter.setServiceInterface(ITestBean.class);
|
||||
exporter.setService(target);
|
||||
exporter.setRemoteInvocationExecutor(new DefaultRemoteInvocationExecutor() {
|
||||
@Override
|
||||
public Object invoke(RemoteInvocation invocation, Object targetObject)
|
||||
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
boolean condition = invocation instanceof TestRemoteInvocation;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(invocation.getAttributes()).isNull();
|
||||
assertThat(invocation.getAttribute("myKey")).isNull();
|
||||
return super.invoke(invocation, targetObject);
|
||||
}
|
||||
});
|
||||
exporter.afterPropertiesSet();
|
||||
|
||||
HttpInvokerProxyFactoryBean pfb = new HttpInvokerProxyFactoryBean();
|
||||
pfb.setServiceInterface(ITestBean.class);
|
||||
pfb.setServiceUrl("https://myurl");
|
||||
pfb.setRemoteInvocationFactory(methodInvocation -> {
|
||||
RemoteInvocation invocation = new TestRemoteInvocation(methodInvocation);
|
||||
assertThat(invocation.getAttributes()).isNull();
|
||||
assertThat(invocation.getAttribute("myKey")).isNull();
|
||||
return invocation;
|
||||
});
|
||||
|
||||
pfb.setHttpInvokerRequestExecutor(new AbstractHttpInvokerRequestExecutor() {
|
||||
@Override
|
||||
protected RemoteInvocationResult doExecuteRequest(
|
||||
HttpInvokerClientConfiguration config, ByteArrayOutputStream baos) throws Exception {
|
||||
assertThat(config.getServiceUrl()).isEqualTo("https://myurl");
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
request.setContent(baos.toByteArray());
|
||||
exporter.handleRequest(request, response);
|
||||
return readRemoteInvocationResult(
|
||||
new ByteArrayInputStream(response.getContentAsByteArray()), config.getCodebaseUrl());
|
||||
}
|
||||
});
|
||||
|
||||
pfb.afterPropertiesSet();
|
||||
ITestBean proxy = (ITestBean) pfb.getObject();
|
||||
assertThat(proxy.getName()).isEqualTo("myname");
|
||||
assertThat(proxy.getAge()).isEqualTo(99);
|
||||
}
|
||||
|
||||
@Test
|
||||
void httpInvokerWithSpecialLocalMethods() {
|
||||
String serviceUrl = "https://myurl";
|
||||
HttpInvokerProxyFactoryBean pfb = new HttpInvokerProxyFactoryBean();
|
||||
pfb.setServiceInterface(ITestBean.class);
|
||||
pfb.setServiceUrl(serviceUrl);
|
||||
|
||||
pfb.setHttpInvokerRequestExecutor((config, invocation) -> { throw new IOException("argh"); });
|
||||
|
||||
pfb.afterPropertiesSet();
|
||||
ITestBean proxy = (ITestBean) pfb.getObject();
|
||||
|
||||
// shouldn't go through to remote service
|
||||
assertThat(proxy.toString().contains("HTTP invoker")).isTrue();
|
||||
assertThat(proxy.toString().contains(serviceUrl)).isTrue();
|
||||
assertThat(proxy.hashCode()).isEqualTo(proxy.hashCode());
|
||||
assertThat(proxy.equals(proxy)).isTrue();
|
||||
|
||||
// should go through
|
||||
assertThatExceptionOfType(RemoteAccessException.class)
|
||||
.isThrownBy(() -> proxy.setAge(50))
|
||||
.withCauseInstanceOf(IOException.class);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class TestRemoteInvocation extends RemoteInvocation {
|
||||
|
||||
TestRemoteInvocation(MethodInvocation methodInvocation) {
|
||||
super(methodInvocation);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class TestRemoteInvocationWrapper implements Serializable {
|
||||
|
||||
private final RemoteInvocation remoteInvocation;
|
||||
|
||||
TestRemoteInvocationWrapper(RemoteInvocation remoteInvocation) {
|
||||
this.remoteInvocation = remoteInvocation;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class TestRemoteInvocationResultWrapper implements Serializable {
|
||||
|
||||
private final RemoteInvocationResult remoteInvocationResult;
|
||||
|
||||
TestRemoteInvocationResultWrapper(RemoteInvocationResult remoteInvocationResult) {
|
||||
this.remoteInvocationResult = remoteInvocationResult;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.remoting.jaxws;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
import javax.xml.ws.BindingProvider;
|
||||
import javax.xml.ws.Service;
|
||||
import javax.xml.ws.WebServiceClient;
|
||||
import javax.xml.ws.WebServiceException;
|
||||
import javax.xml.ws.WebServiceFeature;
|
||||
import javax.xml.ws.WebServiceRef;
|
||||
import javax.xml.ws.soap.AddressingFeature;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.remoting.RemoteAccessException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
*/
|
||||
public class JaxWsSupportTests {
|
||||
|
||||
@Test
|
||||
public void testJaxWsPortAccess() throws Exception {
|
||||
doTestJaxWsPortAccess((WebServiceFeature[]) null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJaxWsPortAccessWithFeature() throws Exception {
|
||||
doTestJaxWsPortAccess(new AddressingFeature());
|
||||
}
|
||||
|
||||
private void doTestJaxWsPortAccess(WebServiceFeature... features) throws Exception {
|
||||
GenericApplicationContext ac = new GenericApplicationContext();
|
||||
|
||||
GenericBeanDefinition serviceDef = new GenericBeanDefinition();
|
||||
serviceDef.setBeanClass(OrderServiceImpl.class);
|
||||
ac.registerBeanDefinition("service", serviceDef);
|
||||
|
||||
GenericBeanDefinition exporterDef = new GenericBeanDefinition();
|
||||
exporterDef.setBeanClass(SimpleJaxWsServiceExporter.class);
|
||||
exporterDef.getPropertyValues().add("baseAddress", "http://localhost:9999/");
|
||||
ac.registerBeanDefinition("exporter", exporterDef);
|
||||
|
||||
GenericBeanDefinition clientDef = new GenericBeanDefinition();
|
||||
clientDef.setBeanClass(JaxWsPortProxyFactoryBean.class);
|
||||
clientDef.getPropertyValues().add("wsdlDocumentUrl", "http://localhost:9999/OrderService?wsdl");
|
||||
clientDef.getPropertyValues().add("namespaceUri", "http://jaxws.remoting.springframework.org/");
|
||||
clientDef.getPropertyValues().add("username", "juergen");
|
||||
clientDef.getPropertyValues().add("password", "hoeller");
|
||||
clientDef.getPropertyValues().add("serviceName", "OrderService");
|
||||
clientDef.getPropertyValues().add("serviceInterface", OrderService.class);
|
||||
clientDef.getPropertyValues().add("lookupServiceOnStartup", Boolean.FALSE);
|
||||
if (features != null) {
|
||||
clientDef.getPropertyValues().add("portFeatures", features);
|
||||
}
|
||||
ac.registerBeanDefinition("client", clientDef);
|
||||
|
||||
GenericBeanDefinition serviceFactoryDef = new GenericBeanDefinition();
|
||||
serviceFactoryDef.setBeanClass(LocalJaxWsServiceFactoryBean.class);
|
||||
serviceFactoryDef.getPropertyValues().add("wsdlDocumentUrl", "http://localhost:9999/OrderService?wsdl");
|
||||
serviceFactoryDef.getPropertyValues().add("namespaceUri", "http://jaxws.remoting.springframework.org/");
|
||||
serviceFactoryDef.getPropertyValues().add("serviceName", "OrderService");
|
||||
ac.registerBeanDefinition("orderService", serviceFactoryDef);
|
||||
|
||||
ac.registerBeanDefinition("accessor", new RootBeanDefinition(ServiceAccessor.class));
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(ac);
|
||||
|
||||
try {
|
||||
ac.refresh();
|
||||
|
||||
OrderService orderService = ac.getBean("client", OrderService.class);
|
||||
boolean condition = orderService instanceof BindingProvider;
|
||||
assertThat(condition).isTrue();
|
||||
((BindingProvider) orderService).getRequestContext();
|
||||
|
||||
String order = orderService.getOrder(1000);
|
||||
assertThat(order).isEqualTo("order 1000");
|
||||
assertThatExceptionOfType(Exception.class).isThrownBy(() ->
|
||||
orderService.getOrder(0))
|
||||
.matches(ex -> ex instanceof OrderNotFoundException ||
|
||||
ex instanceof RemoteAccessException);
|
||||
// ignore RemoteAccessException as probably setup issue with JAX-WS provider vs JAXB
|
||||
|
||||
ServiceAccessor serviceAccessor = ac.getBean("accessor", ServiceAccessor.class);
|
||||
order = serviceAccessor.orderService.getOrder(1000);
|
||||
assertThat(order).isEqualTo("order 1000");
|
||||
assertThatExceptionOfType(Exception.class).isThrownBy(() ->
|
||||
serviceAccessor.orderService.getOrder(0))
|
||||
.matches(ex -> ex instanceof OrderNotFoundException ||
|
||||
ex instanceof WebServiceException);
|
||||
// ignore WebServiceException as probably setup issue with JAX-WS provider vs JAXB
|
||||
}
|
||||
catch (BeanCreationException ex) {
|
||||
if ("exporter".equals(ex.getBeanName()) && ex.getRootCause() instanceof ClassNotFoundException) {
|
||||
// ignore - probably running on JDK without the JAX-WS impl present
|
||||
}
|
||||
else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
ac.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class ServiceAccessor {
|
||||
|
||||
@WebServiceRef
|
||||
public OrderService orderService;
|
||||
|
||||
public OrderService myService;
|
||||
|
||||
@WebServiceRef(value = OrderServiceService.class, wsdlLocation = "http://localhost:9999/OrderService?wsdl")
|
||||
public void setMyService(OrderService myService) {
|
||||
this.myService = myService;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@WebServiceClient(targetNamespace = "http://jaxws.remoting.springframework.org/", name="OrderService")
|
||||
public static class OrderServiceService extends Service {
|
||||
|
||||
public OrderServiceService() throws MalformedURLException {
|
||||
super(new URL("http://localhost:9999/OrderService?wsdl"),
|
||||
new QName("http://jaxws.remoting.springframework.org/", "OrderService"));
|
||||
}
|
||||
|
||||
public OrderServiceService(URL wsdlDocumentLocation, QName serviceName) {
|
||||
super(wsdlDocumentLocation, serviceName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.remoting.jaxws;
|
||||
|
||||
import javax.xml.ws.WebFault;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
@WebFault
|
||||
@SuppressWarnings("serial")
|
||||
public class OrderNotFoundException extends Exception {
|
||||
|
||||
private final String faultInfo;
|
||||
|
||||
public OrderNotFoundException(String message) {
|
||||
super(message);
|
||||
this.faultInfo = null;
|
||||
}
|
||||
|
||||
public OrderNotFoundException(String message, String faultInfo) {
|
||||
super(message);
|
||||
this.faultInfo = faultInfo;
|
||||
}
|
||||
|
||||
public String getFaultInfo() {
|
||||
return this.faultInfo;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.remoting.jaxws;
|
||||
|
||||
import javax.jws.WebService;
|
||||
import javax.jws.soap.SOAPBinding;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
@WebService
|
||||
@SOAPBinding(style = SOAPBinding.Style.RPC)
|
||||
public interface OrderService {
|
||||
|
||||
String getOrder(int id) throws OrderNotFoundException;
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.remoting.jaxws;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.jws.WebService;
|
||||
import javax.xml.ws.WebServiceContext;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
@WebService(serviceName="OrderService", portName="OrderService",
|
||||
endpointInterface = "org.springframework.remoting.jaxws.OrderService")
|
||||
public class OrderServiceImpl implements OrderService {
|
||||
|
||||
@Resource
|
||||
private WebServiceContext webServiceContext;
|
||||
|
||||
@Override
|
||||
public String getOrder(int id) throws OrderNotFoundException {
|
||||
Assert.notNull(this.webServiceContext, "WebServiceContext has not been injected");
|
||||
if (id == 0) {
|
||||
throw new OrderNotFoundException("Order 0 not found");
|
||||
}
|
||||
return "order " + id;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user