Yuan.Wang
2 years ago
36 changed files with 2 additions and 5838 deletions
@ -1,4 +1,5 @@ |
|||||||
1 spring版本4.3.29.RELEASE<br> |
1 spring版本4.3.29.RELEASE<br> |
||||||
2 fine-spring内容见https://kms.fineres.com/pages/viewpage.action?pageId=152798513<br> |
2 fine-spring内容见https://kms.fineres.com/pages/viewpage.action?pageId=152798513<br> |
||||||
3 定制内容处均有"// 定制"注释<br> |
3 定制内容处均有"// 定制"注释<br> |
||||||
4 去除对jackson的ObjectMapper的支持:DEC-17331<br> |
4 去除对jackson的ObjectMapper的支持:DEC-17331<br> |
||||||
|
5、删除com.fr.third.springframework.remoting.caucho ;com.fr.third.springframework.remoting.httpinvoker; com.fr.third.springframework.remoting.rmi.*<br> |
@ -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); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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()); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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()); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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; |
|
@ -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; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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)); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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(); |
|
||||||
|
|
||||||
} |
|
@ -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; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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; |
|
||||||
|
|
||||||
} |
|
@ -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
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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)); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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; |
|
@ -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); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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"><property name="jndiEnvironment"> |
|
||||||
* <props> |
|
||||||
* <prop key="java.naming.factory.initial">com.sun.jndi.cosnaming.CNCtxFactory</prop> |
|
||||||
* <prop key="java.naming.provider.url">iiop://localhost:1050</prop>
|
|
||||||
* </props> |
|
||||||
* </property></pre> |
|
||||||
* |
|
||||||
* @author Juergen Hoeller |
|
||||||
* @since 1.1 |
|
||||||
* @see #setJndiTemplate |
|
||||||
* @see #setJndiEnvironment |
|
||||||
* @see #setJndiName |
|
||||||
* @see JndiRmiServiceExporter |
|
||||||
* @see JndiRmiProxyFactoryBean |
|
||||||
* @see 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);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
} |
|
@ -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"><property name="jndiEnvironment"> |
|
||||||
* <props> |
|
||||||
* <prop key="java.naming.factory.initial">com.sun.jndi.cosnaming.CNCtxFactory</prop> |
|
||||||
* <prop key="java.naming.provider.url">iiop://localhost:1050</prop>
|
|
||||||
* </props> |
|
||||||
* </property></pre> |
|
||||||
* |
|
||||||
* @author Juergen Hoeller |
|
||||||
* @since 1.1 |
|
||||||
* @see #setServiceInterface |
|
||||||
* @see #setJndiName |
|
||||||
* @see #setJndiTemplate |
|
||||||
* @see #setJndiEnvironment |
|
||||||
* @see #setJndiName |
|
||||||
* @see JndiRmiServiceExporter |
|
||||||
* @see 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; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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"><property name="jndiEnvironment"> |
|
||||||
* <props> |
|
||||||
* <prop key="java.naming.factory.initial">com.sun.jndi.cosnaming.CNCtxFactory</prop> |
|
||||||
* <prop key="java.naming.provider.url">iiop://localhost:1050</prop>
|
|
||||||
* </props> |
|
||||||
* </property></pre> |
|
||||||
* |
|
||||||
* @author Juergen Hoeller |
|
||||||
* @since 1.1 |
|
||||||
* @see #setService |
|
||||||
* @see #setJndiTemplate |
|
||||||
* @see #setJndiEnvironment |
|
||||||
* @see #setJndiName |
|
||||||
* @see JndiRmiClientInterceptor |
|
||||||
* @see JndiRmiProxyFactoryBean |
|
||||||
*/ |
|
||||||
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);
|
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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; |
|
||||||
|
|
||||||
} |
|
@ -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); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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…
Reference in new issue