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 
  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.AccessController;
  95 import java.security.PrivilegedAction;
  96 import java.util.Collection;
  97 import java.util.HashMap;
  98 import java.util.HashSet;
  99 import java.util.Iterator;
 100 import java.util.Map;
 101 import java.util.Set;
 102 import java.util.concurrent.Executor;
 103 import java.util.concurrent.ThreadFactory;
 104 
 105 import 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  */
 136 public 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 }