All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.xml.ws.client.WSServiceDelegate Maven / Gradle / Ivy

There is a newer version: 4.0.3
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package com.sun.xml.ws.client;

import com.sun.istack.NotNull;
import com.sun.istack.Nullable;
import com.sun.xml.ws.Closeable;
import com.sun.xml.ws.api.BindingID;
import com.sun.xml.ws.api.EndpointAddress;
import com.sun.xml.ws.api.WSBinding;
import com.sun.xml.ws.api.WSService;
import com.sun.xml.ws.api.addressing.WSEndpointReference;
import com.sun.xml.ws.api.client.ServiceInterceptor;
import com.sun.xml.ws.api.client.ServiceInterceptorFactory;
import com.sun.xml.ws.api.model.SEIModel;
import com.sun.xml.ws.api.pipe.*;
import com.sun.xml.ws.api.server.Container;
import com.sun.xml.ws.api.server.ContainerResolver;
import com.sun.xml.ws.api.wsdl.parser.WSDLParserExtension;
import com.sun.xml.ws.binding.BindingImpl;
import com.sun.xml.ws.binding.WebServiceFeatureList;
import com.sun.xml.ws.client.HandlerConfigurator.AnnotationConfigurator;
import com.sun.xml.ws.client.HandlerConfigurator.HandlerResolverImpl;
import com.sun.xml.ws.client.sei.SEIStub;
import com.sun.xml.ws.developer.WSBindingProvider;
import com.sun.xml.ws.model.AbstractSEIModelImpl;
import com.sun.xml.ws.model.RuntimeModeler;
import com.sun.xml.ws.model.SOAPSEIModel;
import com.sun.xml.ws.model.wsdl.WSDLModelImpl;
import com.sun.xml.ws.model.wsdl.WSDLPortImpl;
import com.sun.xml.ws.model.wsdl.WSDLServiceImpl;
import com.sun.xml.ws.resources.ClientMessages;
import com.sun.xml.ws.resources.DispatchMessages;
import com.sun.xml.ws.resources.ProviderApiMessages;
import com.sun.xml.ws.util.JAXWSUtils;
import com.sun.xml.ws.util.ServiceConfigurationError;
import com.sun.xml.ws.util.ServiceFinder;
import static com.sun.xml.ws.util.xml.XmlUtil.createDefaultCatalogResolver;
import com.sun.xml.ws.wsdl.parser.RuntimeWSDLParser;
import org.xml.sax.SAXException;

import javax.jws.HandlerChain;
import javax.xml.bind.JAXBContext;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.*;
import javax.xml.ws.handler.HandlerResolver;
import javax.xml.ws.soap.AddressingFeature;
import java.io.IOException;
import java.lang.reflect.Proxy;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

/**
 * Service objects provide the client view of a Web service.
 *
 * 

Service acts as a factory of the following: *

    *
  • Proxies for a target service endpoint. *
  • Instances of javax.xml.ws.Dispatch for * dynamic message-oriented invocation of a remote * operation. *
  • * *

    The ports available on a service can be enumerated using the * getPorts method. Alternatively, you can pass a * service endpoint interface to the unary getPort method * and let the runtime select a compatible port. * *

    Handler chains for all the objects created by a Service * can be set by means of the provided HandlerRegistry. * *

    An Executor may be set on the service in order * to gain better control over the threads used to dispatch asynchronous * callbacks. For instance, thread pooling with certain parameters * can be enabled by creating a ThreadPoolExecutor and * registering it with the service. * * @author WS Development Team * @see Executor * @since JAX-WS 2.0 */ public class WSServiceDelegate extends WSService { /** * All ports. *

    * This includes ports statically known to WSDL, as well as * ones that are dynamically added * through {@link #addPort(QName, String, String)}. *

    * For statically known ports we'll have {@link SEIPortInfo}. * For dynamically added ones we'll have {@link PortInfo}. */ private final Map ports = new HashMap(); /** * Whenever we create {@link BindingProvider}, we use this to configure handlers. */ private @NotNull HandlerConfigurator handlerConfigurator = new HandlerResolverImpl(null); private final Class serviceClass; /** * Name of the service for which this {@link WSServiceDelegate} is created for. */ private final @NotNull QName serviceName; /** * Information about SEI, keyed by their interface type. */ // private final Map seiContext = new HashMap(); private final Map seiContext = new HashMap(); private Executor executor; /** * The WSDL service that this {@link Service} object represents. *

    * This field is null iff no WSDL is given to {@link Service}. * This fiels can be be null if the service is created without wsdl but later * the epr supplies a wsdl that can be parsed. */ private @Nullable WSDLServiceImpl wsdlService; private final Container container; /** * Multiple {@link ServiceInterceptor}s are aggregated into one. */ /*package*/ final @NotNull ServiceInterceptor serviceInterceptor; public WSServiceDelegate(URL wsdlDocumentLocation, QName serviceName, Class serviceClass) { this( wsdlDocumentLocation==null ? null : new StreamSource(wsdlDocumentLocation.toExternalForm()), serviceName,serviceClass); } /** * @param serviceClass * Either {@link Service}.class or other generated service-derived classes. */ public WSServiceDelegate(@Nullable Source wsdl, @NotNull QName serviceName, @NotNull final Class serviceClass) { //we cant create a Service without serviceName if (serviceName == null) throw new WebServiceException(ClientMessages.INVALID_SERVICE_NAME_NULL(serviceName)); InitParams initParams = INIT_PARAMS.get(); INIT_PARAMS.set(null); // mark it as consumed if(initParams==null) initParams = EMPTY_PARAMS; this.serviceName = serviceName; this.serviceClass = serviceClass; Container tContainer = initParams.getContainer()!=null ? initParams.getContainer() : ContainerResolver.getInstance().getContainer(); if (tContainer == Container.NONE) { tContainer = new ClientContainer(); } this.container = tContainer; // load interceptor ServiceInterceptor interceptor = ServiceInterceptorFactory.load(this, Thread.currentThread().getContextClassLoader()); ServiceInterceptor si = container.getSPI(ServiceInterceptor.class); if (si != null) { interceptor = ServiceInterceptor.aggregate(interceptor, si); } this.serviceInterceptor = interceptor; //if wsdl is null, try and get it from the WebServiceClient.wsdlLocation if(wsdl == null){ if(serviceClass != Service.class){ WebServiceClient wsClient = AccessController.doPrivileged(new PrivilegedAction() { public WebServiceClient run() { return serviceClass.getAnnotation(WebServiceClient.class); } }); String wsdlLocation = wsClient.wsdlLocation(); wsdlLocation = JAXWSUtils.absolutize(JAXWSUtils.getFileOrURLName(wsdlLocation)); wsdl = new StreamSource(wsdlLocation); } } WSDLServiceImpl service=null; if (wsdl != null) { try { URL url = wsdl.getSystemId()==null ? null : new URL(wsdl.getSystemId()); WSDLModelImpl model = parseWSDL(url, wsdl); service = model.getService(this.serviceName); if (service == null) throw new WebServiceException( ClientMessages.INVALID_SERVICE_NAME(this.serviceName, buildNameList(model.getServices().keySet()))); // fill in statically known ports for (WSDLPortImpl port : service.getPorts()) ports.put(port.getName(), new PortInfo(this, port)); } catch (MalformedURLException e) { throw new WebServiceException(ClientMessages.INVALID_WSDL_URL(wsdl.getSystemId())); } } this.wsdlService = service; if (serviceClass != Service.class) { //if @HandlerChain present, set HandlerResolver on service context HandlerChain handlerChain = AccessController.doPrivileged(new PrivilegedAction() { public HandlerChain run() { return serviceClass.getAnnotation(HandlerChain.class); } }); if (handlerChain != null) handlerConfigurator = new AnnotationConfigurator(this); } } /** * Parses the WSDL and builds {@link WSDLModel}. * @param wsdlDocumentLocation * Either this or wsdl parameter must be given. * Null location means the system won't be able to resolve relative references in the WSDL, */ private WSDLModelImpl parseWSDL(URL wsdlDocumentLocation, Source wsdlSource) { try { return RuntimeWSDLParser.parse(wsdlDocumentLocation, wsdlSource, createDefaultCatalogResolver(), true, getContainer(), ServiceFinder.find(WSDLParserExtension.class).toArray()); } catch (IOException e) { throw new WebServiceException(e); } catch (XMLStreamException e) { throw new WebServiceException(e); } catch (SAXException e) { throw new WebServiceException(e); } catch (ServiceConfigurationError e) { throw new WebServiceException(e); } } public Executor getExecutor() { if (executor != null) { return executor; } else executor = Executors.newCachedThreadPool(new DaemonThreadFactory()); return executor; } public void setExecutor(Executor executor) { this.executor = executor; } public HandlerResolver getHandlerResolver() { return handlerConfigurator.getResolver(); } /*package*/ final HandlerConfigurator getHandlerConfigurator() { return handlerConfigurator; } public void setHandlerResolver(HandlerResolver resolver) { handlerConfigurator = new HandlerResolverImpl(resolver); } public T getPort(QName portName, Class portInterface) throws WebServiceException { return getPort(portName, portInterface, EMPTY_FEATURES); } public T getPort(QName portName, Class portInterface, WebServiceFeature... features) { if (portName == null || portInterface == null) throw new IllegalArgumentException(); WSDLPortImpl portModel = getPortModel(portName); return getPort(portModel.getEPR(),portName,portInterface,features); } public T getPort(EndpointReference epr, Class portInterface, WebServiceFeature... features) { return getPort(WSEndpointReference.create(epr),portInterface,features); } public T getPort(WSEndpointReference wsepr, Class portInterface, WebServiceFeature... features) { //get the portType from SEI, so that it can be used if EPR does n't have endpointName QName portTypeName = RuntimeModeler.getPortTypeName(portInterface); //if port name is not specified in EPR, it will use portTypeName to get it from the WSDL model. QName portName = getPortNameFromEPR(wsepr, portTypeName); return getPort(wsepr,portName,portInterface,features); } private T getPort(WSEndpointReference wsepr, QName portName, Class portInterface, WebServiceFeature... features) { addSEI(portName, portInterface); return createEndpointIFBaseProxy(wsepr,portName,portInterface,features); } public T getPort(Class portInterface, WebServiceFeature... features) { //get the portType from SEI QName portTypeName = RuntimeModeler.getPortTypeName(portInterface); //get the first port corresponding to the SEI WSDLPortImpl port = wsdlService.getMatchingPort(portTypeName); if (port == null) throw new WebServiceException(ClientMessages.UNDEFINED_PORT_TYPE(portTypeName)); QName portName = port.getName(); return getPort(portName, portInterface,features); } public T getPort(Class portInterface) throws WebServiceException { return getPort(portInterface, EMPTY_FEATURES); } public void addPort(QName portName, String bindingId, String endpointAddress) throws WebServiceException { if (!ports.containsKey(portName)) { BindingID bid = (bindingId == null) ? BindingID.SOAP11_HTTP : BindingID.parse(bindingId); ports.put(portName, new PortInfo(this, (endpointAddress == null) ? null : EndpointAddress.create(endpointAddress), portName, bid)); } else throw new WebServiceException(DispatchMessages.DUPLICATE_PORT(portName.toString())); } public Dispatch createDispatch(QName portName, Class aClass, Service.Mode mode) throws WebServiceException { return createDispatch(portName, aClass, mode, EMPTY_FEATURES); } @Override public Dispatch createDispatch(QName portName, WSEndpointReference wsepr, Class aClass, Service.Mode mode, WebServiceFeature... features) { PortInfo port = safeGetPort(portName); BindingImpl binding = port.createBinding(features,null); Dispatch dispatch = Stubs.createDispatch(portName, this, binding, aClass, mode, createPipeline(port, binding), wsepr); serviceInterceptor.postCreateDispatch((WSBindingProvider) dispatch); return dispatch; } public Dispatch createDispatch(QName portName, Class aClass, Service.Mode mode, WebServiceFeature... features) { WebServiceFeatureList featureList = new WebServiceFeatureList(features); WSEndpointReference wsepr = null; if(featureList.isEnabled(AddressingFeature.class) && wsdlService != null && wsdlService.get(portName) != null) { wsepr = wsdlService.get(portName).getEPR(); } return createDispatch(portName, wsepr, aClass, mode, features); } public Dispatch createDispatch(EndpointReference endpointReference, Class type, Service.Mode mode, WebServiceFeature... features) { WSEndpointReference wsepr = new WSEndpointReference(endpointReference); QName portName = addPortEpr(wsepr); return createDispatch(portName, wsepr, type, mode, features); } /** * Obtains {@link PortInfo} for the given name, with error check. */ public @NotNull PortInfo safeGetPort(QName portName) { PortInfo port = ports.get(portName); if (port == null) { throw new WebServiceException(ClientMessages.INVALID_PORT_NAME(portName, buildNameList(ports.keySet()))); } return port; } private StringBuilder buildNameList(Collection names) { StringBuilder sb = new StringBuilder(); for (QName qn : names) { if (sb.length() > 0) sb.append(','); sb.append(qn); } return sb; } /** * Creates a new pipeline for the given port name. */ private Tube createPipeline(PortInfo portInfo, WSBinding binding) { //Check all required WSDL extensions are understood checkAllWSDLExtensionsUnderstood(portInfo,binding); SEIModel seiModel = null; if(portInfo instanceof SEIPortInfo) { seiModel = ((SEIPortInfo)portInfo).model; } BindingID bindingId = portInfo.bindingId; TubelineAssembler assembler = TubelineAssemblerFactory.create( Thread.currentThread().getContextClassLoader(), bindingId); if (assembler == null) throw new WebServiceException("Unable to process bindingID=" + bindingId); // TODO: i18n return assembler.createClient( new ClientTubeAssemblerContext( portInfo.targetEndpoint, portInfo.portModel, this, binding, container,((BindingImpl)binding).createCodec(),seiModel)); } /** * Checks only if RespectBindingFeature is enabled * checks if all required wsdl extensions in the * corresponding wsdl:Port are understood when RespectBindingFeature is enabled. * @throws WebServiceException * when any wsdl extension that has wsdl:required=true is not understood */ private void checkAllWSDLExtensionsUnderstood(PortInfo port, WSBinding binding) { if (port.portModel != null && binding.isFeatureEnabled(RespectBindingFeature.class)) { ((WSDLPortImpl) port.portModel).areRequiredExtensionsUnderstood(); } } public EndpointAddress getEndpointAddress(QName qName) { return ports.get(qName).targetEndpoint; } public Dispatch createDispatch(QName portName, JAXBContext jaxbContext, Service.Mode mode) throws WebServiceException { return createDispatch(portName, jaxbContext, mode, EMPTY_FEATURES); } @Override public Dispatch createDispatch(QName portName, WSEndpointReference wsepr, JAXBContext jaxbContext, Service.Mode mode, WebServiceFeature... features) { PortInfo port = safeGetPort(portName); BindingImpl binding = port.createBinding(features,null); Dispatch dispatch = Stubs.createJAXBDispatch( portName, this, binding, jaxbContext, mode, createPipeline(port, binding), wsepr); serviceInterceptor.postCreateDispatch((WSBindingProvider)dispatch); return dispatch; } @Override public @NotNull Container getContainer() { return container; } public Dispatch createDispatch(QName portName, JAXBContext jaxbContext, Service.Mode mode, WebServiceFeature... webServiceFeatures) { WebServiceFeatureList featureList = new WebServiceFeatureList(webServiceFeatures); WSEndpointReference wsepr = null; if(featureList.isEnabled(AddressingFeature.class) && wsdlService != null && wsdlService.get(portName) != null) { wsepr = wsdlService.get(portName).getEPR(); } return createDispatch(portName, wsepr, jaxbContext, mode, webServiceFeatures); } public Dispatch createDispatch(EndpointReference endpointReference, JAXBContext context, Service.Mode mode, WebServiceFeature... features) { WSEndpointReference wsepr = new WSEndpointReference(endpointReference); QName portName = addPortEpr(wsepr); return createDispatch(portName, wsepr, context, mode, features); } private QName addPortEpr(WSEndpointReference wsepr) { if (wsepr == null) throw new WebServiceException(ProviderApiMessages.NULL_EPR()); QName eprPortName = getPortNameFromEPR(wsepr, null); //add Port, if it does n't exist; // TODO: what if it has different epr address? { PortInfo portInfo = new PortInfo(this, (wsepr.getAddress() == null) ? null : EndpointAddress.create(wsepr.getAddress()), eprPortName, getPortModel(eprPortName).getBinding().getBindingId()); if (!ports.containsKey(eprPortName)) { ports.put(eprPortName, portInfo); } } return eprPortName; } /** * * @param wsepr EndpointReference from which portName will be extracted. * If EndpointName ( port name) is null in EPR, then it will try to get if from WSDLModel using portType QName * @param portTypeName * should be null in dispatch case * should be non null in SEI case * @return * port name from EPR after validating various metadat elements. * Also if service instance does n't have wsdl, * then it gets the WSDL metadata from EPR and builds wsdl model. */ private QName getPortNameFromEPR(@NotNull WSEndpointReference wsepr, @Nullable QName portTypeName) { QName portName; WSEndpointReference.Metadata metadata = wsepr.getMetaData(); QName eprServiceName = metadata.getServiceName(); QName eprPortName = metadata.getPortName(); if ((eprServiceName != null ) && !eprServiceName.equals(serviceName)) { throw new WebServiceException("EndpointReference WSDL ServiceName differs from Service Instance WSDL Service QName.\n" + " The two Service QNames must match"); } if (wsdlService == null) { Source eprWsdlSource = metadata.getWsdlSource(); if (eprWsdlSource == null) { throw new WebServiceException(ProviderApiMessages.NULL_WSDL()); } try { WSDLModelImpl eprWsdlMdl = parseWSDL(new URL(wsepr.getAddress()), eprWsdlSource); wsdlService = eprWsdlMdl.getService(serviceName); if (wsdlService == null) throw new WebServiceException(ClientMessages.INVALID_SERVICE_NAME(serviceName, buildNameList(eprWsdlMdl.getServices().keySet()))); } catch (MalformedURLException e) { throw new WebServiceException(ClientMessages.INVALID_ADDRESS(wsepr.getAddress())); } } portName = eprPortName; if (portName == null && portTypeName != null) { //get the first port corresponding to the SEI WSDLPortImpl port = wsdlService.getMatchingPort(portTypeName); if (port == null) throw new WebServiceException(ClientMessages.UNDEFINED_PORT_TYPE(portTypeName)); portName = port.getName(); } if (portName == null) throw new WebServiceException(ProviderApiMessages.NULL_PORTNAME()); if (wsdlService.get(portName) == null) throw new WebServiceException(ClientMessages.INVALID_EPR_PORT_NAME(portName, buildWsdlPortNames())); return portName; } public QName getServiceName() { return serviceName; } protected Class getServiceClass() { return serviceClass; } public Iterator getPorts() throws WebServiceException { // KK: the spec seems to be ambigous about whether // this returns ports that are dynamically added or not. if (ports.isEmpty()) throw new WebServiceException("dii.service.no.wsdl.available"); return ports.keySet().iterator(); } public URL getWSDLDocumentLocation() { if(wsdlService==null) return null; try { return new URL(wsdlService.getParent().getLocation().getSystemId()); } catch (MalformedURLException e) { throw new AssertionError(e); // impossible } } private T createEndpointIFBaseProxy(@Nullable WSEndpointReference epr,QName portName, Class portInterface, WebServiceFeature[] webServiceFeatures) { //fail if service doesnt have WSDL if (wsdlService == null) throw new WebServiceException(ClientMessages.INVALID_SERVICE_NO_WSDL(serviceName)); if (wsdlService.get(portName)==null) { throw new WebServiceException( ClientMessages.INVALID_PORT_NAME(portName,buildWsdlPortNames())); } SEIPortInfo eif = seiContext.get(portName); BindingImpl binding = eif.createBinding(webServiceFeatures,portInterface); SEIStub pis = new SEIStub(this, binding, eif.model, createPipeline(eif, binding), epr); T proxy = portInterface.cast(Proxy.newProxyInstance(portInterface.getClassLoader(), new Class[]{portInterface, WSBindingProvider.class, Closeable.class}, pis)); if (serviceInterceptor != null) { serviceInterceptor.postCreateProxy((WSBindingProvider)proxy, portInterface); } return proxy; } /** * Lists up the port names in WSDL. For error diagnostics. */ private StringBuilder buildWsdlPortNames() { Set wsdlPortNames = new HashSet(); for (WSDLPortImpl port : wsdlService.getPorts()) wsdlPortNames.add(port.getName()); return buildNameList(wsdlPortNames); } /** * Obtains a {@link WSDLPortImpl} with error check. * * @return guaranteed to be non-null. */ public @NotNull WSDLPortImpl getPortModel(QName portName) { WSDLPortImpl port = wsdlService.get(portName); if (port == null) throw new WebServiceException( ClientMessages.INVALID_PORT_NAME(portName,buildWsdlPortNames())); return port; } /** * Contributes to the construction of {@link WSServiceDelegate} by filling in * {@link SEIPortInfo} about a given SEI (linked from the {@link Service}-derived class.) */ //todo: valid port in wsdl private void addSEI(QName portName, Class portInterface) throws WebServiceException { SEIPortInfo spi = seiContext.get(portName); if (spi != null) return; WSDLPortImpl wsdlPort = getPortModel(portName); RuntimeModeler modeler = new RuntimeModeler(portInterface, serviceName, wsdlPort); modeler.setPortName(portName); AbstractSEIModelImpl model = modeler.buildRuntimeModel(); spi = new SEIPortInfo(this, portInterface, (SOAPSEIModel) model, wsdlPort); seiContext.put(spi.portName, spi); //seiContext.put(spi.sei, spi); ports.put(spi.portName, spi); } public WSDLServiceImpl getWsdlService() { return wsdlService; } class DaemonThreadFactory implements ThreadFactory { public Thread newThread(Runnable r) { Thread daemonThread = new Thread(r); daemonThread.setDaemon(Boolean.TRUE); return daemonThread; } } private static final WebServiceFeature[] EMPTY_FEATURES = new WebServiceFeature[0]; }