001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019:
020: package org.apache.axis2.jaxws.spi;
021:
022: import javax.xml.ws.handler.HandlerResolver;
023: import org.apache.axis2.client.ServiceClient;
024: import org.apache.axis2.java.security.AccessController;
025: import org.apache.axis2.jaxws.binding.BindingImpl;
026: import org.apache.axis2.jaxws.ExceptionFactory;
027: import org.apache.axis2.jaxws.client.PropertyMigrator;
028: import org.apache.axis2.jaxws.client.dispatch.JAXBDispatch;
029: import org.apache.axis2.jaxws.client.dispatch.XMLDispatch;
030: import org.apache.axis2.jaxws.client.proxy.JAXWSProxyHandler;
031: import org.apache.axis2.jaxws.description.DescriptionFactory;
032: import org.apache.axis2.jaxws.description.EndpointDescription;
033: import org.apache.axis2.jaxws.description.ServiceDescription;
034: import org.apache.axis2.jaxws.description.ServiceDescriptionWSDL;
035: import org.apache.axis2.jaxws.handler.HandlerResolverImpl;
036: import org.apache.axis2.jaxws.i18n.Messages;
037: import org.apache.axis2.jaxws.registry.FactoryRegistry;
038: import org.apache.axis2.jaxws.spi.migrator.ApplicationContextMigratorUtil;
039: import org.apache.axis2.jaxws.util.WSDLWrapper;
040: import org.apache.axis2.jaxws.utility.ExecutorFactory;
041: import org.apache.commons.logging.Log;
042: import org.apache.commons.logging.LogFactory;
043:
044: import javax.activation.DataSource;
045: import javax.xml.bind.JAXBContext;
046: import javax.xml.namespace.QName;
047: import javax.xml.soap.SOAPMessage;
048: import javax.xml.transform.Source;
049: import javax.xml.ws.Dispatch;
050: import javax.xml.ws.Service;
051: import javax.xml.ws.Service.Mode;
052: import javax.xml.ws.WebServiceException;
053: import javax.xml.ws.handler.HandlerResolver;
054: import javax.xml.ws.http.HTTPBinding;
055: import javax.xml.ws.soap.SOAPBinding;
056: import java.lang.reflect.Proxy;
057: import java.net.URL;
058: import java.security.PrivilegedActionException;
059: import java.security.PrivilegedExceptionAction;
060: import java.util.Iterator;
061: import java.util.concurrent.Executor;
062: import java.util.concurrent.Executors;
063: import java.util.concurrent.ThreadFactory;
064:
065: /**
066: * The ServiceDelegate serves as the backing implementation for all of the methods in the {@link
067: * javax.xml.ws.Service} API. This is the plug point for the client implementation.
068: */
069: public class ServiceDelegate extends javax.xml.ws.spi.ServiceDelegate {
070: private static final Log log = LogFactory
071: .getLog(ServiceDelegate.class);
072: private Executor executor;
073:
074: private ServiceDescription serviceDescription;
075: private QName serviceQname;
076: private ServiceClient serviceClient = null;
077:
078: private HandlerResolver handlerResolver = null;
079:
080: public ServiceDelegate(URL url, QName qname, Class clazz)
081: throws WebServiceException {
082: super ();
083: this .serviceQname = qname;
084:
085: if (!isValidServiceName()) {
086: throw ExceptionFactory.makeWebServiceException(Messages
087: .getMessage("serviceDelegateConstruct0", ""));
088: }
089: serviceDescription = DescriptionFactory
090: .createServiceDescription(url, serviceQname, clazz);
091: // TODO: This check should be done when the Service Description is created above; that should throw this exception.
092: // That is because we (following the behavior of the RI) require the WSDL be fully specified (not partial) on the client
093: if (isValidWSDLLocation()) {
094: if (!isServiceDefined(serviceQname)) {
095: throw ExceptionFactory
096: .makeWebServiceException(Messages
097: .getMessage(
098: "serviceDelegateConstruct0",
099: serviceQname.toString(), url
100: .toString()));
101: }
102: }
103:
104: // Register the necessary ApplicationContextMigrators
105: ApplicationContextMigratorUtil.addApplicationContextMigrator(
106: serviceDescription.getAxisConfigContext(),
107: Constants.APPLICATION_CONTEXT_MIGRATOR_LIST_ID,
108: new PropertyMigrator());
109: }
110:
111: //================================================
112: // JAX-WS API methods
113: //================================================
114:
115: /*
116: * (non-Javadoc)
117: * @see javax.xml.ws.spi.ServiceDelegate#addPort(javax.xml.namespace.QName, java.lang.String, java.lang.String)
118: */
119: // Creates a DISPATCH ONLY port. Per JAXWS Sec 4.1 javax.xm..ws.Service, p. 49, ports added via addPort method
120: // are only suitibale for creating Distpach instances.
121: public void addPort(QName portName, String bindingId,
122: String endpointAddress) throws WebServiceException {
123: if (endpointAddress != null
124: && endpointAddress.trim().length() == 0) {
125: ExceptionFactory.makeWebServiceException(Messages
126: .getMessage("addPortErr1",
127: (portName != null) ? portName
128: .getLocalPart() : "",
129: endpointAddress));
130: }
131: EndpointDescription endpointDesc = DescriptionFactory
132: .updateEndpoint(serviceDescription, null, portName,
133: DescriptionFactory.UpdateType.ADD_PORT);
134: // TODO: Need to set endpointAddress and set or check bindingId on the EndpointDesc
135: endpointDesc.setEndpointAddress(endpointAddress);
136: endpointDesc.setClientBindingID(bindingId);
137: }
138:
139: /*
140: * (non-Javadoc)
141: * @see javax.xml.ws.spi.ServiceDelegate#createDispatch(javax.xml.namespace.QName, java.lang.Class, javax.xml.ws.Service.Mode)
142: */
143: public <T> Dispatch<T> createDispatch(QName qname, Class<T> clazz,
144: Mode mode) throws WebServiceException {
145: if (qname == null) {
146: throw ExceptionFactory.makeWebServiceException(Messages
147: .getMessage("createDispatchFail0"));
148: }
149: if (!isValidDispatchType(clazz)) {
150: throw ExceptionFactory.makeWebServiceException(Messages
151: .getMessage("dispatchInvalidType"));
152: }
153:
154: EndpointDescription endpointDesc = DescriptionFactory
155: .updateEndpoint(serviceDescription, null, qname,
156: DescriptionFactory.UpdateType.CREATE_DISPATCH);
157: if (endpointDesc == null) {
158: throw ExceptionFactory
159: .makeWebServiceException(Messages.getMessage(
160: "createDispatchFail2", qname.toString()));
161: }
162:
163: XMLDispatch<T> dispatch = new XMLDispatch<T>(this , endpointDesc);
164:
165: // FIXME: This call needs to be revisited. Not really sure what we're trying to do here.
166: dispatch.setBinding(addBinding(endpointDesc, endpointDesc
167: .getClientBindingID()));
168: if (mode != null) {
169: dispatch.setMode(mode);
170: } else {
171: dispatch.setMode(Service.Mode.PAYLOAD);
172: }
173:
174: if (serviceClient == null)
175: serviceClient = getServiceClient(qname);
176:
177: dispatch.setServiceClient(serviceClient);
178: dispatch.setType(clazz);
179: return dispatch;
180: }
181:
182: /*
183: * (non-Javadoc)
184: * @see javax.xml.ws.spi.ServiceDelegate#createDispatch(javax.xml.namespace.QName, javax.xml.bind.JAXBContext, javax.xml.ws.Service.Mode)
185: */
186: public Dispatch<java.lang.Object> createDispatch(QName qname,
187: JAXBContext context, Mode mode) {
188: if (qname == null) {
189: throw ExceptionFactory.makeWebServiceException(Messages
190: .getMessage("createDispatchFail0"));
191: }
192:
193: EndpointDescription endpointDesc = DescriptionFactory
194: .updateEndpoint(serviceDescription, null, qname,
195: DescriptionFactory.UpdateType.CREATE_DISPATCH);
196: if (endpointDesc == null) {
197: throw ExceptionFactory
198: .makeWebServiceException(Messages.getMessage(
199: "createDispatchFail2", qname.toString()));
200: }
201:
202: JAXBDispatch<Object> dispatch = new JAXBDispatch(this ,
203: endpointDesc);
204: dispatch.setBinding(addBinding(endpointDesc, endpointDesc
205: .getClientBindingID()));
206:
207: if (mode != null) {
208: dispatch.setMode(mode);
209: } else {
210: dispatch.setMode(Service.Mode.PAYLOAD);
211: }
212:
213: if (serviceClient == null)
214: serviceClient = getServiceClient(qname);
215:
216: dispatch.setJAXBContext(context);
217: dispatch.setServiceClient(serviceClient);
218:
219: return dispatch;
220: }
221:
222: /*
223: * (non-Javadoc)
224: * @see javax.xml.ws.spi.ServiceDelegate#getPort(java.lang.Class)
225: */
226: public <T> T getPort(Class<T> sei) throws WebServiceException {
227: return getPort(null, sei);
228: }
229:
230: /*
231: * (non-Javadoc)
232: * @see javax.xml.ws.spi.ServiceDelegate#getPort(javax.xml.namespace.QName, java.lang.Class)
233: */
234: public <T> T getPort(QName portName, Class<T> sei)
235: throws WebServiceException {
236: /* TODO Check to see if WSDL Location is provided.
237: * if not check WebService annotation's WSDLLocation
238: * if both are not provided then throw exception.
239: * (JLB): I'm not sure lack of WSDL should cause an exception
240: */
241:
242: if (!isValidWSDLLocation()) {
243: //TODO: Should I throw Exception if no WSDL
244: //throw ExceptionFactory.makeWebServiceException("WSLD Not found");
245: }
246: if (sei == null) {
247: throw ExceptionFactory.makeWebServiceException(Messages
248: .getMessage("getPortInvalidSEI", portName
249: .toString(), "null"));
250: }
251:
252: EndpointDescription endpointDesc = DescriptionFactory
253: .updateEndpoint(serviceDescription, sei, portName,
254: DescriptionFactory.UpdateType.GET_PORT);
255: if (endpointDesc == null) {
256: // TODO: NLS
257: throw ExceptionFactory
258: .makeWebServiceException("Unable to getPort for port QName "
259: + portName.toString());
260: }
261:
262: String[] interfacesNames = new String[] {
263: sei.getName(),
264: org.apache.axis2.jaxws.spi.BindingProvider.class
265: .getName() };
266:
267: // As required by java.lang.reflect.Proxy, ensure that the interfaces
268: // for the proxy are loadable by the same class loader.
269: Class[] interfaces = null;
270: // First, let's try loading the interfaces with the SEI classLoader
271: ClassLoader classLoader = getClassLoader(sei);
272: try {
273: interfaces = loadClasses(classLoader, interfacesNames);
274: } catch (ClassNotFoundException e1) {
275: // Let's try with context classLoader now
276: classLoader = getContextClassLoader();
277: try {
278: interfaces = loadClasses(classLoader, interfacesNames);
279: } catch (ClassNotFoundException e2) {
280: // TODO: NLS
281: throw ExceptionFactory.makeWebServiceException(
282: "Unable to load proxy classes", e2);
283: }
284: }
285:
286: JAXWSProxyHandler proxyHandler = new JAXWSProxyHandler(this ,
287: interfaces[0], endpointDesc);
288: Object proxyClass = Proxy.newProxyInstance(classLoader,
289: interfaces, proxyHandler);
290: return sei.cast(proxyClass);
291: }
292:
293: /*
294: * (non-Javadoc)
295: * @see javax.xml.ws.spi.ServiceDelegate#getExecutor()
296: */
297: public Executor getExecutor() {
298: //FIXME: Use client provider executor too.
299: if (executor == null) {
300: executor = getDefaultExecutor();
301: }
302: return executor;
303: }
304:
305: /*
306: * (non-Javadoc)
307: * @see javax.xml.ws.spi.ServiceDelegate#getHandlerResolver()
308: */
309: public HandlerResolver getHandlerResolver() {
310: if (handlerResolver == null) {
311: handlerResolver = new HandlerResolverImpl(
312: serviceDescription);
313: }
314: return handlerResolver;
315: }
316:
317: /*
318: * (non-Javadoc)
319: * @see javax.xml.ws.spi.ServiceDelegate#getPorts()
320: */
321: public Iterator<QName> getPorts() {
322: return getServiceDescription().getPorts().iterator();
323: }
324:
325: /*
326: * (non-Javadoc)
327: * @see javax.xml.ws.spi.ServiceDelegate#getServiceName()
328: */
329: public QName getServiceName() {
330: return serviceQname;
331: }
332:
333: /*
334: * (non-Javadoc)
335: * @see javax.xml.ws.spi.ServiceDelegate#getWSDLDocumentLocation()
336: */
337: public URL getWSDLDocumentLocation() {
338: return ((ServiceDescriptionWSDL) serviceDescription)
339: .getWSDLLocation();
340: }
341:
342: /*
343: * (non-Javadoc)
344: * @see javax.xml.ws.spi.ServiceDelegate#setExecutor(java.util.concurrent.Executor)
345: */
346: public void setExecutor(Executor e) {
347: if (e == null) {
348: throw ExceptionFactory.makeWebServiceException(Messages
349: .getMessage("cannotSetExcutorToNull"));
350: }
351:
352: executor = e;
353: }
354:
355: /*
356: * (non-Javadoc)
357: * @see javax.xml.ws.spi.ServiceDelegate#setHandlerResolver(javax.xml.ws.handler.HandlerResolver)
358: */
359: public void setHandlerResolver(HandlerResolver handlerresolver) {
360: this .handlerResolver = handlerresolver;
361: }
362:
363: //================================================
364: // Internal public APIs
365: //================================================
366:
367: /** Get the ServiceDescription tree that this ServiceDelegate */
368: public ServiceDescription getServiceDescription() {
369: return serviceDescription;
370: }
371:
372: //TODO Change when ServiceDescription has to return ServiceClient or OperationClient
373:
374: /**
375: *
376: */
377: public ServiceClient getServiceClient(QName portQName)
378: throws WebServiceException {
379: return serviceDescription.getServiceClient(portQName);
380: }
381:
382: //================================================
383: // Impl methods
384: //================================================
385:
386: //TODO: Need to make the default number of threads configurable
387: private Executor getDefaultExecutor() {
388: ExecutorFactory executorFactory = (ExecutorFactory) FactoryRegistry
389: .getFactory(ExecutorFactory.class);
390: return executorFactory.getExecutorInstance();
391: }
392:
393: private boolean isValidServiceName() {
394: return serviceQname != null
395: && !"".equals(serviceQname.toString().trim());
396: }
397:
398: private boolean isValidWSDLLocation() {
399: URL wsdlLocation = getWSDLDocumentLocation();
400: return wsdlLocation != null
401: && !"".equals(wsdlLocation.toString().trim());
402: }
403:
404: // TODO: Remove this method and put the WSDLWrapper methods on the ServiceDescriptor directly
405: private WSDLWrapper getWSDLWrapper() {
406: return ((ServiceDescriptionWSDL) serviceDescription)
407: .getWSDLWrapper();
408: }
409:
410: private boolean isServiceDefined(QName serviceName) {
411: return getWSDLWrapper().getService(serviceName) != null;
412: }
413:
414: private BindingImpl addBinding(EndpointDescription endpointDesc,
415: String bindingId) {
416: // TODO: before creating binding do I have to do something with Handlers ... how is Binding related to Handler, this mistry sucks!!!
417: if (bindingId != null) {
418: //TODO: create all the bindings here
419: if (bindingId.equals(SOAPBinding.SOAP11HTTP_BINDING)) {
420: //instantiate soap11 binding implementation here and call setBinding in BindingProvider
421: return new org.apache.axis2.jaxws.binding.SOAPBinding(
422: endpointDesc);
423: }
424:
425: if (bindingId.equals(SOAPBinding.SOAP12HTTP_BINDING)) {
426: //instantiate soap11 binding implementation here and call setBinding in BindingProvider
427: return new org.apache.axis2.jaxws.binding.SOAPBinding(
428: endpointDesc);
429: }
430:
431: if (bindingId.equals(HTTPBinding.HTTP_BINDING)) {
432: //instantiate http binding implementation here and call setBinding in BindingProvider
433: return new org.apache.axis2.jaxws.binding.HTTPBinding(
434: endpointDesc);
435: }
436: }
437: return new org.apache.axis2.jaxws.binding.SOAPBinding(
438: endpointDesc);
439: }
440:
441: private boolean isValidDispatchType(Class clazz) {
442: return clazz != null
443: && (clazz == String.class || clazz == Source.class
444: || clazz == DataSource.class || clazz == SOAPMessage.class);
445: }
446:
447: /** @return ClassLoader */
448: private static ClassLoader getClassLoader(final Class cls) {
449: // NOTE: This method must remain private because it uses AccessController
450: ClassLoader cl = null;
451: try {
452: cl = (ClassLoader) AccessController
453: .doPrivileged(new PrivilegedExceptionAction() {
454: public Object run()
455: throws ClassNotFoundException {
456: return cls.getClassLoader();
457: }
458: });
459: } catch (PrivilegedActionException e) {
460: if (log.isDebugEnabled()) {
461: log.debug("Exception thrown from AccessController: "
462: + e);
463: }
464: throw ExceptionFactory.makeWebServiceException(e
465: .getException());
466: }
467:
468: return cl;
469: }
470:
471: /** @return ClassLoader */
472: private static ClassLoader getContextClassLoader() {
473: // NOTE: This method must remain private because it uses AccessController
474: ClassLoader cl = null;
475: try {
476: cl = (ClassLoader) AccessController
477: .doPrivileged(new PrivilegedExceptionAction() {
478: public Object run()
479: throws ClassNotFoundException {
480: return Thread.currentThread()
481: .getContextClassLoader();
482: }
483: });
484: } catch (PrivilegedActionException e) {
485: if (log.isDebugEnabled()) {
486: log.debug("Exception thrown from AccessController: "
487: + e);
488: }
489: throw ExceptionFactory.makeWebServiceException(e
490: .getException());
491: }
492:
493: return cl;
494: }
495:
496: /**
497: * Return the class for this name
498: *
499: * @return Class
500: */
501: private static Class forName(final String className,
502: final boolean initialize, final ClassLoader classLoader)
503: throws ClassNotFoundException {
504: // NOTE: This method must remain protected because it uses AccessController
505: Class cl = null;
506: try {
507: cl = (Class) AccessController
508: .doPrivileged(new PrivilegedExceptionAction() {
509: public Object run()
510: throws ClassNotFoundException {
511: return Class.forName(className, initialize,
512: classLoader);
513: }
514: });
515: } catch (PrivilegedActionException e) {
516: if (log.isDebugEnabled()) {
517: log.debug("Exception thrown from AccessController: "
518: + e);
519: }
520: throw (ClassNotFoundException) e.getException();
521: }
522:
523: return cl;
524: }
525:
526: private static Class[] loadClasses(ClassLoader classLoader,
527: String[] classNames) throws ClassNotFoundException {
528: Class[] classes = new Class[classNames.length];
529: for (int i = 0; i < classNames.length; i++) {
530: classes[i] = forName(classNames[i], true, classLoader);
531: }
532: return classes;
533: }
534:
535: }
|