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.jaxws;
018:
019: import java.lang.reflect.InvocationTargetException;
020: import java.lang.reflect.Method;
021:
022: import javax.xml.namespace.QName;
023: import javax.xml.ws.ProtocolException;
024: import javax.xml.ws.Service;
025: import javax.xml.ws.WebServiceException;
026: import javax.xml.ws.soap.SOAPFaultException;
027:
028: import org.aopalliance.intercept.MethodInterceptor;
029: import org.aopalliance.intercept.MethodInvocation;
030:
031: import org.springframework.aop.support.AopUtils;
032: import org.springframework.beans.factory.InitializingBean;
033: import org.springframework.remoting.RemoteAccessException;
034: import org.springframework.remoting.RemoteConnectFailureException;
035: import org.springframework.remoting.RemoteProxyFailureException;
036:
037: /**
038: * {@link org.aopalliance.intercept.MethodInterceptor} for accessing a
039: * specific port of a JAX-WS service.
040: *
041: * <p>Uses either {@link LocalJaxWsServiceFactory}'s facilities underneath,
042: * or takes an explicit reference to an existing JAX-WS Service instance
043: * (e.g. obtained via {@link org.springframework.jndi.JndiObjectFactoryBean}).
044: *
045: * @author Juergen Hoeller
046: * @since 2.5
047: * @see #setPortName
048: * @see #setServiceInterface
049: * @see javax.xml.ws.Service#getPort
050: * @see org.springframework.remoting.RemoteAccessException
051: * @see org.springframework.jndi.JndiObjectFactoryBean
052: */
053: public class JaxWsPortClientInterceptor extends
054: LocalJaxWsServiceFactory implements MethodInterceptor,
055: InitializingBean {
056:
057: private Service jaxWsService;
058:
059: private String portName;
060:
061: private Class<?> serviceInterface;
062:
063: private boolean lookupServiceOnStartup = true;
064:
065: private QName portQName;
066:
067: private Object portStub;
068:
069: private final Object preparationMonitor = new Object();
070:
071: /**
072: * Set a reference to an existing JAX-WS Service instance,
073: * for example obtained via {@link org.springframework.jndi.JndiObjectFactoryBean}.
074: * If not set, {@link LocalJaxWsServiceFactory}'s properties have to be specified.
075: * @see #setWsdlDocumentUrl
076: * @see #setNamespaceUri
077: * @see #setServiceName
078: * @see org.springframework.jndi.JndiObjectFactoryBean
079: */
080: public void setJaxWsService(Service jaxWsService) {
081: this .jaxWsService = jaxWsService;
082: }
083:
084: /**
085: * Return a reference to an existing JAX-WS Service instance, if any.
086: */
087: public Service getJaxWsService() {
088: return this .jaxWsService;
089: }
090:
091: /**
092: * Set the name of the port.
093: * Corresponds to the "wsdl:port" name.
094: */
095: public void setPortName(String portName) {
096: this .portName = portName;
097: }
098:
099: /**
100: * Return the name of the port.
101: */
102: public String getPortName() {
103: return this .portName;
104: }
105:
106: /**
107: * Set the interface of the service that this factory should create a proxy for.
108: */
109: public void setServiceInterface(Class serviceInterface) {
110: if (serviceInterface != null && !serviceInterface.isInterface()) {
111: throw new IllegalArgumentException(
112: "'serviceInterface' must be an interface");
113: }
114: this .serviceInterface = serviceInterface;
115: }
116:
117: /**
118: * Return the interface of the service that this factory should create a proxy for.
119: */
120: public Class getServiceInterface() {
121: return this .serviceInterface;
122: }
123:
124: /**
125: * Set whether to look up the JAX-WS service on startup.
126: * <p>Default is "true". Turn this flag off to allow for late start
127: * of the target server. In this case, the JAX-WS service will be
128: * lazily fetched on first access.
129: */
130: public void setLookupServiceOnStartup(boolean lookupServiceOnStartup) {
131: this .lookupServiceOnStartup = lookupServiceOnStartup;
132: }
133:
134: public void afterPropertiesSet() {
135: if (this .lookupServiceOnStartup) {
136: prepare();
137: }
138: }
139:
140: public void prepare() {
141: if (getServiceInterface() == null) {
142: throw new IllegalArgumentException(
143: "Property 'serviceInterface' is required");
144: }
145: Service serviceToUse = getJaxWsService();
146: if (serviceToUse == null) {
147: serviceToUse = createJaxWsService();
148: }
149: this .portQName = getQName(getPortName() != null ? getPortName()
150: : getServiceInterface().getName());
151: this .portStub = (getPortName() != null ? serviceToUse.getPort(
152: this .portQName, getServiceInterface()) : serviceToUse
153: .getPort(getServiceInterface()));
154: }
155:
156: /**
157: * Return whether this client interceptor has already been prepared,
158: * i.e. has already looked up the JAX-WS service and port.
159: */
160: protected boolean isPrepared() {
161: synchronized (this .preparationMonitor) {
162: return (this .portStub != null);
163: }
164: }
165:
166: /**
167: * Return the prepared QName for the port.
168: * @see #setPortName
169: * @see #getQName
170: */
171: protected final QName getPortQName() {
172: return this .portQName;
173: }
174:
175: /**
176: * Return the underlying JAX-WS port stub that this interceptor delegates to
177: * for each method invocation on the proxy.
178: */
179: protected Object getPortStub() {
180: return this .portStub;
181: }
182:
183: public Object invoke(MethodInvocation invocation) throws Throwable {
184: if (AopUtils.isToStringMethod(invocation.getMethod())) {
185: return "JAX-WS proxy for port [" + getPortName()
186: + "] of service [" + getServiceName() + "]";
187: }
188: // Lazily prepare service and stub if necessary.
189: synchronized (this .preparationMonitor) {
190: if (!isPrepared()) {
191: prepare();
192: }
193: }
194: return doInvoke(invocation);
195: }
196:
197: /**
198: * Perform a JAX-WS service invocation based on the given method invocation.
199: * @param invocation the AOP method invocation
200: * @return the invocation result, if any
201: * @throws Throwable in case of invocation failure
202: * @see #getPortStub()
203: * @see #doInvoke(org.aopalliance.intercept.MethodInvocation, Object)
204: */
205: protected Object doInvoke(MethodInvocation invocation)
206: throws Throwable {
207: try {
208: return doInvoke(invocation, getPortStub());
209: } catch (SOAPFaultException ex) {
210: throw new JaxWsSoapFaultException(ex);
211: } catch (ProtocolException ex) {
212: return new RemoteConnectFailureException(
213: "Could not connect to remote service ["
214: + this .portQName + "]", ex);
215: } catch (WebServiceException ex) {
216: throw new RemoteAccessException(
217: "Could not access remote service at ["
218: + this .portQName + "]", ex);
219: }
220: }
221:
222: /**
223: * Perform a JAX-WS service invocation on the given port stub.
224: * @param invocation the AOP method invocation
225: * @param portStub the RMI port stub to invoke
226: * @return the invocation result, if any
227: * @throws Throwable in case of invocation failure
228: * @see #getPortStub()
229: */
230: protected Object doInvoke(MethodInvocation invocation,
231: Object portStub) throws Throwable {
232: Method method = invocation.getMethod();
233: try {
234: return method.invoke(portStub, invocation.getArguments());
235: } catch (InvocationTargetException ex) {
236: throw ex.getTargetException();
237: } catch (Throwable ex) {
238: throw new RemoteProxyFailureException(
239: "Invocation of stub method failed: " + method, ex);
240: }
241: }
242:
243: }
|