Browse Source

REPORT-95576 spring framework 高危漏洞,需要删除类

feature/x
Yuan.Wang 2 years ago
parent
commit
65dfc5202d
  1. 194
      fine-spring/src/main/java/com/fr/third/springframework/remoting/caucho/BurlapClientInterceptor.java
  2. 97
      fine-spring/src/main/java/com/fr/third/springframework/remoting/caucho/BurlapExporter.java
  3. 73
      fine-spring/src/main/java/com/fr/third/springframework/remoting/caucho/BurlapProxyFactoryBean.java
  4. 76
      fine-spring/src/main/java/com/fr/third/springframework/remoting/caucho/BurlapServiceExporter.java
  5. 297
      fine-spring/src/main/java/com/fr/third/springframework/remoting/caucho/HessianClientInterceptor.java
  6. 247
      fine-spring/src/main/java/com/fr/third/springframework/remoting/caucho/HessianExporter.java
  7. 70
      fine-spring/src/main/java/com/fr/third/springframework/remoting/caucho/HessianProxyFactoryBean.java
  8. 73
      fine-spring/src/main/java/com/fr/third/springframework/remoting/caucho/HessianServiceExporter.java
  9. 79
      fine-spring/src/main/java/com/fr/third/springframework/remoting/caucho/SimpleBurlapServiceExporter.java
  10. 77
      fine-spring/src/main/java/com/fr/third/springframework/remoting/caucho/SimpleHessianServiceExporter.java
  11. 10
      fine-spring/src/main/java/com/fr/third/springframework/remoting/caucho/package-info.java
  12. 299
      fine-spring/src/main/java/com/fr/third/springframework/remoting/httpinvoker/AbstractHttpInvokerRequestExecutor.java
  13. 418
      fine-spring/src/main/java/com/fr/third/springframework/remoting/httpinvoker/HttpComponentsHttpInvokerRequestExecutor.java
  14. 42
      fine-spring/src/main/java/com/fr/third/springframework/remoting/httpinvoker/HttpInvokerClientConfiguration.java
  15. 233
      fine-spring/src/main/java/com/fr/third/springframework/remoting/httpinvoker/HttpInvokerClientInterceptor.java
  16. 86
      fine-spring/src/main/java/com/fr/third/springframework/remoting/httpinvoker/HttpInvokerProxyFactoryBean.java
  17. 59
      fine-spring/src/main/java/com/fr/third/springframework/remoting/httpinvoker/HttpInvokerRequestExecutor.java
  18. 225
      fine-spring/src/main/java/com/fr/third/springframework/remoting/httpinvoker/HttpInvokerServiceExporter.java
  19. 232
      fine-spring/src/main/java/com/fr/third/springframework/remoting/httpinvoker/SimpleHttpInvokerRequestExecutor.java
  20. 184
      fine-spring/src/main/java/com/fr/third/springframework/remoting/httpinvoker/SimpleHttpInvokerServiceExporter.java
  21. 11
      fine-spring/src/main/java/com/fr/third/springframework/remoting/httpinvoker/package-info.java
  22. 119
      fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/CodebaseAwareObjectInputStream.java
  23. 510
      fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/JndiRmiClientInterceptor.java
  24. 102
      fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/JndiRmiProxyFactoryBean.java
  25. 149
      fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/JndiRmiServiceExporter.java
  26. 181
      fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/RemoteInvocationSerializingExporter.java
  27. 76
      fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/RmiBasedExporter.java
  28. 416
      fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/RmiClientInterceptor.java
  29. 193
      fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/RmiClientInterceptorUtils.java
  30. 59
      fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/RmiInvocationHandler.java
  31. 77
      fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/RmiInvocationWrapper.java
  32. 91
      fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/RmiProxyFactoryBean.java
  33. 314
      fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/RmiRegistryFactoryBean.java
  34. 462
      fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/RmiServiceExporter.java
  35. 6
      fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/package-info.java

194
fine-spring/src/main/java/com/fr/third/springframework/remoting/caucho/BurlapClientInterceptor.java

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

97
fine-spring/src/main/java/com/fr/third/springframework/remoting/caucho/BurlapExporter.java

@ -1,97 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.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 com.fr.third.springframework.remoting.caucho;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import com.caucho.burlap.io.BurlapInput;
import com.caucho.burlap.io.BurlapOutput;
import com.caucho.burlap.server.BurlapSkeleton;
import com.fr.third.springframework.beans.factory.InitializingBean;
import com.fr.third.springframework.remoting.support.RemoteExporter;
import com.fr.third.springframework.util.Assert;
/**
* General stream-based protocol exporter for a Burlap endpoint.
*
* <p>Burlap is a slim, XML-based RPC protocol.
* For information on Burlap, see the
* <a href="https://www.caucho.com/burlap">Burlap website</a>.
* This exporter requires Burlap 3.x.
*
* @author Juergen Hoeller
* @since 2.5.1
* @see #invoke(java.io.InputStream, java.io.OutputStream)
* @see BurlapServiceExporter
* @see SimpleBurlapServiceExporter
* @deprecated as of Spring 4.0, since Burlap hasn't evolved in years
* and is effectively retired (in contrast to its sibling Hessian)
*/
@Deprecated
public class BurlapExporter extends RemoteExporter implements InitializingBean {
private BurlapSkeleton skeleton;
@Override
public void afterPropertiesSet() {
prepare();
}
/**
* Initialize this service exporter.
*/
public void prepare() {
checkService();
checkServiceInterface();
this.skeleton = new BurlapSkeleton(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, "Burlap exporter has not been initialized");
ClassLoader originalClassLoader = overrideThreadContextClassLoader();
try {
this.skeleton.invoke(new BurlapInput(inputStream), new BurlapOutput(outputStream));
}
finally {
try {
inputStream.close();
}
catch (IOException ex) {
// ignore
}
try {
outputStream.close();
}
catch (IOException ex) {
// ignore
}
resetThreadContextClassLoader(originalClassLoader);
}
}
}

73
fine-spring/src/main/java/com/fr/third/springframework/remoting/caucho/BurlapProxyFactoryBean.java

@ -1,73 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.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 com.fr.third.springframework.remoting.caucho;
import com.fr.third.springframework.aop.framework.ProxyFactory;
import com.fr.third.springframework.beans.factory.FactoryBean;
/**
* {@link FactoryBean} for Burlap proxies. Exposes the proxied service
* for use as a bean reference, using the specified service interface.
*
* <p>Burlap is a slim, XML-based RPC protocol.
* For information on Burlap, see the
* <a href="https://www.caucho.com/burlap">Burlap website</a>
*
* <p>The service URL must be an HTTP URL exposing a Burlap service.
* For details, see the {@link BurlapClientInterceptor} javadoc.
*
* @author Juergen Hoeller
* @since 13.05.2003
* @see #setServiceInterface
* @see #setServiceUrl
* @see BurlapClientInterceptor
* @see BurlapServiceExporter
* @see com.fr.third.springframework.remoting.caucho.HessianProxyFactoryBean
* @see com.fr.third.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean
* @see com.fr.third.springframework.remoting.rmi.RmiProxyFactoryBean
* @deprecated as of Spring 4.0, since Burlap hasn't evolved in years
* and is effectively retired (in contrast to its sibling Hessian)
*/
@Deprecated
public class BurlapProxyFactoryBean extends BurlapClientInterceptor implements FactoryBean<Object> {
private Object serviceProxy;
@Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
this.serviceProxy = new ProxyFactory(getServiceInterface(), this).getProxy(getBeanClassLoader());
}
@Override
public Object getObject() {
return this.serviceProxy;
}
@Override
public Class<?> getObjectType() {
return getServiceInterface();
}
@Override
public boolean isSingleton() {
return true;
}
}

76
fine-spring/src/main/java/com/fr/third/springframework/remoting/caucho/BurlapServiceExporter.java

@ -1,76 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.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 com.fr.third.springframework.remoting.caucho;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.fr.third.springframework.web.HttpRequestHandler;
import com.fr.third.springframework.web.HttpRequestMethodNotSupportedException;
import com.fr.third.springframework.web.util.NestedServletException;
/**
* Servlet-API-based HTTP request handler that exports the specified service bean
* as Burlap service endpoint, accessible via a Burlap proxy.
*
* <p><b>Note:</b> Spring also provides an alternative version of this exporter,
* for Sun's JRE 1.6 HTTP server: {@link SimpleBurlapServiceExporter}.
*
* <p>Burlap is a slim, XML-based RPC protocol.
* For information on Burlap, see the
* <a href="https://www.caucho.com/burlap">Burlap website</a>.
* This exporter requires Burlap 3.x.
*
* <p>Note: Burlap services exported with this class can be accessed by
* any Burlap client, as there isn't any special handling involved.
*
* @author Juergen Hoeller
* @since 13.05.2003
* @see BurlapClientInterceptor
* @see BurlapProxyFactoryBean
* @see com.fr.third.springframework.remoting.caucho.HessianServiceExporter
* @see com.fr.third.springframework.remoting.httpinvoker.HttpInvokerServiceExporter
* @see com.fr.third.springframework.remoting.rmi.RmiServiceExporter
* @deprecated as of Spring 4.0, since Burlap hasn't evolved in years
* and is effectively retired (in contrast to its sibling Hessian)
*/
@Deprecated
public class BurlapServiceExporter extends BurlapExporter implements HttpRequestHandler {
/**
* Processes the incoming Burlap request and creates a Burlap 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"}, "BurlapServiceExporter only supports POST requests");
}
try {
invoke(request.getInputStream(), response.getOutputStream());
}
catch (Throwable ex) {
throw new NestedServletException("Burlap skeleton invocation failed", ex);
}
}
}

297
fine-spring/src/main/java/com/fr/third/springframework/remoting/caucho/HessianClientInterceptor.java

@ -1,297 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.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 com.fr.third.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 com.fr.third.springframework.remoting.RemoteAccessException;
import com.fr.third.springframework.remoting.RemoteConnectFailureException;
import com.fr.third.springframework.remoting.RemoteLookupFailureException;
import com.fr.third.springframework.remoting.RemoteProxyFailureException;
import com.fr.third.springframework.remoting.support.UrlBasedRemoteAccessor;
import com.fr.third.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
*/
public class HessianClientInterceptor extends UrlBasedRemoteAccessor implements MethodInterceptor {
private HessianProxyFactory proxyFactory = new HessianProxyFactory();
private Object hessianProxy;
/**
* Set the HessianProxyFactory instance to use.
* If not specified, a default HessianProxyFactory will be created.
* <p>Allows to use an externally configured factory instance,
* in particular a custom HessianProxyFactory subclass.
*/
public void setProxyFactory(HessianProxyFactory proxyFactory) {
this.proxyFactory = (proxyFactory != null ? proxyFactory : new HessianProxyFactory());
}
/**
* Specify the Hessian SerializerFactory to use.
* <p>This will typically be passed in as an inner bean definition
* of type {@code com.caucho.hessian.io.SerializerFactory},
* 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
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);
}
}
}

247
fine-spring/src/main/java/com/fr/third/springframework/remoting/caucho/HessianExporter.java

@ -1,247 +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 com.fr.third.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 com.fr.third.springframework.beans.factory.InitializingBean;
import com.fr.third.springframework.remoting.support.RemoteExporter;
import com.fr.third.springframework.util.Assert;
import com.fr.third.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
* @see SimpleHessianServiceExporter
*/
public class HessianExporter extends RemoteExporter implements InitializingBean {
public static final String CONTENT_TYPE_HESSIAN = "application/x-hessian";
private SerializerFactory serializerFactory = new SerializerFactory();
private HessianRemoteResolver remoteResolver;
private Log debugLogger;
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(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()) {
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);
}
if (this.serializerFactory != null) {
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);
}
}
}

70
fine-spring/src/main/java/com/fr/third/springframework/remoting/caucho/HessianProxyFactoryBean.java

@ -1,70 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.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 com.fr.third.springframework.remoting.caucho;
import com.fr.third.springframework.aop.framework.ProxyFactory;
import com.fr.third.springframework.beans.factory.FactoryBean;
/**
* {@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 com.fr.third.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean
* @see com.fr.third.springframework.remoting.rmi.RmiProxyFactoryBean
*/
public class HessianProxyFactoryBean extends HessianClientInterceptor implements FactoryBean<Object> {
private Object serviceProxy;
@Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
this.serviceProxy = new ProxyFactory(getServiceInterface(), this).getProxy(getBeanClassLoader());
}
@Override
public Object getObject() {
return this.serviceProxy;
}
@Override
public Class<?> getObjectType() {
return getServiceInterface();
}
@Override
public boolean isSingleton() {
return true;
}
}

73
fine-spring/src/main/java/com/fr/third/springframework/remoting/caucho/HessianServiceExporter.java

@ -1,73 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.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 com.fr.third.springframework.remoting.caucho;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.fr.third.springframework.web.HttpRequestHandler;
import com.fr.third.springframework.web.HttpRequestMethodNotSupportedException;
import com.fr.third.springframework.web.util.NestedServletException;
/**
* Servlet-API-based HTTP request handler that exports the specified service bean
* as Hessian service endpoint, accessible via a Hessian proxy.
*
* <p><b>Note:</b> Spring also provides an alternative version of this exporter,
* for Sun's JRE 1.6 HTTP server: {@link SimpleHessianServiceExporter}.
*
* <p>Hessian is a slim, binary RPC protocol.
* For information on Hessian, see the
* <a href="http://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 com.fr.third.springframework.remoting.httpinvoker.HttpInvokerServiceExporter
* @see com.fr.third.springframework.remoting.rmi.RmiServiceExporter
*/
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);
}
}
}

79
fine-spring/src/main/java/com/fr/third/springframework/remoting/caucho/SimpleBurlapServiceExporter.java

@ -1,79 +0,0 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.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 com.fr.third.springframework.remoting.caucho;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.fr.third.springframework.lang.UsesSunHttpServer;
import com.fr.third.springframework.util.FileCopyUtils;
/**
* HTTP request handler that exports the specified service bean as
* Burlap service endpoint, accessible via a Burlap proxy.
* Designed for Sun's JRE 1.6 HTTP server, implementing the
* {@link com.sun.net.httpserver.HttpHandler} interface.
*
* <p>Burlap is a slim, XML-based RPC protocol.
* For information on Burlap, see the
* <a href="https://www.caucho.com/burlap">Burlap website</a>.
* This exporter requires Burlap 3.x.
*
* <p>Note: Burlap services exported with this class can be accessed by
* any Burlap client, as there isn't any special handling involved.
*
* @author Juergen Hoeller
* @since 2.5.1
* @see com.fr.third.springframework.remoting.caucho.BurlapClientInterceptor
* @see com.fr.third.springframework.remoting.caucho.BurlapProxyFactoryBean
* @see SimpleHessianServiceExporter
* @see com.fr.third.springframework.remoting.httpinvoker.SimpleHttpInvokerServiceExporter
* @deprecated as of Spring 4.0, since Burlap hasn't evolved in years
* and is effectively retired (in contrast to its sibling Hessian)
*/
@Deprecated
@UsesSunHttpServer
public class SimpleBurlapServiceExporter extends BurlapExporter implements HttpHandler {
/**
* Processes the incoming Burlap request and creates a Burlap 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("Burlap skeleton invocation failed", ex);
}
exchange.sendResponseHeaders(200, output.size());
FileCopyUtils.copy(output.toByteArray(), exchange.getResponseBody());
}
}

77
fine-spring/src/main/java/com/fr/third/springframework/remoting/caucho/SimpleHessianServiceExporter.java

@ -1,77 +0,0 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.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 com.fr.third.springframework.remoting.caucho;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.fr.third.springframework.lang.UsesSunHttpServer;
import com.fr.third.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 com.fr.third.springframework.remoting.caucho.HessianClientInterceptor
* @see com.fr.third.springframework.remoting.caucho.HessianProxyFactoryBean
* @see com.fr.third.springframework.remoting.httpinvoker.SimpleHttpInvokerServiceExporter
*/
@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());
}
}

10
fine-spring/src/main/java/com/fr/third/springframework/remoting/caucho/package-info.java

@ -1,10 +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>
*/
package com.fr.third.springframework.remoting.caucho;

299
fine-spring/src/main/java/com/fr/third/springframework/remoting/httpinvoker/AbstractHttpInvokerRequestExecutor.java

@ -1,299 +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 com.fr.third.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 com.fr.third.springframework.beans.factory.BeanClassLoaderAware;
import com.fr.third.springframework.remoting.rmi.CodebaseAwareObjectInputStream;
import com.fr.third.springframework.remoting.support.RemoteInvocation;
import com.fr.third.springframework.remoting.support.RemoteInvocationResult;
import com.fr.third.springframework.util.Assert;
import com.fr.third.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
*/
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;
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.
*/
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 {
ObjectOutputStream oos = new ObjectOutputStream(decorateOutputStream(os));
try {
doWriteRemoteInvocation(invocation, oos);
}
finally {
oos.close();
}
}
/**
* Return the OutputStream to use for writing remote invocations,
* potentially decorating the given original OutputStream.
* <p>The default implementation returns the given stream as-is.
* Can be overridden, for example, for custom encryption or compression.
* @param os the original OutputStream
* @return the potentially decorated OutputStream
*/
protected OutputStream decorateOutputStream(OutputStream os) throws IOException {
return os;
}
/**
* Perform the actual writing of the given invocation object to the
* given ObjectOutputStream.
* <p>The default implementation simply calls {@code writeObject}.
* 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, String codebaseUrl)
throws IOException, ClassNotFoundException {
ObjectInputStream ois = createObjectInputStream(decorateInputStream(is), codebaseUrl);
try {
return doReadRemoteInvocationResult(ois);
}
finally {
ois.close();
}
}
/**
* Return the InputStream to use for reading remote invocation results,
* potentially decorating the given original InputStream.
* <p>The default implementation returns the given stream as-is.
* Can be overridden, for example, for custom encryption or compression.
* @param is the original InputStream
* @return the potentially decorated InputStream
*/
protected InputStream decorateInputStream(InputStream is) throws IOException {
return is;
}
/**
* Create an ObjectInputStream for the given InputStream and codebase.
* The default implementation creates a CodebaseAwareObjectInputStream.
* @param is the InputStream to read from
* @param codebaseUrl the codebase URL to load classes from if not found locally
* (can be {@code null})
* @return the new ObjectInputStream instance to use
* @throws IOException if creation of the ObjectInputStream failed
* @see com.fr.third.springframework.remoting.rmi.CodebaseAwareObjectInputStream
*/
protected ObjectInputStream createObjectInputStream(InputStream is, String codebaseUrl) throws IOException {
return new CodebaseAwareObjectInputStream(is, getBeanClassLoader(), codebaseUrl);
}
/**
* Perform the actual reading of an invocation object from the
* given ObjectInputStream.
* <p>The default implementation simply calls {@code readObject}.
* 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;
}
}

418
fine-spring/src/main/java/com/fr/third/springframework/remoting/httpinvoker/HttpComponentsHttpInvokerRequestExecutor.java

@ -1,418 +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 com.fr.third.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 com.fr.third.springframework.context.i18n.LocaleContext;
import com.fr.third.springframework.context.i18n.LocaleContextHolder;
import com.fr.third.springframework.remoting.support.RemoteInvocationResult;
import com.fr.third.springframework.util.Assert;
import com.fr.third.springframework.util.ClassUtils;
import com.fr.third.springframework.util.StringUtils;
/**
* {@link com.fr.third.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 com.fr.third.springframework.remoting.httpinvoker.SimpleHttpInvokerRequestExecutor
*/
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 static Class<?> abstractHttpClientClass;
static {
try {
// Looking for AbstractHttpClient class (deprecated as of HttpComponents 4.3)
abstractHttpClientClass = ClassUtils.forName("org.apache.http.impl.client.AbstractHttpClient",
HttpComponentsHttpInvokerRequestExecutor.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
// Probably removed from HttpComponents in the meantime...
}
}
private HttpClient httpClient;
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, 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();
setLegacyConnectionTimeout(getHttpClient(), timeout);
}
/**
* Apply the specified connection timeout to deprecated {@link HttpClient}
* implementations.
* <p>As of HttpClient 4.3, default parameters have to be exposed through a
* {@link RequestConfig} instance instead of setting the parameters on the
* client. Unfortunately, this behavior is not backward-compatible and older
* {@link HttpClient} implementations will ignore the {@link RequestConfig}
* object set in the context.
* <p>If the specified client is an older implementation, we set the custom
* connection timeout through the deprecated API. Otherwise, we just return
* as it is set through {@link RequestConfig} with newer clients.
* @param client the client to configure
* @param timeout the custom connection timeout
*/
@SuppressWarnings("deprecation")
private void setLegacyConnectionTimeout(HttpClient client, int timeout) {
if (abstractHttpClientClass != null && abstractHttpClientClass.isInstance(client)) {
client.getParams().setIntParameter(org.apache.http.params.CoreConnectionPNames.CONNECTION_TIMEOUT, timeout);
}
}
/**
* 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();
setLegacySocketTimeout(getHttpClient(), timeout);
}
/**
* Apply the specified socket timeout to deprecated {@link HttpClient}
* implementations. See {@link #setLegacyConnectionTimeout}.
* @param client the client to configure
* @param timeout the custom socket timeout
* @see #setLegacyConnectionTimeout
*/
@SuppressWarnings("deprecation")
private void setLegacySocketTimeout(HttpClient client, int timeout) {
if (abstractHttpClientClass != null && abstractHttpClientClass.isInstance(client)) {
client.getParams().setIntParameter(org.apache.http.params.CoreConnectionPNames.SO_TIMEOUT, timeout);
}
}
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 a 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, StringUtils.toLanguageTag(locale));
}
}
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
*/
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));
}
}

42
fine-spring/src/main/java/com/fr/third/springframework/remoting/httpinvoker/HttpInvokerClientConfiguration.java

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

233
fine-spring/src/main/java/com/fr/third/springframework/remoting/httpinvoker/HttpInvokerClientInterceptor.java

@ -1,233 +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 com.fr.third.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 com.fr.third.springframework.aop.support.AopUtils;
import com.fr.third.springframework.remoting.RemoteAccessException;
import com.fr.third.springframework.remoting.RemoteConnectFailureException;
import com.fr.third.springframework.remoting.RemoteInvocationFailureException;
import com.fr.third.springframework.remoting.support.RemoteInvocation;
import com.fr.third.springframework.remoting.support.RemoteInvocationBasedAccessor;
import com.fr.third.springframework.remoting.support.RemoteInvocationResult;
/**
* {@link org.aopalliance.intercept.MethodInterceptor} for accessing an
* HTTP invoker service. The service URL must be an HTTP URL exposing
* an HTTP invoker service.
*
* <p>Serializes remote invocation objects and deserializes remote invocation
* result objects. Uses Java serialization just like RMI, but provides the
* same ease of setup as Caucho's HTTP-based Hessian and Burlap protocols.
*
* <P>HTTP invoker is a very extensible and customizable protocol.
* It supports the RemoteInvocationFactory mechanism, like RMI invoker,
* allowing to include additional invocation attributes (for example,
* a security context). Furthermore, it allows to customize request
* execution via the {@link HttpInvokerRequestExecutor} strategy.
*
* <p>Can use the JDK's {@link java.rmi.server.RMIClassLoader} to load classes
* from a given {@link #setCodebaseUrl codebase}, performing on-demand dynamic
* code download from a remote location. The codebase can consist of multiple
* URLs, separated by spaces. Note that RMIClassLoader requires a SecurityManager
* to be set, analogous to when using dynamic class download with standard RMI!
* (See the RMI documentation for details.)
*
* <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
*/
public class HttpInvokerClientInterceptor extends RemoteInvocationBasedAccessor
implements MethodInterceptor, HttpInvokerClientConfiguration {
private String codebaseUrl;
private HttpInvokerRequestExecutor httpInvokerRequestExecutor;
/**
* Set the codebase URL to download classes from if not found locally.
* Can consists of multiple URLs, separated by spaces.
* <p>Follows RMI's codebase conventions for dynamic class download.
* In contrast to RMI, where the server determines the URL for class download
* (via the "java.rmi.server.codebase" system property), it's the client
* that determines the codebase URL here. The server will usually be the
* same as for the service URL, just pointing to a different path there.
* @see #setServiceUrl
* @see com.fr.third.springframework.remoting.rmi.CodebaseAwareObjectInputStream
* @see java.rmi.server.RMIClassLoader
*/
public void setCodebaseUrl(String codebaseUrl) {
this.codebaseUrl = codebaseUrl;
}
/**
* Return the codebase URL to download classes from if not found locally.
*/
@Override
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
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
*/
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;
}
}

86
fine-spring/src/main/java/com/fr/third/springframework/remoting/httpinvoker/HttpInvokerProxyFactoryBean.java

@ -1,86 +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 com.fr.third.springframework.remoting.httpinvoker;
import com.fr.third.springframework.aop.framework.ProxyFactory;
import com.fr.third.springframework.beans.factory.FactoryBean;
/**
* {@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 and Burlap protocols.
*
* <p><b>HTTP invoker is the recommended protocol for Java-to-Java remoting.</b>
* It is more powerful and more extensible than Hessian and Burlap, at the
* expense of being tied to Java. Nevertheless, it is as easy to set up as
* Hessian and Burlap, which is its main advantage compared to RMI.
*
* <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 com.fr.third.springframework.remoting.rmi.RmiProxyFactoryBean
* @see com.fr.third.springframework.remoting.caucho.HessianProxyFactoryBean
* @see com.fr.third.springframework.remoting.caucho.BurlapProxyFactoryBean
*/
public class HttpInvokerProxyFactoryBean extends HttpInvokerClientInterceptor implements FactoryBean<Object> {
private Object serviceProxy;
@Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
if (getServiceInterface() == null) {
throw new IllegalArgumentException("Property 'serviceInterface' is required");
}
this.serviceProxy = new ProxyFactory(getServiceInterface(), this).getProxy(getBeanClassLoader());
}
@Override
public Object getObject() {
return this.serviceProxy;
}
@Override
public Class<?> getObjectType() {
return getServiceInterface();
}
@Override
public boolean isSingleton() {
return true;
}
}

59
fine-spring/src/main/java/com/fr/third/springframework/remoting/httpinvoker/HttpInvokerRequestExecutor.java

@ -1,59 +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 com.fr.third.springframework.remoting.httpinvoker;
import java.io.IOException;
import com.fr.third.springframework.remoting.support.RemoteInvocation;
import com.fr.third.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
*/
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;
}

225
fine-spring/src/main/java/com/fr/third/springframework/remoting/httpinvoker/HttpInvokerServiceExporter.java

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

232
fine-spring/src/main/java/com/fr/third/springframework/remoting/httpinvoker/SimpleHttpInvokerRequestExecutor.java

@ -1,232 +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 com.fr.third.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 com.fr.third.springframework.context.i18n.LocaleContext;
import com.fr.third.springframework.context.i18n.LocaleContextHolder;
import com.fr.third.springframework.remoting.support.RemoteInvocationResult;
import com.fr.third.springframework.util.StringUtils;
/**
* {@link com.fr.third.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
*/
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, StringUtils.toLanguageTag(locale));
}
}
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));
}
}

184
fine-spring/src/main/java/com/fr/third/springframework/remoting/httpinvoker/SimpleHttpInvokerServiceExporter.java

@ -1,184 +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 com.fr.third.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 com.fr.third.springframework.lang.UsesSunHttpServer;
import com.fr.third.springframework.remoting.rmi.RemoteInvocationSerializingExporter;
import com.fr.third.springframework.remoting.support.RemoteInvocation;
import com.fr.third.springframework.remoting.support.RemoteInvocationResult;
/**
* HTTP request handler that exports the specified service bean as
* HTTP invoker service endpoint, accessible via an HTTP invoker proxy.
* Designed for Sun's JRE 1.6 HTTP server, implementing the
* {@link com.sun.net.httpserver.HttpHandler} interface.
*
* <p>Deserializes remote invocation objects and serializes remote invocation
* result objects. Uses Java serialization just like RMI, but provides the
* same ease of setup as Caucho's HTTP-based Hessian and Burlap protocols.
*
* <p><b>HTTP invoker is the recommended protocol for Java-to-Java remoting.</b>
* It is more powerful and more extensible than Hessian and Burlap, at the
* expense of being tied to Java. Nevertheless, it is as easy to set up as
* Hessian and Burlap, which is its main advantage compared to RMI.
*
* <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 com.fr.third.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor
* @see com.fr.third.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean
* @see com.fr.third.springframework.remoting.caucho.SimpleHessianServiceExporter
* @see com.fr.third.springframework.remoting.caucho.SimpleBurlapServiceExporter
*/
@UsesSunHttpServer
public class SimpleHttpInvokerServiceExporter extends RemoteInvocationSerializingExporter implements HttpHandler {
/**
* Reads a remote invocation from the request, executes it,
* and writes the remote invocation result to the response.
* @see #readRemoteInvocation(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 com.fr.third.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;
}
}

11
fine-spring/src/main/java/com/fr/third/springframework/remoting/httpinvoker/package-info.java

@ -1,11 +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 and Burlap protocols.
*
* <p><b>HTTP invoker is the recommended protocol for Java-to-Java remoting.</b>
* It is more powerful and more extensible than Hessian and Burlap, at the
* expense of being tied to Java. Neverthelesss, it is as easy to set up as
* Hessian and Burlap, which is its main advantage compared to RMI.
*/
package com.fr.third.springframework.remoting.httpinvoker;

119
fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/CodebaseAwareObjectInputStream.java

@ -1,119 +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 com.fr.third.springframework.remoting.rmi;
import java.io.IOException;
import java.io.InputStream;
import java.rmi.server.RMIClassLoader;
import com.fr.third.springframework.core.ConfigurableObjectInputStream;
/**
* 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 com.fr.third.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor#setCodebaseUrl
*/
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, 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, 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);
}
}

510
fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/JndiRmiClientInterceptor.java

@ -1,510 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.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 com.fr.third.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 javax.rmi.PortableRemoteObject;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.omg.CORBA.OBJECT_NOT_EXIST;
import org.omg.CORBA.SystemException;
import com.fr.third.springframework.aop.support.AopUtils;
import com.fr.third.springframework.beans.factory.InitializingBean;
import com.fr.third.springframework.jndi.JndiObjectLocator;
import com.fr.third.springframework.remoting.RemoteAccessException;
import com.fr.third.springframework.remoting.RemoteConnectFailureException;
import com.fr.third.springframework.remoting.RemoteInvocationFailureException;
import com.fr.third.springframework.remoting.RemoteLookupFailureException;
import com.fr.third.springframework.remoting.support.DefaultRemoteInvocationFactory;
import com.fr.third.springframework.remoting.support.RemoteInvocation;
import com.fr.third.springframework.remoting.support.RemoteInvocationFactory;
import com.fr.third.springframework.util.ReflectionUtils;
/**
* {@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">&lt;property name="jndiEnvironment"&gt;
* &lt;props>
* &lt;prop key="java.naming.factory.initial"&gt;com.sun.jndi.cosnaming.CNCtxFactory&lt;/prop&gt;
* &lt;prop key="java.naming.provider.url"&gt;iiop://localhost:1050&lt;/prop&gt;
* &lt;/props&gt;
* &lt;/property&gt;</pre>
*
* @author Juergen Hoeller
* @since 1.1
* @see #setJndiTemplate
* @see #setJndiEnvironment
* @see #setJndiName
* @see JndiRmiServiceExporter
* @see JndiRmiProxyFactoryBean
* @see com.fr.third.springframework.remoting.RemoteAccessException
* @see java.rmi.RemoteException
* @see java.rmi.Remote
*/
public class JndiRmiClientInterceptor extends JndiObjectLocator implements MethodInterceptor, InitializingBean {
// 定制 本类中注释掉的内容,见
// https://code.fineres.com/projects/CORE/repos/base-third/commits/5b3f31597873b165b6a154393ee4ade942beec0a#fine-spring/src/com/fr/third/springframework/remoting/rmi/JndiRmiClientInterceptor.java
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) {
if (serviceInterface != null && !serviceInterface.isInterface()) {
throw new IllegalArgumentException("'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 {
// Object stub = lookup();
// if (getServiceInterface() != null && !(stub instanceof RmiInvocationHandler)) {
// try {
// stub = PortableRemoteObject.narrow(stub, getServiceInterface());
// }
// catch (ClassCastException ex) {
// throw new RemoteLookupFailureException(
// "Could not narrow RMI stub to service interface [" + getServiceInterface().getName() + "]", ex);
// }
// }
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
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;
}
}
// catch (SystemException 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);
}
// /**
// * Determine whether the given CORBA exception indicates a connect failure.
// * <p>The default implementation checks for CORBA's
// * {@link org.omg.CORBA.OBJECT_NOT_EXIST} exception.
// * @param ex the RMI exception to check
// * @return whether the exception should be treated as connect failure
// */
// protected boolean isConnectFailure(SystemException ex) {
// return (ex instanceof OBJECT_NOT_EXIST);
// }
/**
* 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.isWarnEnabled()) {
logger.warn("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
*/
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
*/
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 (SystemException ex) {
// throw convertCorbaAccessException(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 if (targetEx instanceof SystemException) {
// throw convertCorbaAccessException((SystemException) 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 com.fr.third.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());
}
// /**
// * Convert the given CORBA SystemException that happened during remote access
// * to Spring's RemoteAccessException if the method signature does not declare
// * RemoteException. Else, return the SystemException wrapped in a RemoteException.
// * @param method the invoked method
// * @param ex the RemoteException that happened
// * @return the exception to be thrown to the caller
// */
// private Exception convertCorbaAccessException(SystemException ex, Method method) {
// if (ReflectionUtils.declaresException(method, RemoteException.class)) {
// // A traditional RMI service: wrap CORBA exceptions in standard RemoteExceptions.
// return new RemoteException("Failed to access CORBA service [" + getJndiName() + "]", ex);
// }
// else {
// if (isConnectFailure(ex)) {
// return new RemoteConnectFailureException("Could not connect to CORBA service [" + getJndiName() + "]", ex);
// }
// else {
// return new RemoteAccessException("Could not access CORBA service [" + getJndiName() + "]", ex);
// }
// }
// }
}

102
fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/JndiRmiProxyFactoryBean.java

@ -1,102 +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 com.fr.third.springframework.remoting.rmi;
import javax.naming.NamingException;
import com.fr.third.springframework.aop.framework.ProxyFactory;
import com.fr.third.springframework.beans.factory.BeanClassLoaderAware;
import com.fr.third.springframework.beans.factory.FactoryBean;
import com.fr.third.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">&lt;property name="jndiEnvironment"&gt;
* &lt;props>
* &lt;prop key="java.naming.factory.initial"&gt;com.sun.jndi.cosnaming.CNCtxFactory&lt;/prop&gt;
* &lt;prop key="java.naming.provider.url"&gt;iiop://localhost:1050&lt;/prop&gt;
* &lt;/props&gt;
* &lt;/property&gt;</pre>
*
* @author Juergen Hoeller
* @since 1.1
* @see #setServiceInterface
* @see #setJndiName
* @see #setJndiTemplate
* @see #setJndiEnvironment
* @see #setJndiName
* @see JndiRmiServiceExporter
* @see com.fr.third.springframework.remoting.RemoteAccessException
* @see java.rmi.RemoteException
* @see java.rmi.Remote
* @see javax.rmi.PortableRemoteObject#narrow
*/
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();
if (getServiceInterface() == null) {
throw new IllegalArgumentException("Property 'serviceInterface' is required");
}
this.serviceProxy = new ProxyFactory(getServiceInterface(), this).getProxy(this.beanClassLoader);
}
@Override
public Object getObject() {
return this.serviceProxy;
}
@Override
public Class<?> getObjectType() {
return getServiceInterface();
}
@Override
public boolean isSingleton() {
return true;
}
}

149
fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/JndiRmiServiceExporter.java

@ -1,149 +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 com.fr.third.springframework.remoting.rmi;
import java.rmi.NoSuchObjectException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.Properties;
import javax.naming.NamingException;
import com.fr.third.springframework.beans.factory.DisposableBean;
import com.fr.third.springframework.beans.factory.InitializingBean;
import com.fr.third.springframework.jndi.JndiTemplate;
/**
* Service exporter which binds RMI services to JNDI.
* Typically used for RMI-IIOP (CORBA).
*
* 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">&lt;property name="jndiEnvironment"&gt;
* &lt;props>
* &lt;prop key="java.naming.factory.initial"&gt;com.sun.jndi.cosnaming.CNCtxFactory&lt;/prop&gt;
* &lt;prop key="java.naming.provider.url"&gt;iiop://localhost:1050&lt;/prop&gt;
* &lt;/props&gt;
* &lt;/property&gt;</pre>
*
* @author Juergen Hoeller
* @since 1.1
* @see #setService
* @see #setJndiTemplate
* @see #setJndiEnvironment
* @see #setJndiName
* @see JndiRmiClientInterceptor
* @see JndiRmiProxyFactoryBean
*/
public class JndiRmiServiceExporter extends RmiBasedExporter implements InitializingBean, DisposableBean {
// 定制 本类中注释掉的内容,见
// https://code.fineres.com/projects/CORE/repos/base-third/commits/5b3f31597873b165b6a154393ee4ade942beec0a#fine-spring/src/com/fr/third/springframework/remoting/rmi/JndiRmiClientInterceptor.java
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();
// PortableRemoteObject.exportObject(this.exportedObject);
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.isInfoEnabled()) {
logger.info("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, NoSuchObjectException {
if (logger.isInfoEnabled()) {
logger.info("Unbinding RMI service from JNDI location [" + this.jndiName + "]");
}
this.jndiTemplate.unbind(this.jndiName);
// PortableRemoteObject.unexportObject(this.exportedObject);
}
}

181
fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/RemoteInvocationSerializingExporter.java

@ -1,181 +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 com.fr.third.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 com.fr.third.springframework.beans.factory.InitializingBean;
import com.fr.third.springframework.remoting.support.RemoteInvocation;
import com.fr.third.springframework.remoting.support.RemoteInvocationBasedExporter;
import com.fr.third.springframework.remoting.support.RemoteInvocationResult;
import com.fr.third.springframework.util.Assert;
import com.fr.third.springframework.util.ClassUtils;
/**
* Abstract base class for remote service exporters that explicitly deserialize
* {@link com.fr.third.springframework.remoting.support.RemoteInvocation} objects and serialize
* {@link com.fr.third.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
*/
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);
}
}

76
fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/RmiBasedExporter.java

@ -1,76 +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 com.fr.third.springframework.remoting.rmi;
import java.lang.reflect.InvocationTargetException;
import java.rmi.Remote;
import com.fr.third.springframework.remoting.support.RemoteInvocation;
import com.fr.third.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
*/
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);
}
}

416
fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/RmiClientInterceptor.java

@ -1,416 +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 com.fr.third.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 com.fr.third.springframework.aop.support.AopUtils;
import com.fr.third.springframework.remoting.RemoteConnectFailureException;
import com.fr.third.springframework.remoting.RemoteInvocationFailureException;
import com.fr.third.springframework.remoting.RemoteLookupFailureException;
import com.fr.third.springframework.remoting.support.RemoteInvocationBasedAccessor;
import com.fr.third.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 com.fr.third.springframework.remoting.RemoteAccessException
* @see java.rmi.RemoteException
* @see java.rmi.Remote
*/
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
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
*/
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
*/
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
*/
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 com.fr.third.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 [" + 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();
}
}
}

193
fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/RmiClientInterceptorUtils.java

@ -1,193 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.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 com.fr.third.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 com.fr.third.springframework.remoting.RemoteAccessException;
import com.fr.third.springframework.remoting.RemoteConnectFailureException;
import com.fr.third.springframework.remoting.RemoteProxyFailureException;
import com.fr.third.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
*/
public abstract class RmiClientInterceptorUtils {
// 定制 本类中注释掉的内容,见
// https://code.fineres.com/projects/CORE/repos/base-third/commits/5b3f31597873b165b6a154393ee4ade942beec0a#fine-spring/src/com/fr/third/springframework/remoting/rmi/JndiRmiClientInterceptor.java
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
*/
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 ||
isCorbaConnectFailure(ex.getCause()));
}
/**
* Check whether the given RMI exception root cause indicates a CORBA
* connection failure.
* <p>This is relevant on the IBM JVM, in particular for WebSphere EJB clients.
* <p>See the
* <a href="https://www.redbooks.ibm.com/Redbooks.nsf/RedbookAbstracts/tips0243.html">IBM website</code>
* for details.
* @param ex the RMI exception to check
*/
private static boolean isCorbaConnectFailure(Throwable ex) {
// return ((ex instanceof COMM_FAILURE || ex instanceof NO_RESPONSE) &&
// ((SystemException) ex).completed == CompletionStatus.COMPLETED_NO);
return (ex instanceof ConnectException || ex instanceof ConnectIOException ||
ex instanceof UnknownHostException || ex instanceof NoSuchObjectException ||
ex instanceof StubNotFoundException || ex.getCause() instanceof SocketException);
}
}

59
fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/RmiInvocationHandler.java

@ -1,59 +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 com.fr.third.springframework.remoting.rmi;
import java.lang.reflect.InvocationTargetException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import com.fr.third.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
*/
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()
*/
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
*/
public Object invoke(RemoteInvocation invocation)
throws RemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException;
}

77
fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/RmiInvocationWrapper.java

@ -1,77 +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 com.fr.third.springframework.remoting.rmi;
import java.lang.reflect.InvocationTargetException;
import java.rmi.RemoteException;
import com.fr.third.springframework.remoting.support.RemoteInvocation;
import com.fr.third.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
*/
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
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
public Object invoke(RemoteInvocation invocation)
throws RemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return this.rmiExporter.invoke(invocation, this.wrappedObject);
}
}

91
fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/RmiProxyFactoryBean.java

@ -1,91 +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 com.fr.third.springframework.remoting.rmi;
import com.fr.third.springframework.aop.framework.ProxyFactory;
import com.fr.third.springframework.beans.factory.BeanClassLoaderAware;
import com.fr.third.springframework.beans.factory.FactoryBean;
/**
* {@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 and Burlap, is serialization.
* Effectively, any serializable Java object can be transported without hassle.
* Hessian and Burlap have their own (de-)serialization mechanisms, but are
* 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 com.fr.third.springframework.remoting.RemoteAccessException
* @see com.fr.third.springframework.remoting.caucho.HessianProxyFactoryBean
* @see com.fr.third.springframework.remoting.caucho.BurlapProxyFactoryBean
* @see com.fr.third.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean
*/
public class RmiProxyFactoryBean extends RmiClientInterceptor implements FactoryBean<Object>, BeanClassLoaderAware {
private Object serviceProxy;
@Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
if (getServiceInterface() == null) {
throw new IllegalArgumentException("Property 'serviceInterface' is required");
}
this.serviceProxy = new ProxyFactory(getServiceInterface(), this).getProxy(getBeanClassLoader());
}
@Override
public Object getObject() {
return this.serviceProxy;
}
@Override
public Class<?> getObjectType() {
return getServiceInterface();
}
@Override
public boolean isSingleton() {
return true;
}
}

314
fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/RmiRegistryFactoryBean.java

@ -1,314 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.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 com.fr.third.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 com.fr.third.springframework.beans.factory.DisposableBean;
import com.fr.third.springframework.beans.factory.FactoryBean;
import com.fr.third.springframework.beans.factory.InitializingBean;
/**
* {@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 com.fr.third.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 com.fr.third.springframework.jmx.support.ConnectorServerFactoryBean
* @see java.rmi.registry.Registry
* @see java.rmi.registry.LocateRegistry
*/
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,
RMIClientSocketFactory clientSocketFactory, RMIServerSocketFactory serverSocketFactory)
throws RemoteException {
if (registryHost != null) {
// Host explicitly specified: only lookup possible.
if (logger.isInfoEnabled()) {
logger.info("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, RMIClientSocketFactory clientSocketFactory, RMIServerSocketFactory serverSocketFactory)
throws RemoteException {
if (clientSocketFactory != null) {
if (this.alwaysCreate) {
logger.info("Creating new RMI registry");
this.created = true;
return LocateRegistry.createRegistry(registryPort, clientSocketFactory, serverSocketFactory);
}
if (logger.isInfoEnabled()) {
logger.info("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.debug("RMI registry access threw exception", ex);
logger.info("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.info("Creating new RMI registry");
this.created = true;
return LocateRegistry.createRegistry(registryPort);
}
if (logger.isInfoEnabled()) {
logger.info("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.debug("RMI registry access threw exception", ex);
logger.info("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.info("Unexporting RMI registry");
UnicastRemoteObject.unexportObject(this.registry, true);
}
}
}

462
fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/RmiServiceExporter.java

@ -1,462 +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 com.fr.third.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 com.fr.third.springframework.beans.factory.DisposableBean;
import com.fr.third.springframework.beans.factory.InitializingBean;
/**
* 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 and Burlap, is serialization.
* Effectively, any serializable Java object can be transported without hassle.
* Hessian and Burlap have their own (de-)serialization mechanisms, but are
* 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 com.fr.third.springframework.remoting.caucho.HessianServiceExporter
* @see com.fr.third.springframework.remoting.caucho.BurlapServiceExporter
* @see com.fr.third.springframework.remoting.httpinvoker.HttpInvokerServiceExporter
*/
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.isInfoEnabled()) {
logger.info("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,
RMIClientSocketFactory clientSocketFactory, RMIServerSocketFactory serverSocketFactory)
throws RemoteException {
if (registryHost != null) {
// Host explicitly specified: only lookup possible.
if (logger.isInfoEnabled()) {
logger.info("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, RMIClientSocketFactory clientSocketFactory, RMIServerSocketFactory serverSocketFactory)
throws RemoteException {
if (clientSocketFactory != null) {
if (this.alwaysCreateRegistry) {
logger.info("Creating new RMI registry");
return LocateRegistry.createRegistry(registryPort, clientSocketFactory, serverSocketFactory);
}
if (logger.isInfoEnabled()) {
logger.info("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.debug("RMI registry access threw exception", ex);
logger.info("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.info("Creating new RMI registry");
return LocateRegistry.createRegistry(registryPort);
}
if (logger.isInfoEnabled()) {
logger.info("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.debug("RMI registry access threw exception", ex);
logger.info("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.isInfoEnabled()) {
logger.info("Unbinding RMI service '" + this.serviceName +
"' from registry" + (this.createdRegistry ? (" at port '" + this.registryPort + "'") : ""));
}
try {
this.registry.unbind(this.serviceName);
}
catch (NotBoundException ex) {
if (logger.isWarnEnabled()) {
logger.warn("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.isWarnEnabled()) {
logger.warn("RMI object for service '" + this.serviceName + "' is not exported anymore", ex);
}
}
}
}

6
fine-spring/src/main/java/com/fr/third/springframework/remoting/rmi/package-info.java

@ -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 com.fr.third.springframework.remoting.rmi;
Loading…
Cancel
Save