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.jaxrpc;
018:
019: import java.lang.reflect.InvocationTargetException;
020: import java.lang.reflect.Method;
021: import java.rmi.Remote;
022: import java.rmi.RemoteException;
023: import java.util.HashMap;
024: import java.util.Iterator;
025: import java.util.Map;
026: import java.util.Properties;
027:
028: import javax.xml.namespace.QName;
029: import javax.xml.rpc.Call;
030: import javax.xml.rpc.JAXRPCException;
031: import javax.xml.rpc.Service;
032: import javax.xml.rpc.ServiceException;
033: import javax.xml.rpc.Stub;
034: import javax.xml.rpc.soap.SOAPFaultException;
035:
036: import org.aopalliance.intercept.MethodInterceptor;
037: import org.aopalliance.intercept.MethodInvocation;
038:
039: import org.springframework.aop.support.AopUtils;
040: import org.springframework.beans.factory.InitializingBean;
041: import org.springframework.remoting.RemoteLookupFailureException;
042: import org.springframework.remoting.RemoteProxyFailureException;
043: import org.springframework.remoting.rmi.RmiClientInterceptorUtils;
044: import org.springframework.util.CollectionUtils;
045: import org.springframework.util.ReflectionUtils;
046:
047: /**
048: * {@link org.aopalliance.intercept.MethodInterceptor} for accessing a specific port
049: * of a JAX-RPC service. Uses either {@link LocalJaxRpcServiceFactory}'s facilities
050: * underneath or takes an explicit reference to an existing JAX-RPC Service instance
051: * (e.g. obtained via {@link org.springframework.jndi.JndiObjectFactoryBean}).
052: *
053: * <p>Allows to set JAX-RPC's standard stub properties directly, via the
054: * "username", "password", "endpointAddress" and "maintainSession" properties.
055: * For typical usage, it is not necessary to specify those.
056: *
057: * <p>In standard JAX-RPC style, this invoker is used with an RMI service interface.
058: * Alternatively, this invoker can also proxy a JAX-RPC service with a matching
059: * non-RMI business interface, that is, an interface that declares the service methods
060: * without RemoteExceptions. In the latter case, RemoteExceptions thrown by JAX-RPC
061: * will automatically get converted to Spring's unchecked RemoteAccessException.
062: *
063: * <p>Setting "serviceInterface" is usually sufficient: The invoker will automatically
064: * use JAX-RPC "dynamic invocations" via the Call API in this case, no matter whether
065: * the specified interface is an RMI or non-RMI interface. Alternatively, a corresponding
066: * JAX-RPC port interface can be specified as "portInterface", which will turn this
067: * invoker into "static invocation" mode (operating on a standard JAX-RPC port stub).
068: *
069: * @author Juergen Hoeller
070: * @since 15.12.2003
071: * @see #setPortName
072: * @see #setServiceInterface
073: * @see #setPortInterface
074: * @see javax.xml.rpc.Service#createCall
075: * @see javax.xml.rpc.Service#getPort
076: * @see org.springframework.remoting.RemoteAccessException
077: * @see org.springframework.jndi.JndiObjectFactoryBean
078: */
079: public class JaxRpcPortClientInterceptor extends
080: LocalJaxRpcServiceFactory implements MethodInterceptor,
081: InitializingBean {
082:
083: private Service jaxRpcService;
084:
085: private Service serviceToUse;
086:
087: private String portName;
088:
089: private String username;
090:
091: private String password;
092:
093: private String endpointAddress;
094:
095: private boolean maintainSession;
096:
097: /** Map of custom properties, keyed by property name (String) */
098: private final Map customPropertyMap = new HashMap();
099:
100: private Class serviceInterface;
101:
102: private Class portInterface;
103:
104: private boolean lookupServiceOnStartup = true;
105:
106: private boolean refreshServiceAfterConnectFailure = false;
107:
108: private QName portQName;
109:
110: private Remote portStub;
111:
112: private final Object preparationMonitor = new Object();
113:
114: /**
115: * Set a reference to an existing JAX-RPC Service instance,
116: * for example obtained via {@link org.springframework.jndi.JndiObjectFactoryBean}.
117: * If not set, {@link LocalJaxRpcServiceFactory}'s properties have to be specified.
118: * @see #setServiceFactoryClass
119: * @see #setWsdlDocumentUrl
120: * @see #setNamespaceUri
121: * @see #setServiceName
122: * @see org.springframework.jndi.JndiObjectFactoryBean
123: */
124: public void setJaxRpcService(Service jaxRpcService) {
125: this .jaxRpcService = jaxRpcService;
126: }
127:
128: /**
129: * Return a reference to an existing JAX-RPC Service instance, if any.
130: */
131: public Service getJaxRpcService() {
132: return this .jaxRpcService;
133: }
134:
135: /**
136: * Set the name of the port.
137: * Corresponds to the "wsdl:port" name.
138: */
139: public void setPortName(String portName) {
140: this .portName = portName;
141: }
142:
143: /**
144: * Return the name of the port.
145: */
146: public String getPortName() {
147: return this .portName;
148: }
149:
150: /**
151: * Set the username to specify on the stub or call.
152: * @see javax.xml.rpc.Stub#USERNAME_PROPERTY
153: * @see javax.xml.rpc.Call#USERNAME_PROPERTY
154: */
155: public void setUsername(String username) {
156: this .username = username;
157: }
158:
159: /**
160: * Return the username to specify on the stub or call.
161: */
162: public String getUsername() {
163: return this .username;
164: }
165:
166: /**
167: * Set the password to specify on the stub or call.
168: * @see javax.xml.rpc.Stub#PASSWORD_PROPERTY
169: * @see javax.xml.rpc.Call#PASSWORD_PROPERTY
170: */
171: public void setPassword(String password) {
172: this .password = password;
173: }
174:
175: /**
176: * Return the password to specify on the stub or call.
177: */
178: public String getPassword() {
179: return this .password;
180: }
181:
182: /**
183: * Set the endpoint address to specify on the stub or call.
184: * @see javax.xml.rpc.Stub#ENDPOINT_ADDRESS_PROPERTY
185: * @see javax.xml.rpc.Call#setTargetEndpointAddress
186: */
187: public void setEndpointAddress(String endpointAddress) {
188: this .endpointAddress = endpointAddress;
189: }
190:
191: /**
192: * Return the endpoint address to specify on the stub or call.
193: */
194: public String getEndpointAddress() {
195: return this .endpointAddress;
196: }
197:
198: /**
199: * Set the maintain session flag to specify on the stub or call.
200: * @see javax.xml.rpc.Stub#SESSION_MAINTAIN_PROPERTY
201: * @see javax.xml.rpc.Call#SESSION_MAINTAIN_PROPERTY
202: */
203: public void setMaintainSession(boolean maintainSession) {
204: this .maintainSession = maintainSession;
205: }
206:
207: /**
208: * Return the maintain session flag to specify on the stub or call.
209: */
210: public boolean isMaintainSession() {
211: return this .maintainSession;
212: }
213:
214: /**
215: * Set custom properties to be set on the stub or call.
216: * <p>Can be populated with a String "value" (parsed via PropertiesEditor)
217: * or a "props" element in XML bean definitions.
218: * @see javax.xml.rpc.Stub#_setProperty
219: * @see javax.xml.rpc.Call#setProperty
220: */
221: public void setCustomProperties(Properties customProperties) {
222: CollectionUtils.mergePropertiesIntoMap(customProperties,
223: this .customPropertyMap);
224: }
225:
226: /**
227: * Set custom properties to be set on the stub or call.
228: * <p>Can be populated with a "map" or "props" element in XML bean definitions.
229: * @see javax.xml.rpc.Stub#_setProperty
230: * @see javax.xml.rpc.Call#setProperty
231: */
232: public void setCustomPropertyMap(Map customProperties) {
233: if (customProperties != null) {
234: Iterator it = customProperties.entrySet().iterator();
235: while (it.hasNext()) {
236: Map.Entry entry = (Map.Entry) it.next();
237: if (!(entry.getKey() instanceof String)) {
238: throw new IllegalArgumentException(
239: "Illegal property key [" + entry.getKey()
240: + "]: only Strings allowed");
241: }
242: addCustomProperty((String) entry.getKey(), entry
243: .getValue());
244: }
245: }
246: }
247:
248: /**
249: * Allow Map access to the custom properties to be set on the stub
250: * or call, with the option to add or override specific entries.
251: * <p>Useful for specifying entries directly, for example via
252: * "customPropertyMap[myKey]". This is particularly useful for
253: * adding or overriding entries in child bean definitions.
254: */
255: public Map getCustomPropertyMap() {
256: return this .customPropertyMap;
257: }
258:
259: /**
260: * Add a custom property to this JAX-RPC Stub/Call.
261: * @param name the name of the attribute to expose
262: * @param value the attribute value to expose
263: * @see javax.xml.rpc.Stub#_setProperty
264: * @see javax.xml.rpc.Call#setProperty
265: */
266: public void addCustomProperty(String name, Object value) {
267: this .customPropertyMap.put(name, value);
268: }
269:
270: /**
271: * Set the interface of the service that this factory should create a proxy for.
272: * This will typically be a non-RMI business interface, although you can also
273: * use an RMI port interface as recommended by JAX-RPC here.
274: * <p>Calls on the specified service interface will either be translated to the
275: * underlying RMI port interface (in case of a "portInterface" being specified)
276: * or to dynamic calls (using the JAX-RPC Dynamic Invocation Interface).
277: * <p>The dynamic call mechanism has the advantage that you don't need to
278: * maintain an RMI port interface in addition to an existing non-RMI business
279: * interface. In terms of configuration, specifying the business interface
280: * as "serviceInterface" will be enough; this interceptor will automatically
281: * use dynamic calls in such a scenario.
282: * @see javax.xml.rpc.Service#createCall
283: * @see #setPortInterface
284: */
285: public void setServiceInterface(Class serviceInterface) {
286: if (serviceInterface != null && !serviceInterface.isInterface()) {
287: throw new IllegalArgumentException(
288: "'serviceInterface' must be an interface");
289: }
290: this .serviceInterface = serviceInterface;
291: }
292:
293: /**
294: * Return the interface of the service that this factory should create a proxy for.
295: */
296: public Class getServiceInterface() {
297: return this .serviceInterface;
298: }
299:
300: /**
301: * Set the JAX-RPC port interface to use. Only needs to be set if a JAX-RPC
302: * port stub should be used instead of the dynamic call mechanism.
303: * See the javadoc of the "serviceInterface" property for more details.
304: * <p>The interface must be suitable for a JAX-RPC port, that is, it must be
305: * an RMI service interface (that extends <code>java.rmi.Remote</code>).
306: * <p><b>NOTE:</b> Check whether your JAX-RPC provider returns thread-safe
307: * port stubs. If not, use the dynamic call mechanism instead, which will
308: * always be thread-safe. In particular, do not use JAX-RPC port stubs
309: * with Apache Axis, whose port stubs are known to be non-thread-safe.
310: * @see javax.xml.rpc.Service#getPort
311: * @see java.rmi.Remote
312: * @see #setServiceInterface
313: */
314: public void setPortInterface(Class portInterface) {
315: if (portInterface != null
316: && (!portInterface.isInterface() || !Remote.class
317: .isAssignableFrom(portInterface))) {
318: throw new IllegalArgumentException(
319: "'portInterface' must be an interface derived from [java.rmi.Remote]");
320: }
321: this .portInterface = portInterface;
322: }
323:
324: /**
325: * Return the JAX-RPC port interface to use.
326: */
327: public Class getPortInterface() {
328: return this .portInterface;
329: }
330:
331: /**
332: * Set whether to look up the JAX-RPC service on startup.
333: * <p>Default is "true". Turn this flag off to allow for late start
334: * of the target server. In this case, the JAX-RPC service will be
335: * lazily fetched on first access.
336: */
337: public void setLookupServiceOnStartup(boolean lookupServiceOnStartup) {
338: this .lookupServiceOnStartup = lookupServiceOnStartup;
339: }
340:
341: /**
342: * Set whether to refresh the JAX-RPC service on connect failure,
343: * that is, whenever a JAX-RPC invocation throws a RemoteException.
344: * <p>Default is "false", keeping a reference to the JAX-RPC service
345: * in any case, retrying the next invocation on the same service
346: * even in case of failure. Turn this flag on to reinitialize the
347: * entire service in case of connect failures.
348: */
349: public void setRefreshServiceAfterConnectFailure(
350: boolean refreshServiceAfterConnectFailure) {
351: this .refreshServiceAfterConnectFailure = refreshServiceAfterConnectFailure;
352: }
353:
354: /**
355: * Prepares the JAX-RPC service and port if the "lookupServiceOnStartup"
356: * is turned on (which it is by default).
357: */
358: public void afterPropertiesSet() {
359: if (this .lookupServiceOnStartup) {
360: prepare();
361: }
362: }
363:
364: /**
365: * Create and initialize the JAX-RPC service for the specified port.
366: * <p>Prepares a JAX-RPC stub if possible (if an RMI interface is available);
367: * falls back to JAX-RPC dynamic calls else. Using dynamic calls can be enforced
368: * through overriding {@link #alwaysUseJaxRpcCall} to return <code>true</code>.
369: * <p>{@link #postProcessJaxRpcService} and {@link #postProcessPortStub}
370: * hooks are available for customization in subclasses. When using dynamic calls,
371: * each can be post-processed via {@link #postProcessJaxRpcCall}.
372: * @throws RemoteLookupFailureException if service initialization or port stub creation failed
373: */
374: public void prepare() throws RemoteLookupFailureException {
375: if (getPortName() == null) {
376: throw new IllegalArgumentException(
377: "Property 'portName' is required");
378: }
379:
380: synchronized (this .preparationMonitor) {
381: this .serviceToUse = null;
382:
383: // Cache the QName for the port.
384: this .portQName = getQName(getPortName());
385:
386: try {
387: Service service = getJaxRpcService();
388: if (service == null) {
389: service = createJaxRpcService();
390: } else {
391: postProcessJaxRpcService(service);
392: }
393:
394: Class portInterface = getPortInterface();
395: if (portInterface != null && !alwaysUseJaxRpcCall()) {
396: // JAX-RPC-compliant port interface -> using JAX-RPC stub for port.
397:
398: if (logger.isDebugEnabled()) {
399: logger
400: .debug("Creating JAX-RPC proxy for JAX-RPC port ["
401: + this .portQName
402: + "], using port interface ["
403: + portInterface.getName() + "]");
404: }
405: Remote remoteObj = service.getPort(this .portQName,
406: portInterface);
407:
408: if (logger.isDebugEnabled()) {
409: Class serviceInterface = getServiceInterface();
410: if (serviceInterface != null) {
411: boolean isImpl = serviceInterface
412: .isInstance(remoteObj);
413: logger.debug("Using service interface ["
414: + serviceInterface.getName()
415: + "] for JAX-RPC port ["
416: + this .portQName + "] - "
417: + (!isImpl ? "not" : "")
418: + " directly implemented");
419: }
420: }
421:
422: if (!(remoteObj instanceof Stub)) {
423: throw new RemoteLookupFailureException(
424: "Port stub of class ["
425: + remoteObj.getClass()
426: .getName()
427: + "] is not a valid JAX-RPC stub: it does not implement interface [javax.xml.rpc.Stub]");
428: }
429: Stub stub = (Stub) remoteObj;
430:
431: // Apply properties to JAX-RPC stub.
432: preparePortStub(stub);
433:
434: // Allow for custom post-processing in subclasses.
435: postProcessPortStub(stub);
436:
437: this .portStub = remoteObj;
438: }
439:
440: else {
441: // No JAX-RPC-compliant port interface -> using JAX-RPC dynamic calls.
442: if (logger.isDebugEnabled()) {
443: logger
444: .debug("Using JAX-RPC dynamic calls for JAX-RPC port ["
445: + this .portQName + "]");
446: }
447: }
448:
449: this .serviceToUse = service;
450: } catch (ServiceException ex) {
451: throw new RemoteLookupFailureException(
452: "Failed to initialize service for JAX-RPC port ["
453: + this .portQName + "]", ex);
454: }
455: }
456: }
457:
458: /**
459: * Return whether to always use JAX-RPC dynamic calls.
460: * Called by <code>afterPropertiesSet</code>.
461: * <p>Default is "false"; if an RMI interface is specified as "portInterface"
462: * or "serviceInterface", it will be used to create a JAX-RPC port stub.
463: * <p>Can be overridden to enforce the use of the JAX-RPC Call API,
464: * for example if there is a need to customize at the Call level.
465: * This just necessary if you you want to use an RMI interface as
466: * "serviceInterface", though; in case of only a non-RMI interface being
467: * available, this interceptor will fall back to the Call API anyway.
468: * @see #postProcessJaxRpcCall
469: */
470: protected boolean alwaysUseJaxRpcCall() {
471: return false;
472: }
473:
474: /**
475: * Reset the prepared service of this interceptor,
476: * allowing for reinitialization on next access.
477: */
478: protected void reset() {
479: synchronized (this .preparationMonitor) {
480: this .serviceToUse = null;
481: }
482: }
483:
484: /**
485: * Return whether this client interceptor has already been prepared,
486: * i.e. has already looked up the JAX-RPC service and port.
487: */
488: protected boolean isPrepared() {
489: synchronized (this .preparationMonitor) {
490: return (this .serviceToUse != null);
491: }
492: }
493:
494: /**
495: * Return the prepared QName for the port.
496: * @see #setPortName
497: * @see #getQName
498: */
499: protected final QName getPortQName() {
500: return this .portQName;
501: }
502:
503: /**
504: * Prepare the given JAX-RPC port stub, applying properties to it.
505: * Called by {@link #afterPropertiesSet}.
506: * <p>Just applied when actually creating a JAX-RPC port stub, in case of a
507: * compliant port interface. Else, JAX-RPC dynamic calls will be used.
508: * @param stub the current JAX-RPC port stub
509: * @see #setUsername
510: * @see #setPassword
511: * @see #setEndpointAddress
512: * @see #setMaintainSession
513: * @see #setCustomProperties
514: * @see #setPortInterface
515: * @see #prepareJaxRpcCall
516: */
517: protected void preparePortStub(Stub stub) {
518: String username = getUsername();
519: if (username != null) {
520: stub._setProperty(Stub.USERNAME_PROPERTY, username);
521: }
522: String password = getPassword();
523: if (password != null) {
524: stub._setProperty(Stub.PASSWORD_PROPERTY, password);
525: }
526: String endpointAddress = getEndpointAddress();
527: if (endpointAddress != null) {
528: stub._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY,
529: endpointAddress);
530: }
531: if (isMaintainSession()) {
532: stub._setProperty(Stub.SESSION_MAINTAIN_PROPERTY,
533: Boolean.TRUE);
534: }
535: if (this .customPropertyMap != null) {
536: for (Iterator it = this .customPropertyMap.keySet()
537: .iterator(); it.hasNext();) {
538: String key = (String) it.next();
539: stub._setProperty(key, this .customPropertyMap.get(key));
540: }
541: }
542: }
543:
544: /**
545: * Post-process the given JAX-RPC port stub. Called by {@link #prepare}.
546: * <p>The default implementation is empty.
547: * <p>Just applied when actually creating a JAX-RPC port stub, in case of a
548: * compliant port interface. Else, JAX-RPC dynamic calls will be used.
549: * @param stub the current JAX-RPC port stub
550: * (can be cast to an implementation-specific class if necessary)
551: * @see #setPortInterface
552: * @see #postProcessJaxRpcCall
553: */
554: protected void postProcessPortStub(Stub stub) {
555: }
556:
557: /**
558: * Return the underlying JAX-RPC port stub that this interceptor delegates to
559: * for each method invocation on the proxy.
560: */
561: protected Remote getPortStub() {
562: return this .portStub;
563: }
564:
565: /**
566: * Translates the method invocation into a JAX-RPC service invocation.
567: * <p>Prepares the service on the fly, if necessary, in case of lazy
568: * lookup or a connect failure having happened.
569: * @see #prepare()
570: * @see #doInvoke
571: */
572: public Object invoke(MethodInvocation invocation) throws Throwable {
573: if (AopUtils.isToStringMethod(invocation.getMethod())) {
574: return "JAX-RPC proxy for port [" + getPortName()
575: + "] of service [" + getServiceName() + "]";
576: }
577: // Lazily prepare service and stub if necessary.
578: synchronized (this .preparationMonitor) {
579: if (!isPrepared()) {
580: prepare();
581: }
582: }
583: return doInvoke(invocation);
584: }
585:
586: /**
587: * Perform a JAX-RPC service invocation based on the given method invocation.
588: * <p>Uses traditional RMI stub invocation if a JAX-RPC port stub is available;
589: * falls back to JAX-RPC dynamic calls else.
590: * @param invocation the AOP method invocation
591: * @return the invocation result, if any
592: * @throws Throwable in case of invocation failure
593: * @see #getPortStub()
594: * @see #doInvoke(org.aopalliance.intercept.MethodInvocation, java.rmi.Remote)
595: * @see #performJaxRpcCall(org.aopalliance.intercept.MethodInvocation, javax.xml.rpc.Service)
596: */
597: protected Object doInvoke(MethodInvocation invocation)
598: throws Throwable {
599: Remote stub = getPortStub();
600: try {
601: if (stub != null) {
602: // JAX-RPC port stub available -> traditional RMI stub invocation.
603: if (logger.isTraceEnabled()) {
604: logger.trace("Invoking operation '"
605: + invocation.getMethod().getName()
606: + "' on JAX-RPC port stub");
607: }
608: return doInvoke(invocation, stub);
609: } else {
610: // No JAX-RPC stub -> using JAX-RPC dynamic calls.
611: if (logger.isTraceEnabled()) {
612: logger.trace("Invoking operation '"
613: + invocation.getMethod().getName()
614: + "' as JAX-RPC dynamic call");
615: }
616: return performJaxRpcCall(invocation, this .serviceToUse);
617: }
618: } catch (RemoteException ex) {
619: throw handleRemoteException(invocation.getMethod(), ex);
620: } catch (SOAPFaultException ex) {
621: throw new JaxRpcSoapFaultException(ex);
622: } catch (JAXRPCException ex) {
623: throw new RemoteProxyFailureException(
624: "Invalid JAX-RPC call configuration", ex);
625: }
626: }
627:
628: /**
629: * Perform a JAX-RPC service invocation on the given port stub.
630: * @param invocation the AOP method invocation
631: * @param portStub the RMI port stub to invoke
632: * @return the invocation result, if any
633: * @throws Throwable in case of invocation failure
634: * @see #getPortStub()
635: * @see #doInvoke(org.aopalliance.intercept.MethodInvocation, java.rmi.Remote)
636: * @see #performJaxRpcCall
637: */
638: protected Object doInvoke(MethodInvocation invocation,
639: Remote portStub) throws Throwable {
640: try {
641: return RmiClientInterceptorUtils.doInvoke(invocation,
642: portStub);
643: } catch (InvocationTargetException ex) {
644: throw ex.getTargetException();
645: }
646: }
647:
648: /**
649: * Perform a JAX-RPC dynamic call for the given AOP method invocation.
650: * Delegates to {@link #prepareJaxRpcCall} and
651: * {@link #postProcessJaxRpcCall} for setting up the call object.
652: * <p>The default implementation uses method name as JAX-RPC operation name
653: * and method arguments as arguments for the JAX-RPC call. Can be
654: * overridden in subclasses for custom operation names and/or arguments.
655: * @param invocation the current AOP MethodInvocation that should
656: * be converted to a JAX-RPC call
657: * @param service the JAX-RPC Service to use for the call
658: * @return the return value of the invocation, if any
659: * @throws Throwable the exception thrown by the invocation, if any
660: * @see #prepareJaxRpcCall
661: * @see #postProcessJaxRpcCall
662: */
663: protected Object performJaxRpcCall(MethodInvocation invocation,
664: Service service) throws Throwable {
665: Method method = invocation.getMethod();
666: QName portQName = this .portQName;
667:
668: // Create JAX-RPC call object, using the method name as operation name.
669: // Synchronized because of non-thread-safe Axis implementation!
670: Call call = null;
671: synchronized (service) {
672: call = service.createCall(portQName, method.getName());
673: }
674:
675: // Apply properties to JAX-RPC stub.
676: prepareJaxRpcCall(call);
677:
678: // Allow for custom post-processing in subclasses.
679: postProcessJaxRpcCall(call, invocation);
680:
681: // Perform actual invocation.
682: return call.invoke(invocation.getArguments());
683: }
684:
685: /**
686: * Prepare the given JAX-RPC call, applying properties to it. Called by {@link #invoke}.
687: * <p>Just applied when actually using JAX-RPC dynamic calls, i.e. if no compliant
688: * port interface was specified. Else, a JAX-RPC port stub will be used.
689: * @param call the current JAX-RPC call object
690: * @see #setUsername
691: * @see #setPassword
692: * @see #setEndpointAddress
693: * @see #setMaintainSession
694: * @see #setCustomProperties
695: * @see #setPortInterface
696: * @see #preparePortStub
697: */
698: protected void prepareJaxRpcCall(Call call) {
699: String username = getUsername();
700: if (username != null) {
701: call.setProperty(Call.USERNAME_PROPERTY, username);
702: }
703: String password = getPassword();
704: if (password != null) {
705: call.setProperty(Call.PASSWORD_PROPERTY, password);
706: }
707: String endpointAddress = getEndpointAddress();
708: if (endpointAddress != null) {
709: call.setTargetEndpointAddress(endpointAddress);
710: }
711: if (isMaintainSession()) {
712: call.setProperty(Call.SESSION_MAINTAIN_PROPERTY,
713: Boolean.TRUE);
714: }
715: if (this .customPropertyMap != null) {
716: for (Iterator it = this .customPropertyMap.keySet()
717: .iterator(); it.hasNext();) {
718: String key = (String) it.next();
719: call.setProperty(key, this .customPropertyMap.get(key));
720: }
721: }
722: }
723:
724: /**
725: * Post-process the given JAX-RPC call. Called by {@link #invoke}.
726: * <p>The default implementation is empty.
727: * <p>Just applied when actually using JAX-RPC dynamic calls, i.e. if no compliant
728: * port interface was specified. Else, a JAX-RPC port stub will be used.
729: * @param call the current JAX-RPC call object
730: * (can be cast to an implementation-specific class if necessary)
731: * @param invocation the current AOP MethodInvocation that the call was
732: * created for (can be used to check method name, method parameters
733: * and/or passed-in arguments)
734: * @see #setPortInterface
735: * @see #postProcessPortStub
736: */
737: protected void postProcessJaxRpcCall(Call call,
738: MethodInvocation invocation) {
739: }
740:
741: /**
742: * Handle the given RemoteException that was thrown from a JAX-RPC port stub
743: * or JAX-RPC call invocation.
744: * @param method the service interface method that we invoked
745: * @param ex the original RemoteException
746: * @return the exception to rethrow (may be the original RemoteException
747: * or an extracted/wrapped exception, but never <code>null</code>)
748: */
749: protected Throwable handleRemoteException(Method method,
750: RemoteException ex) {
751: boolean isConnectFailure = isConnectFailure(ex);
752: if (isConnectFailure && this .refreshServiceAfterConnectFailure) {
753: reset();
754: }
755: Throwable cause = ex.getCause();
756: if (cause != null
757: && ReflectionUtils.declaresException(method, cause
758: .getClass())) {
759: if (logger.isDebugEnabled()) {
760: logger.debug("Rethrowing wrapped exception of type ["
761: + cause.getClass().getName() + "] as-is");
762: }
763: // Declared on the service interface: probably a wrapped business exception.
764: return ex.getCause();
765: } else {
766: // Throw either a RemoteAccessException or the original RemoteException,
767: // depending on what the service interface declares.
768: return RmiClientInterceptorUtils.convertRmiAccessException(
769: method, ex, isConnectFailure, portQName.toString());
770: }
771: }
772:
773: /**
774: * Determine whether the given RMI exception indicates a connect failure.
775: * <p>The default implementation returns <code>true</code> unless the
776: * exception class name (or exception superclass name) contains the term
777: * "Fault" (e.g. "AxisFault"), assuming that the JAX-RPC provider only
778: * throws RemoteException in case of WSDL faults and connect failures.
779: * @param ex the RMI exception to check
780: * @return whether the exception should be treated as connect failure
781: * @see org.springframework.remoting.rmi.RmiClientInterceptorUtils#isConnectFailure
782: */
783: protected boolean isConnectFailure(RemoteException ex) {
784: return (ex.getClass().getName().indexOf("Fault") == -1 && ex
785: .getClass().getSuperclass().getName().indexOf("Fault") == -1);
786: }
787:
788: }
|