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