Browse Source
* commit 'b968da39b514c85ef2d6eb6d0ed9deac330feec8': REPORT-95610 hsqldb 升级回退 REPORT-95576 spring framework 高危漏洞,需要删除类 REPORT-95576 spring framework 高危漏洞,需要删除类 REPORT-95610 hsqldb存在公开漏洞需要升级 REPORT-95286 V8组件升级11.4 REPORT-94814 备份还原报错npe REPORT-93688 REPORT-94439 创建hsql连接超时 REPORT-93688 REPORT-94439 创建hsql连接超时persist/jsy11
superman
1 year ago
45 changed files with 108 additions and 5975 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,4 +1,5 @@
|
||||
1 spring版本4.3.29.RELEASE<br> |
||||
2 fine-spring内容见https://kms.fineres.com/pages/viewpage.action?pageId=152798513<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