View Javadoc
1   /*
2    * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package com.sun.xml.internal.ws.client;
27  
28  import com.sun.istack.internal.NotNull;
29  import com.sun.istack.internal.Nullable;
30  import com.sun.xml.internal.ws.Closeable;
31  import com.sun.xml.internal.ws.api.BindingID;
32  import com.sun.xml.internal.ws.api.ComponentFeature;
33  import com.sun.xml.internal.ws.api.ComponentsFeature;
34  import com.sun.xml.internal.ws.api.ComponentFeature.Target;
35  import com.sun.xml.internal.ws.api.EndpointAddress;
36  import com.sun.xml.internal.ws.api.WSService;
37  import com.sun.xml.internal.ws.api.addressing.WSEndpointReference;
38  import com.sun.xml.internal.ws.api.client.ServiceInterceptor;
39  import com.sun.xml.internal.ws.api.client.ServiceInterceptorFactory;
40  import com.sun.xml.internal.ws.api.databinding.DatabindingConfig;
41  import com.sun.xml.internal.ws.api.databinding.DatabindingFactory;
42  import com.sun.xml.internal.ws.api.databinding.MetadataReader;
43  import com.sun.xml.internal.ws.api.model.SEIModel;
44  import com.sun.xml.internal.ws.api.model.wsdl.WSDLModel;
45  import com.sun.xml.internal.ws.api.model.wsdl.WSDLPort;
46  import com.sun.xml.internal.ws.api.model.wsdl.WSDLService;
47  import com.sun.xml.internal.ws.api.pipe.Stubs;
48  import com.sun.xml.internal.ws.api.server.Container;
49  import com.sun.xml.internal.ws.api.server.ContainerResolver;
50  import com.sun.xml.internal.ws.api.wsdl.parser.WSDLParserExtension;
51  import com.sun.xml.internal.ws.binding.BindingImpl;
52  import com.sun.xml.internal.ws.binding.WebServiceFeatureList;
53  import com.sun.xml.internal.ws.client.HandlerConfigurator.AnnotationConfigurator;
54  import com.sun.xml.internal.ws.client.HandlerConfigurator.HandlerResolverImpl;
55  import com.sun.xml.internal.ws.client.sei.SEIStub;
56  import com.sun.xml.internal.ws.developer.MemberSubmissionAddressingFeature;
57  import com.sun.xml.internal.ws.developer.UsesJAXBContextFeature;
58  import com.sun.xml.internal.ws.developer.WSBindingProvider;
59  import com.sun.xml.internal.ws.model.RuntimeModeler;
60  import com.sun.xml.internal.ws.model.SOAPSEIModel;
61  import com.sun.xml.internal.ws.resources.ClientMessages;
62  import com.sun.xml.internal.ws.resources.DispatchMessages;
63  import com.sun.xml.internal.ws.resources.ProviderApiMessages;
64  import com.sun.xml.internal.ws.util.JAXWSUtils;
65  import com.sun.xml.internal.ws.util.ServiceConfigurationError;
66  import com.sun.xml.internal.ws.util.ServiceFinder;
67  import com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser;
68  
69  import org.xml.sax.EntityResolver;
70  import org.xml.sax.SAXException;
71  
72  import javax.jws.HandlerChain;
73  import javax.jws.WebService;
74  import javax.xml.bind.JAXBContext;
75  import javax.xml.namespace.QName;
76  import javax.xml.stream.XMLStreamException;
77  import javax.xml.transform.Source;
78  import javax.xml.transform.stream.StreamSource;
79  import javax.xml.ws.BindingProvider;
80  import javax.xml.ws.Dispatch;
81  import javax.xml.ws.EndpointReference;
82  import javax.xml.ws.Service;
83  import javax.xml.ws.WebServiceClient;
84  import javax.xml.ws.WebServiceException;
85  import javax.xml.ws.WebServiceFeature;
86  import javax.xml.ws.handler.HandlerResolver;
87  import javax.xml.ws.soap.AddressingFeature;
88  
89  import java.io.IOException;
90  import java.lang.reflect.InvocationHandler;
91  import java.lang.reflect.Proxy;
92  import java.net.MalformedURLException;
93  import java.net.URL;
94  import java.security.*;
95  import java.util.Collection;
96  import java.util.HashMap;
97  import java.util.HashSet;
98  import java.util.Iterator;
99  import java.util.Map;
100 import java.util.Set;
101 import java.util.concurrent.Executor;
102 import java.util.concurrent.ThreadFactory;
103 
104 import static com.sun.xml.internal.ws.util.xml.XmlUtil.createDefaultCatalogResolver;
105 
106 /**
107  * <code>Service</code> objects provide the client view of a Web service.
108  *
109  * <p><code>Service</code> acts as a factory of the following:
110  * <ul>
111  * <li>Proxies for a target service endpoint.
112  * <li>Instances of <code>javax.xml.ws.Dispatch</code> for
113  * dynamic message-oriented invocation of a remote
114  * operation.
115  * </li>
116  *
117  * <p>The ports available on a service can be enumerated using the
118  * <code>getPorts</code> method. Alternatively, you can pass a
119  * service endpoint interface to the unary <code>getPort</code> method
120  * and let the runtime select a compatible port.
121  *
122  * <p>Handler chains for all the objects created by a <code>Service</code>
123  * can be set by means of the provided <code>HandlerRegistry</code>.
124  *
125  * <p>An <code>Executor</code> may be set on the service in order
126  * to gain better control over the threads used to dispatch asynchronous
127  * callbacks. For instance, thread pooling with certain parameters
128  * can be enabled by creating a <code>ThreadPoolExecutor</code> and
129  * registering it with the service.
130  *
131  * @author WS Development Team
132  * @see Executor
133  * @since JAX-WS 2.0
134  */
135 public class WSServiceDelegate extends WSService {
136     /**
137      * All ports.
138      * <p>
139      * This includes ports statically known to WSDL, as well as
140      * ones that are dynamically added
141      * through {@link #addPort(QName, String, String)}.
142      * <p>
143      * For statically known ports we'll have {@link SEIPortInfo}.
144      * For dynamically added ones we'll have {@link PortInfo}.
145      */
146     private final Map<QName, PortInfo> ports = new HashMap<QName, PortInfo>();
147     // For monitoring
148     protected Map<QName, PortInfo> getQNameToPortInfoMap() { return ports; }
149 
150     /**
151      * Whenever we create {@link BindingProvider}, we use this to configure handlers.
152      */
153     private @NotNull HandlerConfigurator handlerConfigurator = new HandlerResolverImpl(null);
154 
155     private final Class<? extends Service> serviceClass;
156 
157     private final WebServiceFeatureList features;
158 
159     /**
160      * Name of the service for which this {@link WSServiceDelegate} is created for.
161      */
162     private final @NotNull QName serviceName;
163 
164     /**
165      * Information about SEI, keyed by their interface type.
166      */
167    // private final Map<Class,SEIPortInfo> seiContext = new HashMap<Class,SEIPortInfo>();
168    private final Map<QName,SEIPortInfo> seiContext = new HashMap<QName,SEIPortInfo>();
169 
170     // This executor is used for all the async invocations for all proxies
171     // created from this service. But once the proxy is created, then changing
172     // this executor doesn't affect the already created proxies.
173     private volatile Executor executor;
174 
175     /**
176      * The WSDL service that this {@link Service} object represents.
177      * <p>
178      * This field is null iff no WSDL is given to {@link Service}.
179      * This fiels can be be null if the service is created without wsdl but later
180      * the epr supplies a wsdl that can be parsed.
181      */
182     private  @Nullable WSDLService wsdlService;
183 
184     private final Container container;
185     /**
186      * Multiple {@link ServiceInterceptor}s are aggregated into one.
187      */
188     /*package*/ final @NotNull ServiceInterceptor serviceInterceptor;
189     private URL wsdlURL;
190 
191     public WSServiceDelegate(URL wsdlDocumentLocation, QName serviceName, Class<? extends Service> serviceClass, WebServiceFeature... features) {
192         this(wsdlDocumentLocation, serviceName, serviceClass, new WebServiceFeatureList(features));
193     }
194 
195     protected WSServiceDelegate(URL wsdlDocumentLocation, QName serviceName, Class<? extends Service> serviceClass, WebServiceFeatureList features) {
196         this(
197             wsdlDocumentLocation==null ? null : new StreamSource(wsdlDocumentLocation.toExternalForm()),
198             serviceName,serviceClass, features);
199         wsdlURL = wsdlDocumentLocation;
200     }
201 
202     /**
203      * @param serviceClass
204      *      Either {@link Service}.class or other generated service-derived classes.
205      */
206     public WSServiceDelegate(@Nullable Source wsdl, @NotNull QName serviceName, @NotNull final Class<? extends Service> serviceClass, WebServiceFeature... features) {
207         this(wsdl, serviceName, serviceClass, new WebServiceFeatureList(features));
208     }
209 
210     /**
211      * @param serviceClass
212      *      Either {@link Service}.class or other generated service-derived classes.
213      */
214     protected WSServiceDelegate(@Nullable Source wsdl, @NotNull QName serviceName, @NotNull final Class<? extends Service> serviceClass, WebServiceFeatureList features) {
215         this(wsdl, null, serviceName, serviceClass, features);
216     }
217 
218     /**
219      * @param serviceClass
220      *      Either {@link Service}.class or other generated service-derived classes.
221      */
222     public WSServiceDelegate(@Nullable Source wsdl, @Nullable WSDLService service, @NotNull QName serviceName, @NotNull final Class<? extends Service> serviceClass, WebServiceFeature... features) {
223         this(wsdl, service, serviceName, serviceClass, new WebServiceFeatureList(features));
224     }
225 
226     /**
227      * @param serviceClass
228      *      Either {@link Service}.class or other generated service-derived classes.
229      */
230     public WSServiceDelegate(@Nullable Source wsdl, @Nullable WSDLService service, @NotNull QName serviceName, @NotNull final Class<? extends Service> serviceClass, WebServiceFeatureList features) {
231         //we cant create a Service without serviceName
232         if (serviceName == null) {
233             throw new WebServiceException(ClientMessages.INVALID_SERVICE_NAME_NULL(null));
234         }
235 
236         this.features = features;
237 
238         InitParams initParams = INIT_PARAMS.get();
239         INIT_PARAMS.set(null);  // mark it as consumed
240         if(initParams==null) {
241             initParams = EMPTY_PARAMS;
242         }
243 
244         this.serviceName = serviceName;
245         this.serviceClass = serviceClass;
246         Container tContainer = initParams.getContainer()!=null ? initParams.getContainer() : ContainerResolver.getInstance().getContainer();
247         if (tContainer == Container.NONE) {
248             tContainer = new ClientContainer();
249         }
250         this.container = tContainer;
251 
252         ComponentFeature cf = this.features.get(ComponentFeature.class);
253         if (cf != null) {
254             switch(cf.getTarget()) {
255                 case SERVICE:
256                     getComponents().add(cf.getComponent());
257                     break;
258                 case CONTAINER:
259                     this.container.getComponents().add(cf.getComponent());
260                     break;
261                 default:
262                     throw new IllegalArgumentException();
263             }
264         }
265         ComponentsFeature csf = this.features.get(ComponentsFeature.class);
266         if (csf != null) {
267             for (ComponentFeature cfi : csf.getComponentFeatures()) {
268                 switch(cfi.getTarget()) {
269                     case SERVICE:
270                         getComponents().add(cfi.getComponent());
271                         break;
272                     case CONTAINER:
273                         this.container.getComponents().add(cfi.getComponent());
274                         break;
275                     default:
276                         throw new IllegalArgumentException();
277                 }
278             }
279         }
280 
281         // load interceptor
282         ServiceInterceptor interceptor = ServiceInterceptorFactory.load(this, Thread.currentThread().getContextClassLoader());
283         ServiceInterceptor si = container.getSPI(ServiceInterceptor.class);
284         if (si != null) {
285             interceptor = ServiceInterceptor.aggregate(interceptor, si);
286         }
287         this.serviceInterceptor = interceptor;
288 
289         if (service == null) {
290                 //if wsdl is null, try and get it from the WebServiceClient.wsdlLocation
291                 if(wsdl == null){
292                     if(serviceClass != Service.class){
293                         WebServiceClient wsClient = AccessController.doPrivileged(new PrivilegedAction<WebServiceClient>() {
294                                 public WebServiceClient run() {
295                                     return serviceClass.getAnnotation(WebServiceClient.class);
296                                 }
297                             });
298                         String wsdlLocation = wsClient.wsdlLocation();
299                         wsdlLocation = JAXWSUtils.absolutize(JAXWSUtils.getFileOrURLName(wsdlLocation));
300                         wsdl = new StreamSource(wsdlLocation);
301                     }
302                 }
303                 if (wsdl != null) {
304                     try {
305                         URL url = wsdl.getSystemId()==null ? null : JAXWSUtils.getEncodedURL(wsdl.getSystemId());
306                         WSDLModel model = parseWSDL(url, wsdl, serviceClass);
307                         service = model.getService(this.serviceName);
308                         if (service == null)
309                             throw new WebServiceException(
310                                 ClientMessages.INVALID_SERVICE_NAME(this.serviceName,
311                                     buildNameList(model.getServices().keySet())));
312                         // fill in statically known ports
313                         for (WSDLPort port : service.getPorts())
314                             ports.put(port.getName(), new PortInfo(this, port));
315                     } catch (MalformedURLException e) {
316                         throw new WebServiceException(ClientMessages.INVALID_WSDL_URL(wsdl.getSystemId()));
317                     }
318                 }
319         } else {
320             // fill in statically known ports
321             for (WSDLPort port : service.getPorts())
322                 ports.put(port.getName(), new PortInfo(this, port));
323         }
324         this.wsdlService = service;
325 
326         if (serviceClass != Service.class) {
327             //if @HandlerChain present, set HandlerResolver on service context
328             HandlerChain handlerChain =
329                     AccessController.doPrivileged(new PrivilegedAction<HandlerChain>() {
330                         public HandlerChain run() {
331                             return serviceClass.getAnnotation(HandlerChain.class);
332                         }
333                     });
334             if (handlerChain != null)
335                 handlerConfigurator = new AnnotationConfigurator(this);
336         }
337 
338     }
339 
340     /**
341      * Parses the WSDL and builds {@link com.sun.xml.internal.ws.api.model.wsdl.WSDLModel}.
342      * @param wsdlDocumentLocation
343      *      Either this or <tt>wsdl</tt> parameter must be given.
344      *      Null location means the system won't be able to resolve relative references in the WSDL,
345      */
346     private WSDLModel parseWSDL(URL wsdlDocumentLocation, Source wsdlSource, Class serviceClass) {
347         try {
348             return RuntimeWSDLParser.parse(wsdlDocumentLocation, wsdlSource, createCatalogResolver(),
349                 true, getContainer(), serviceClass, ServiceFinder.find(WSDLParserExtension.class).toArray());
350         } catch (IOException e) {
351             throw new WebServiceException(e);
352         } catch (XMLStreamException e) {
353             throw new WebServiceException(e);
354         } catch (SAXException e) {
355             throw new WebServiceException(e);
356         } catch (ServiceConfigurationError e) {
357             throw new WebServiceException(e);
358         }
359     }
360 
361     protected EntityResolver createCatalogResolver() {
362         return createDefaultCatalogResolver();
363     }
364 
365     public Executor getExecutor() {
366         return executor;
367     }
368 
369     public void setExecutor(Executor executor) {
370         this.executor = executor;
371     }
372 
373     public HandlerResolver getHandlerResolver() {
374         return handlerConfigurator.getResolver();
375     }
376 
377     /*package*/ final HandlerConfigurator getHandlerConfigurator() {
378         return handlerConfigurator;
379     }
380 
381     public void setHandlerResolver(HandlerResolver resolver) {
382         handlerConfigurator = new HandlerResolverImpl(resolver);
383     }
384 
385     public <T> T getPort(QName portName, Class<T> portInterface) throws WebServiceException {
386         return getPort(portName, portInterface, EMPTY_FEATURES);
387     }
388 
389     public <T> T getPort(QName portName, Class<T> portInterface, WebServiceFeature... features) {
390         if (portName == null || portInterface == null)
391             throw new IllegalArgumentException();
392         WSDLService tWsdlService = this.wsdlService;
393         if (tWsdlService == null) {
394             // assigning it to local variable and not setting it back to this.wsdlService intentionally
395             // as we don't want to include the service instance with information gathered from sei
396             tWsdlService = getWSDLModelfromSEI(portInterface);
397             //still null? throw error need wsdl metadata to create a proxy
398             if (tWsdlService == null) {
399                 throw new WebServiceException(ProviderApiMessages.NO_WSDL_NO_PORT(portInterface.getName()));
400             }
401 
402         }
403         WSDLPort portModel = getPortModel(tWsdlService, portName);
404         return getPort(portModel.getEPR(), portName, portInterface, new WebServiceFeatureList(features));
405     }
406 
407     public <T> T getPort(EndpointReference epr, Class<T> portInterface, WebServiceFeature... features) {
408         return getPort(WSEndpointReference.create(epr),portInterface,features);
409     }
410 
411     public <T> T getPort(WSEndpointReference wsepr, Class<T> portInterface, WebServiceFeature... features) {
412         //get the portType from SEI, so that it can be used if EPR does n't have endpointName
413         WebServiceFeatureList featureList = new WebServiceFeatureList(features);
414         QName portTypeName = RuntimeModeler.getPortTypeName(portInterface, getMetadadaReader(featureList, portInterface.getClassLoader()));
415         //if port name is not specified in EPR, it will use portTypeName to get it from the WSDL model.
416         QName portName = getPortNameFromEPR(wsepr, portTypeName);
417         return getPort(wsepr,portName,portInterface, featureList);
418     }
419 
420     protected <T> T getPort(WSEndpointReference wsepr, QName portName, Class<T> portInterface,
421                           WebServiceFeatureList features) {
422         ComponentFeature cf = features.get(ComponentFeature.class);
423         if (cf != null && !Target.STUB.equals(cf.getTarget())) {
424             throw new IllegalArgumentException();
425         }
426         ComponentsFeature csf = features.get(ComponentsFeature.class);
427         if (csf != null) {
428             for (ComponentFeature cfi : csf.getComponentFeatures()) {
429                 if (!Target.STUB.equals(cfi.getTarget()))
430                     throw new IllegalArgumentException();
431             }
432         }
433         features.addAll(this.features);
434 
435         SEIPortInfo spi = addSEI(portName, portInterface, features);
436         return createEndpointIFBaseProxy(wsepr,portName,portInterface,features, spi);
437     }
438 
439     @Override
440     public <T> T getPort(Class<T> portInterface, WebServiceFeature... features) {
441         //get the portType from SEI
442         QName portTypeName = RuntimeModeler.getPortTypeName(portInterface, getMetadadaReader(new WebServiceFeatureList(features), portInterface.getClassLoader()));
443         WSDLService tmpWsdlService = this.wsdlService;
444         if (tmpWsdlService == null) {
445             // assigning it to local variable and not setting it back to this.wsdlService intentionally
446             // as we don't want to include the service instance with information gathered from sei
447             tmpWsdlService = getWSDLModelfromSEI(portInterface);
448             //still null? throw error need wsdl metadata to create a proxy
449             if(tmpWsdlService == null) {
450                 throw new WebServiceException(ProviderApiMessages.NO_WSDL_NO_PORT(portInterface.getName()));
451             }
452         }
453         //get the first port corresponding to the SEI
454         WSDLPort port = tmpWsdlService.getMatchingPort(portTypeName);
455         if (port == null) {
456             throw new WebServiceException(ClientMessages.UNDEFINED_PORT_TYPE(portTypeName));
457         }
458         QName portName = port.getName();
459         return getPort(portName, portInterface,features);
460     }
461 
462     public <T> T getPort(Class<T> portInterface) throws WebServiceException {
463         return getPort(portInterface, EMPTY_FEATURES);
464     }
465 
466     public void addPort(QName portName, String bindingId, String endpointAddress) throws WebServiceException {
467         if (!ports.containsKey(portName)) {
468             BindingID bid = (bindingId == null) ? BindingID.SOAP11_HTTP : BindingID.parse(bindingId);
469             ports.put(portName,
470                     new PortInfo(this, (endpointAddress == null) ? null :
471                             EndpointAddress.create(endpointAddress), portName, bid));
472         } else
473             throw new WebServiceException(DispatchMessages.DUPLICATE_PORT(portName.toString()));
474     }
475 
476 
477     public <T> Dispatch<T> createDispatch(QName portName, Class<T>  aClass, Service.Mode mode) throws WebServiceException {
478         return createDispatch(portName, aClass, mode, EMPTY_FEATURES);
479     }
480 
481     @Override
482     public <T> Dispatch<T> createDispatch(QName portName, WSEndpointReference wsepr, Class<T> aClass, Service.Mode mode, WebServiceFeature... features) {
483         return createDispatch(portName, wsepr, aClass, mode, new WebServiceFeatureList(features));
484     }
485 
486     public <T> Dispatch<T> createDispatch(QName portName, WSEndpointReference wsepr, Class<T> aClass, Service.Mode mode, WebServiceFeatureList features) {
487         PortInfo port = safeGetPort(portName);
488 
489         ComponentFeature cf = features.get(ComponentFeature.class);
490         if (cf != null && !Target.STUB.equals(cf.getTarget())) {
491             throw new IllegalArgumentException();
492         }
493         ComponentsFeature csf = features.get(ComponentsFeature.class);
494         if (csf != null) {
495             for (ComponentFeature cfi : csf.getComponentFeatures()) {
496                 if (!Target.STUB.equals(cfi.getTarget()))
497                     throw new IllegalArgumentException();
498             }
499         }
500         features.addAll(this.features);
501 
502         BindingImpl binding = port.createBinding(features, null, null);
503         binding.setMode(mode);
504         Dispatch<T> dispatch = Stubs.createDispatch(port, this, binding, aClass, mode, wsepr);
505         serviceInterceptor.postCreateDispatch((WSBindingProvider) dispatch);
506         return dispatch;
507     }
508 
509     public <T> Dispatch<T> createDispatch(QName portName, Class<T> aClass, Service.Mode mode, WebServiceFeature... features) {
510         return createDispatch(portName, aClass, mode, new WebServiceFeatureList(features));
511     }
512 
513     public <T> Dispatch<T> createDispatch(QName portName, Class<T> aClass, Service.Mode mode, WebServiceFeatureList features) {
514         WSEndpointReference wsepr = null;
515         boolean isAddressingEnabled = false;
516         AddressingFeature af = features.get(AddressingFeature.class);
517         if (af == null) {
518             af = this.features.get(AddressingFeature.class);
519         }
520         if (af != null && af.isEnabled())
521             isAddressingEnabled = true;
522         MemberSubmissionAddressingFeature msa = features.get(MemberSubmissionAddressingFeature.class);
523         if (msa == null) {
524             msa = this.features.get(MemberSubmissionAddressingFeature.class);
525         }
526         if (msa != null && msa.isEnabled())
527             isAddressingEnabled = true;
528         if(isAddressingEnabled && wsdlService != null && wsdlService.get(portName) != null) {
529             wsepr = wsdlService.get(portName).getEPR();
530         }
531         return createDispatch(portName, wsepr, aClass, mode, features);
532     }
533 
534     public <T> Dispatch<T> createDispatch(EndpointReference endpointReference, Class<T> type, Service.Mode mode, WebServiceFeature... features) {
535         WSEndpointReference wsepr = new WSEndpointReference(endpointReference);
536         QName portName = addPortEpr(wsepr);
537         return createDispatch(portName, wsepr, type, mode, features);
538     }
539 
540     /**
541      * Obtains {@link PortInfo} for the given name, with error check.
542      */
543     public
544     @NotNull
545     PortInfo safeGetPort(QName portName) {
546         PortInfo port = ports.get(portName);
547         if (port == null) {
548             throw new WebServiceException(ClientMessages.INVALID_PORT_NAME(portName, buildNameList(ports.keySet())));
549         }
550         return port;
551     }
552 
553     private StringBuilder buildNameList(Collection<QName> names) {
554         StringBuilder sb = new StringBuilder();
555         for (QName qn : names) {
556             if (sb.length() > 0) sb.append(',');
557             sb.append(qn);
558         }
559         return sb;
560     }
561 
562     public EndpointAddress getEndpointAddress(QName qName) {
563         PortInfo p = ports.get(qName);
564         return p != null ? p.targetEndpoint : null;
565     }
566 
567     public Dispatch<Object> createDispatch(QName portName, JAXBContext jaxbContext, Service.Mode mode) throws WebServiceException {
568         return createDispatch(portName, jaxbContext, mode, EMPTY_FEATURES);
569     }
570 
571     @Override
572     public Dispatch<Object> createDispatch(QName portName, WSEndpointReference wsepr, JAXBContext jaxbContext, Service.Mode mode, WebServiceFeature... features) {
573         return createDispatch(portName, wsepr, jaxbContext, mode, new WebServiceFeatureList(features));
574     }
575 
576     protected Dispatch<Object> createDispatch(QName portName, WSEndpointReference wsepr, JAXBContext jaxbContext, Service.Mode mode, WebServiceFeatureList features) {
577         PortInfo port = safeGetPort(portName);
578 
579         ComponentFeature cf = features.get(ComponentFeature.class);
580         if (cf != null && !Target.STUB.equals(cf.getTarget())) {
581             throw new IllegalArgumentException();
582         }
583         ComponentsFeature csf = features.get(ComponentsFeature.class);
584         if (csf != null) {
585             for (ComponentFeature cfi : csf.getComponentFeatures()) {
586                 if (!Target.STUB.equals(cfi.getTarget()))
587                     throw new IllegalArgumentException();
588             }
589         }
590         features.addAll(this.features);
591 
592         BindingImpl binding = port.createBinding(features, null, null);
593         binding.setMode(mode);
594         Dispatch<Object> dispatch = Stubs.createJAXBDispatch(
595                 port, binding, jaxbContext, mode,wsepr);
596          serviceInterceptor.postCreateDispatch((WSBindingProvider)dispatch);
597          return dispatch;
598     }
599 
600     @Override
601     public @NotNull Container getContainer() {
602         return container;
603     }
604 
605     public Dispatch<Object> createDispatch(QName portName, JAXBContext jaxbContext, Service.Mode mode, WebServiceFeature... webServiceFeatures) {
606         return createDispatch(portName, jaxbContext, mode, new WebServiceFeatureList(webServiceFeatures));
607     }
608 
609     protected Dispatch<Object> createDispatch(QName portName, JAXBContext jaxbContext, Service.Mode mode, WebServiceFeatureList features) {
610         WSEndpointReference wsepr = null;
611         boolean isAddressingEnabled = false;
612         AddressingFeature af = features.get(AddressingFeature.class);
613         if (af == null) {
614             af = this.features.get(AddressingFeature.class);
615         }
616         if (af != null && af.isEnabled())
617             isAddressingEnabled = true;
618         MemberSubmissionAddressingFeature msa = features.get(MemberSubmissionAddressingFeature.class);
619         if (msa == null) {
620             msa = this.features.get(MemberSubmissionAddressingFeature.class);
621         }
622         if (msa != null && msa.isEnabled())
623             isAddressingEnabled = true;
624         if(isAddressingEnabled && wsdlService != null && wsdlService.get(portName) != null) {
625             wsepr = wsdlService.get(portName).getEPR();
626         }
627         return createDispatch(portName, wsepr, jaxbContext, mode, features);
628     }
629 
630     public Dispatch<Object> createDispatch(EndpointReference endpointReference, JAXBContext context, Service.Mode mode, WebServiceFeature... features) {
631         WSEndpointReference wsepr = new WSEndpointReference(endpointReference);
632         QName portName = addPortEpr(wsepr);
633         return createDispatch(portName, wsepr, context, mode, features);
634     }
635 
636     private QName addPortEpr(WSEndpointReference wsepr) {
637         if (wsepr == null)
638             throw new WebServiceException(ProviderApiMessages.NULL_EPR());
639         QName eprPortName = getPortNameFromEPR(wsepr, null);
640         //add Port, if it does n't exist;
641         // TODO: what if it has different epr address?
642         {
643             PortInfo portInfo = new PortInfo(this, (wsepr.getAddress() == null) ? null : EndpointAddress.create(wsepr.getAddress()), eprPortName,
644                     getPortModel(wsdlService, eprPortName).getBinding().getBindingId());
645             if (!ports.containsKey(eprPortName)) {
646                 ports.put(eprPortName, portInfo);
647             }
648         }
649         return eprPortName;
650     }
651 
652     /**
653      *
654      * @param wsepr EndpointReference from which portName will be extracted.
655      *      If EndpointName ( port name) is null in EPR, then it will try to get if from WSDLModel using portType QName
656      * @param portTypeName
657      *          should be null in dispatch case
658      *          should be non null in SEI case
659      * @return
660      *      port name from EPR after validating various metadat elements.
661      *      Also if service instance does n't have wsdl,
662      *      then it gets the WSDL metadata from EPR and builds wsdl model.
663      */
664     private QName getPortNameFromEPR(@NotNull WSEndpointReference wsepr, @Nullable QName portTypeName) {
665         QName portName;
666         WSEndpointReference.Metadata metadata = wsepr.getMetaData();
667         QName eprServiceName = metadata.getServiceName();
668         QName eprPortName = metadata.getPortName();
669         if ((eprServiceName != null ) && !eprServiceName.equals(serviceName)) {
670             throw new WebServiceException("EndpointReference WSDL ServiceName differs from Service Instance WSDL Service QName.\n"
671                     + " The two Service QNames must match");
672         }
673         if (wsdlService == null) {
674             Source eprWsdlSource = metadata.getWsdlSource();
675             if (eprWsdlSource == null) {
676                 throw new WebServiceException(ProviderApiMessages.NULL_WSDL());
677             }
678             try {
679                 WSDLModel eprWsdlMdl = parseWSDL(new URL(wsepr.getAddress()), eprWsdlSource, null);
680                 wsdlService = eprWsdlMdl.getService(serviceName);
681                 if (wsdlService == null)
682                     throw new WebServiceException(ClientMessages.INVALID_SERVICE_NAME(serviceName,
683                             buildNameList(eprWsdlMdl.getServices().keySet())));
684             } catch (MalformedURLException e) {
685                 throw new WebServiceException(ClientMessages.INVALID_ADDRESS(wsepr.getAddress()));
686             }
687         }
688         portName = eprPortName;
689 
690         if (portName == null && portTypeName != null) {
691             //get the first port corresponding to the SEI
692             WSDLPort port = wsdlService.getMatchingPort(portTypeName);
693             if (port == null)
694                 throw new WebServiceException(ClientMessages.UNDEFINED_PORT_TYPE(portTypeName));
695             portName = port.getName();
696         }
697         if (portName == null)
698             throw new WebServiceException(ProviderApiMessages.NULL_PORTNAME());
699         if (wsdlService.get(portName) == null)
700             throw new WebServiceException(ClientMessages.INVALID_EPR_PORT_NAME(portName, buildWsdlPortNames()));
701 
702         return portName;
703 
704     }
705 
706     private <T> T createProxy(final Class<T> portInterface, final InvocationHandler pis) {
707 
708         // When creating the proxy, use a ClassLoader that can load classes
709         // from both the interface class and also from this classes
710         // classloader. This is necessary when this code is used in systems
711         // such as OSGi where the class loader for the interface class may
712         // not be able to load internal JAX-WS classes like
713         // "WSBindingProvider", but the class loader for this class may not
714         // be able to load the interface class.
715         final ClassLoader loader = getDelegatingLoader(portInterface.getClassLoader(),
716                 WSServiceDelegate.class.getClassLoader());
717 
718         // accessClassInPackage privilege needs to be granted ...
719         RuntimePermission perm = new RuntimePermission("accessClassInPackage.com.sun." + "xml.internal.*");
720         PermissionCollection perms = perm.newPermissionCollection();
721         perms.add(perm);
722 
723         return AccessController.doPrivileged(
724                 new PrivilegedAction<T>() {
725                     @Override
726                     public T run() {
727                         Object proxy = Proxy.newProxyInstance(loader,
728                                 new Class[]{portInterface, WSBindingProvider.class, Closeable.class}, pis);
729                         return portInterface.cast(proxy);
730                     }
731                 },
732                 new AccessControlContext(
733                         new ProtectionDomain[]{
734                                 new ProtectionDomain(null, perms)
735                         })
736         );
737     }
738 
739     private WSDLService getWSDLModelfromSEI(final Class sei) {
740         WebService ws = AccessController.doPrivileged(new PrivilegedAction<WebService>() {
741             public WebService run() {
742                 return (WebService) sei.getAnnotation(WebService.class);
743             }
744         });
745         if (ws == null || ws.wsdlLocation().equals(""))
746             return null;
747         String wsdlLocation = ws.wsdlLocation();
748         wsdlLocation = JAXWSUtils.absolutize(JAXWSUtils.getFileOrURLName(wsdlLocation));
749         Source wsdl = new StreamSource(wsdlLocation);
750         WSDLService service = null;
751 
752         try {
753             URL url = wsdl.getSystemId() == null ? null : new URL(wsdl.getSystemId());
754             WSDLModel model = parseWSDL(url, wsdl, sei);
755             service = model.getService(this.serviceName);
756             if (service == null)
757                 throw new WebServiceException(
758                         ClientMessages.INVALID_SERVICE_NAME(this.serviceName,
759                                 buildNameList(model.getServices().keySet())));
760         } catch (MalformedURLException e) {
761             throw new WebServiceException(ClientMessages.INVALID_WSDL_URL(wsdl.getSystemId()));
762         }
763         return service;
764     }
765 
766     public QName getServiceName() {
767         return serviceName;
768     }
769 
770     public Class getServiceClass() {
771         return serviceClass;
772     }
773 
774     public Iterator<QName> getPorts() throws WebServiceException {
775         // KK: the spec seems to be ambigous about whether
776         // this returns ports that are dynamically added or not.
777         return ports.keySet().iterator();
778     }
779 
780     @Override
781     public URL getWSDLDocumentLocation() {
782         if(wsdlService==null)   return null;
783         try {
784             return new URL(wsdlService.getParent().getLocation().getSystemId());
785         } catch (MalformedURLException e) {
786             throw new AssertionError(e); // impossible
787         }
788     }
789 
790     private <T> T createEndpointIFBaseProxy(@Nullable WSEndpointReference epr, QName portName, Class<T> portInterface,
791                                             WebServiceFeatureList webServiceFeatures, SEIPortInfo eif) {
792         //fail if service doesnt have WSDL
793         if (wsdlService == null) {
794             throw new WebServiceException(ClientMessages.INVALID_SERVICE_NO_WSDL(serviceName));
795         }
796 
797         if (wsdlService.get(portName)==null) {
798             throw new WebServiceException(
799                 ClientMessages.INVALID_PORT_NAME(portName,buildWsdlPortNames()));
800         }
801 
802         BindingImpl binding = eif.createBinding(webServiceFeatures, portInterface);
803         InvocationHandler pis = getStubHandler(binding, eif, epr);
804 
805         T proxy = createProxy(portInterface, pis);
806 
807         if (serviceInterceptor != null) {
808             serviceInterceptor.postCreateProxy((WSBindingProvider)proxy, portInterface);
809         }
810         return proxy;
811     }
812 
813     protected InvocationHandler getStubHandler(BindingImpl binding, SEIPortInfo eif, @Nullable WSEndpointReference epr) {
814         return new SEIStub(eif, binding, eif.model, epr);
815     }
816 
817     /**
818      * Lists up the port names in WSDL. For error diagnostics.
819      */
820     private StringBuilder buildWsdlPortNames() {
821         Set<QName> wsdlPortNames = new HashSet<QName>();
822         for (WSDLPort port : wsdlService.getPorts()) {
823             wsdlPortNames.add(port.getName());
824         }
825         return buildNameList(wsdlPortNames);
826     }
827 
828     /**
829      * Obtains a {@link WSDLPortImpl} with error check.
830      *
831      * @return guaranteed to be non-null.
832      */
833     public @NotNull WSDLPort getPortModel(WSDLService wsdlService, QName portName) {
834         WSDLPort port = wsdlService.get(portName);
835         if (port == null)
836             throw new WebServiceException(
837                 ClientMessages.INVALID_PORT_NAME(portName,buildWsdlPortNames()));
838         return port;
839     }
840 
841     /**
842      * Contributes to the construction of {@link WSServiceDelegate} by filling in
843      * {@link SEIPortInfo} about a given SEI (linked from the {@link Service}-derived class.)
844      */
845     //todo: valid port in wsdl
846     private SEIPortInfo addSEI(QName portName, Class portInterface, WebServiceFeatureList features) throws WebServiceException {
847         boolean ownModel = useOwnSEIModel(features);
848         if (ownModel) {
849             // Create a new model and do not cache it
850             return createSEIPortInfo(portName, portInterface, features);
851         }
852 
853         SEIPortInfo spi = seiContext.get(portName);
854         if (spi == null) {
855             spi = createSEIPortInfo(portName, portInterface, features);
856             seiContext.put(spi.portName, spi);
857             ports.put(spi.portName, spi);
858         }
859         return spi;
860     }
861 
862     public SEIModel buildRuntimeModel(QName serviceName, QName portName, Class portInterface, WSDLPort wsdlPort, WebServiceFeatureList features) {
863                 DatabindingFactory fac = DatabindingFactory.newInstance();
864                 DatabindingConfig config = new DatabindingConfig();
865                 config.setContractClass(portInterface);
866                 config.getMappingInfo().setServiceName(serviceName);
867                 config.setWsdlPort(wsdlPort);
868                 config.setFeatures(features);
869                 config.setClassLoader(portInterface.getClassLoader());
870                 config.getMappingInfo().setPortName(portName);
871                 config.setWsdlURL(wsdlURL);
872         // if ExternalMetadataFeature present, ExternalMetadataReader will be created ...
873         config.setMetadataReader(getMetadadaReader(features, portInterface.getClassLoader()));
874 
875                 com.sun.xml.internal.ws.db.DatabindingImpl rt = (com.sun.xml.internal.ws.db.DatabindingImpl)fac.createRuntime(config);
876 
877                 return rt.getModel();
878     }
879 
880     private MetadataReader getMetadadaReader(WebServiceFeatureList features, ClassLoader classLoader) {
881         if (features == null) return null;
882         com.oracle.webservices.internal.api.databinding.ExternalMetadataFeature ef =
883                 features.get(com.oracle.webservices.internal.api.databinding.ExternalMetadataFeature.class);
884         // TODO-Miran: would it be necessary to disable secure xml processing?
885         if (ef != null)
886             return ef.getMetadataReader(classLoader, false);
887         return null;
888     }
889 
890     private SEIPortInfo createSEIPortInfo(QName portName, Class portInterface, WebServiceFeatureList features) {
891         WSDLPort wsdlPort = getPortModel(wsdlService, portName);
892         SEIModel model = buildRuntimeModel(serviceName, portName, portInterface, wsdlPort, features);
893 
894         return new SEIPortInfo(this, portInterface, (SOAPSEIModel) model, wsdlPort);
895     }
896 
897     private boolean useOwnSEIModel(WebServiceFeatureList features) {
898         return features.contains(UsesJAXBContextFeature.class);
899     }
900 
901     public WSDLService getWsdlService() {
902         return wsdlService;
903     }
904 
905     static class DaemonThreadFactory implements ThreadFactory {
906         @Override
907         public Thread newThread(Runnable r) {
908             Thread daemonThread = new Thread(r);
909             daemonThread.setDaemon(Boolean.TRUE);
910             return daemonThread;
911         }
912     }
913 
914     protected static final WebServiceFeature[] EMPTY_FEATURES = new WebServiceFeature[0];
915 
916     private static ClassLoader getDelegatingLoader(ClassLoader loader1, ClassLoader loader2) {
917         if (loader1 == null) return loader2;
918         if (loader2 == null) return loader1;
919         return new DelegatingLoader(loader1, loader2);
920     }
921 
922     private static final class DelegatingLoader extends ClassLoader {
923         private final ClassLoader loader;
924 
925         @Override
926         public int hashCode() {
927             final int prime = 31;
928             int result = 1;
929             result = prime * result
930                     + ((loader == null) ? 0 : loader.hashCode());
931             result = prime * result
932                     + ((getParent() == null) ? 0 : getParent().hashCode());
933             return result;
934         }
935 
936         @Override
937         public boolean equals(Object obj) {
938             if (this == obj)
939                 return true;
940             if (obj == null)
941                 return false;
942             if (getClass() != obj.getClass())
943                 return false;
944             DelegatingLoader other = (DelegatingLoader) obj;
945             if (loader == null) {
946                 if (other.loader != null)
947                     return false;
948             } else if (!loader.equals(other.loader))
949                 return false;
950             if (getParent() == null) {
951                 if (other.getParent() != null)
952                     return false;
953             } else if (!getParent().equals(other.getParent()))
954                 return false;
955             return true;
956         }
957 
958         DelegatingLoader(ClassLoader loader1, ClassLoader loader2) {
959             super(loader2);
960             this.loader = loader1;
961         }
962 
963         protected Class findClass(String name) throws ClassNotFoundException {
964             return loader.loadClass(name);
965         }
966 
967         protected URL findResource(String name) {
968             return loader.getResource(name);
969         }
970     }
971 }