001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.remoting.caucho;
018:
019: import java.lang.reflect.InvocationTargetException;
020: import java.lang.reflect.UndeclaredThrowableException;
021: import java.net.ConnectException;
022: import java.net.MalformedURLException;
023:
024: import com.caucho.hessian.client.HessianProxyFactory;
025: import com.caucho.hessian.client.HessianRuntimeException;
026: import com.caucho.hessian.io.SerializerFactory;
027: import org.aopalliance.intercept.MethodInterceptor;
028: import org.aopalliance.intercept.MethodInvocation;
029:
030: import org.springframework.remoting.RemoteAccessException;
031: import org.springframework.remoting.RemoteConnectFailureException;
032: import org.springframework.remoting.RemoteLookupFailureException;
033: import org.springframework.remoting.RemoteProxyFailureException;
034: import org.springframework.remoting.support.UrlBasedRemoteAccessor;
035: import org.springframework.util.Assert;
036:
037: /**
038: * {@link org.aopalliance.intercept.MethodInterceptor} for accessing a Hessian service.
039: * Supports authentication via username and password.
040: * The service URL must be an HTTP URL exposing a Hessian service.
041: *
042: * <p>Hessian is a slim, binary RPC protocol.
043: * For information on Hessian, see the
044: * <a href="http://www.caucho.com/hessian">Hessian website</a>
045: *
046: * <p>Note: There is no requirement for services accessed with this proxy factory
047: * to have been exported using Spring's {@link HessianServiceExporter}, as there is
048: * no special handling involved. As a consequence, you can also access services that
049: * have been exported using Caucho's {@link com.caucho.hessian.server.HessianServlet}.
050: *
051: * @author Juergen Hoeller
052: * @since 29.09.2003
053: * @see #setServiceInterface
054: * @see #setServiceUrl
055: * @see #setUsername
056: * @see #setPassword
057: * @see HessianServiceExporter
058: * @see HessianProxyFactoryBean
059: * @see com.caucho.hessian.client.HessianProxyFactory
060: * @see com.caucho.hessian.server.HessianServlet
061: */
062: public class HessianClientInterceptor extends UrlBasedRemoteAccessor
063: implements MethodInterceptor {
064:
065: private HessianProxyFactory proxyFactory = new HessianProxyFactory();
066:
067: private Object hessianProxy;
068:
069: /**
070: * Set the HessianProxyFactory instance to use.
071: * If not specified, a default HessianProxyFactory will be created.
072: * <p>Allows to use an externally configured factory instance,
073: * in particular a custom HessianProxyFactory subclass.
074: */
075: public void setProxyFactory(HessianProxyFactory proxyFactory) {
076: this .proxyFactory = (proxyFactory != null ? proxyFactory
077: : new HessianProxyFactory());
078: }
079:
080: /**
081: * Specify the Hessian SerializerFactory to use.
082: * <p>This will typically be passed in as an inner bean definition
083: * of type <code>com.caucho.hessian.io.SerializerFactory</code>,
084: * with custom bean property values applied.
085: */
086: public void setSerializerFactory(SerializerFactory serializerFactory) {
087: this .proxyFactory.setSerializerFactory(serializerFactory);
088: }
089:
090: /**
091: * Set whether to send the Java collection type for each serialized
092: * collection. Default is "true".
093: */
094: public void setSendCollectionType(boolean sendCollectionType) {
095: this .proxyFactory.getSerializerFactory().setSendCollectionType(
096: sendCollectionType);
097: }
098:
099: /**
100: * Set the username that this factory should use to access the remote service.
101: * Default is none.
102: * <p>The username will be sent by Hessian via HTTP Basic Authentication.
103: * @see com.caucho.hessian.client.HessianProxyFactory#setUser
104: */
105: public void setUsername(String username) {
106: this .proxyFactory.setUser(username);
107: }
108:
109: /**
110: * Set the password that this factory should use to access the remote service.
111: * Default is none.
112: * <p>The password will be sent by Hessian via HTTP Basic Authentication.
113: * @see com.caucho.hessian.client.HessianProxyFactory#setPassword
114: */
115: public void setPassword(String password) {
116: this .proxyFactory.setPassword(password);
117: }
118:
119: /**
120: * Set whether overloaded methods should be enabled for remote invocations.
121: * Default is "false".
122: * @see com.caucho.hessian.client.HessianProxyFactory#setOverloadEnabled
123: */
124: public void setOverloadEnabled(boolean overloadEnabled) {
125: this .proxyFactory.setOverloadEnabled(overloadEnabled);
126: }
127:
128: public void afterPropertiesSet() {
129: super .afterPropertiesSet();
130: prepare();
131: }
132:
133: /**
134: * Initialize the Hessian proxy for this interceptor.
135: * @throws RemoteLookupFailureException if the service URL is invalid
136: */
137: public void prepare() throws RemoteLookupFailureException {
138: try {
139: this .hessianProxy = createHessianProxy(this .proxyFactory);
140: } catch (MalformedURLException ex) {
141: throw new RemoteLookupFailureException("Service URL ["
142: + getServiceUrl() + "] is invalid", ex);
143: }
144: }
145:
146: /**
147: * Create the Hessian proxy that is wrapped by this interceptor.
148: * @param proxyFactory the proxy factory to use
149: * @return the Hessian proxy
150: * @throws MalformedURLException if thrown by the proxy factory
151: * @see com.caucho.hessian.client.HessianProxyFactory#create
152: */
153: protected Object createHessianProxy(HessianProxyFactory proxyFactory)
154: throws MalformedURLException {
155: Assert.notNull(getServiceInterface(),
156: "'serviceInterface' is required");
157: return proxyFactory.create(getServiceInterface(),
158: getServiceUrl());
159: }
160:
161: public Object invoke(MethodInvocation invocation) throws Throwable {
162: if (this .hessianProxy == null) {
163: throw new IllegalStateException(
164: "HessianClientInterceptor is not properly initialized - "
165: + "invoke 'prepare' before attempting any operations");
166: }
167:
168: try {
169: return invocation.getMethod().invoke(this .hessianProxy,
170: invocation.getArguments());
171: } catch (InvocationTargetException ex) {
172: if (ex.getTargetException() instanceof HessianRuntimeException) {
173: HessianRuntimeException hre = (HessianRuntimeException) ex
174: .getTargetException();
175: Throwable rootCause = (hre.getRootCause() != null ? hre
176: .getRootCause() : hre);
177: throw convertHessianAccessException(rootCause);
178: } else if (ex.getTargetException() instanceof UndeclaredThrowableException) {
179: UndeclaredThrowableException utex = (UndeclaredThrowableException) ex
180: .getTargetException();
181: throw convertHessianAccessException(utex
182: .getUndeclaredThrowable());
183: }
184: throw ex.getTargetException();
185: } catch (Throwable ex) {
186: throw new RemoteProxyFailureException(
187: "Failed to invoke Hessian proxy for remote service ["
188: + getServiceUrl() + "]", ex);
189: }
190: }
191:
192: /**
193: * Convert the given Hessian access exception to an appropriate
194: * Spring RemoteAccessException.
195: * @param ex the exception to convert
196: * @return the RemoteAccessException to throw
197: */
198: protected RemoteAccessException convertHessianAccessException(
199: Throwable ex) {
200: if (ex instanceof ConnectException) {
201: return new RemoteConnectFailureException(
202: "Cannot connect to Hessian remote service at ["
203: + getServiceUrl() + "]", ex);
204: } else {
205: return new RemoteAccessException(
206: "Cannot access Hessian remote service at ["
207: + getServiceUrl() + "]", ex);
208: }
209: }
210:
211: }
|