972 lines
42 KiB
Java
972 lines
42 KiB
Java
/*
|
|
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
package com.sun.xml.internal.ws.client;
|
|
|
|
import com.sun.istack.internal.NotNull;
|
|
import com.sun.istack.internal.Nullable;
|
|
import com.sun.xml.internal.ws.Closeable;
|
|
import com.sun.xml.internal.ws.api.BindingID;
|
|
import com.sun.xml.internal.ws.api.ComponentFeature;
|
|
import com.sun.xml.internal.ws.api.ComponentsFeature;
|
|
import com.sun.xml.internal.ws.api.ComponentFeature.Target;
|
|
import com.sun.xml.internal.ws.api.EndpointAddress;
|
|
import com.sun.xml.internal.ws.api.WSService;
|
|
import com.sun.xml.internal.ws.api.addressing.WSEndpointReference;
|
|
import com.sun.xml.internal.ws.api.client.ServiceInterceptor;
|
|
import com.sun.xml.internal.ws.api.client.ServiceInterceptorFactory;
|
|
import com.sun.xml.internal.ws.api.databinding.DatabindingConfig;
|
|
import com.sun.xml.internal.ws.api.databinding.DatabindingFactory;
|
|
import com.sun.xml.internal.ws.api.databinding.MetadataReader;
|
|
import com.sun.xml.internal.ws.api.model.SEIModel;
|
|
import com.sun.xml.internal.ws.api.model.wsdl.WSDLModel;
|
|
import com.sun.xml.internal.ws.api.model.wsdl.WSDLPort;
|
|
import com.sun.xml.internal.ws.api.model.wsdl.WSDLService;
|
|
import com.sun.xml.internal.ws.api.pipe.Stubs;
|
|
import com.sun.xml.internal.ws.api.server.Container;
|
|
import com.sun.xml.internal.ws.api.server.ContainerResolver;
|
|
import com.sun.xml.internal.ws.api.wsdl.parser.WSDLParserExtension;
|
|
import com.sun.xml.internal.ws.binding.BindingImpl;
|
|
import com.sun.xml.internal.ws.binding.WebServiceFeatureList;
|
|
import com.sun.xml.internal.ws.client.HandlerConfigurator.AnnotationConfigurator;
|
|
import com.sun.xml.internal.ws.client.HandlerConfigurator.HandlerResolverImpl;
|
|
import com.sun.xml.internal.ws.client.sei.SEIStub;
|
|
import com.sun.xml.internal.ws.developer.MemberSubmissionAddressingFeature;
|
|
import com.sun.xml.internal.ws.developer.UsesJAXBContextFeature;
|
|
import com.sun.xml.internal.ws.developer.WSBindingProvider;
|
|
import com.sun.xml.internal.ws.model.RuntimeModeler;
|
|
import com.sun.xml.internal.ws.model.SOAPSEIModel;
|
|
import com.sun.xml.internal.ws.resources.ClientMessages;
|
|
import com.sun.xml.internal.ws.resources.DispatchMessages;
|
|
import com.sun.xml.internal.ws.resources.ProviderApiMessages;
|
|
import com.sun.xml.internal.ws.util.JAXWSUtils;
|
|
import com.sun.xml.internal.ws.util.ServiceConfigurationError;
|
|
import com.sun.xml.internal.ws.util.ServiceFinder;
|
|
import com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser;
|
|
|
|
import org.xml.sax.EntityResolver;
|
|
import org.xml.sax.SAXException;
|
|
|
|
import javax.jws.HandlerChain;
|
|
import javax.jws.WebService;
|
|
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.BindingProvider;
|
|
import javax.xml.ws.Dispatch;
|
|
import javax.xml.ws.EndpointReference;
|
|
import javax.xml.ws.Service;
|
|
import javax.xml.ws.WebServiceClient;
|
|
import javax.xml.ws.WebServiceException;
|
|
import javax.xml.ws.WebServiceFeature;
|
|
import javax.xml.ws.handler.HandlerResolver;
|
|
import javax.xml.ws.soap.AddressingFeature;
|
|
|
|
import java.io.IOException;
|
|
import java.lang.reflect.InvocationHandler;
|
|
import java.lang.reflect.Proxy;
|
|
import java.net.MalformedURLException;
|
|
import java.net.URL;
|
|
import java.security.*;
|
|
import java.util.Collection;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.Iterator;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.concurrent.Executor;
|
|
import java.util.concurrent.ThreadFactory;
|
|
|
|
import static com.sun.xml.internal.ws.util.xml.XmlUtil.createDefaultCatalogResolver;
|
|
|
|
/**
|
|
* <code>Service</code> objects provide the client view of a Web service.
|
|
*
|
|
* <p><code>Service</code> acts as a factory of the following:
|
|
* <ul>
|
|
* <li>Proxies for a target service endpoint.
|
|
* <li>Instances of <code>javax.xml.ws.Dispatch</code> for
|
|
* dynamic message-oriented invocation of a remote
|
|
* operation.
|
|
* </li>
|
|
*
|
|
* <p>The ports available on a service can be enumerated using the
|
|
* <code>getPorts</code> method. Alternatively, you can pass a
|
|
* service endpoint interface to the unary <code>getPort</code> method
|
|
* and let the runtime select a compatible port.
|
|
*
|
|
* <p>Handler chains for all the objects created by a <code>Service</code>
|
|
* can be set by means of the provided <code>HandlerRegistry</code>.
|
|
*
|
|
* <p>An <code>Executor</code> 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 <code>ThreadPoolExecutor</code> and
|
|
* registering it with the service.
|
|
*
|
|
* @author WS Development Team
|
|
* @see Executor
|
|
* @since JAX-WS 2.0
|
|
*/
|
|
public class WSServiceDelegate extends WSService {
|
|
/**
|
|
* All ports.
|
|
* <p>
|
|
* This includes ports statically known to WSDL, as well as
|
|
* ones that are dynamically added
|
|
* through {@link #addPort(QName, String, String)}.
|
|
* <p>
|
|
* For statically known ports we'll have {@link SEIPortInfo}.
|
|
* For dynamically added ones we'll have {@link PortInfo}.
|
|
*/
|
|
private final Map<QName, PortInfo> ports = new HashMap<QName, PortInfo>();
|
|
// For monitoring
|
|
protected Map<QName, PortInfo> getQNameToPortInfoMap() { return ports; }
|
|
|
|
/**
|
|
* Whenever we create {@link BindingProvider}, we use this to configure handlers.
|
|
*/
|
|
private @NotNull HandlerConfigurator handlerConfigurator = new HandlerResolverImpl(null);
|
|
|
|
private final Class<? extends Service> serviceClass;
|
|
|
|
private final WebServiceFeatureList features;
|
|
|
|
/**
|
|
* 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<Class,SEIPortInfo> seiContext = new HashMap<Class,SEIPortInfo>();
|
|
private final Map<QName,SEIPortInfo> seiContext = new HashMap<QName,SEIPortInfo>();
|
|
|
|
// This executor is used for all the async invocations for all proxies
|
|
// created from this service. But once the proxy is created, then changing
|
|
// this executor doesn't affect the already created proxies.
|
|
private volatile Executor executor;
|
|
|
|
/**
|
|
* The WSDL service that this {@link Service} object represents.
|
|
* <p>
|
|
* 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 WSDLService wsdlService;
|
|
|
|
private final Container container;
|
|
/**
|
|
* Multiple {@link ServiceInterceptor}s are aggregated into one.
|
|
*/
|
|
/*package*/ final @NotNull ServiceInterceptor serviceInterceptor;
|
|
private URL wsdlURL;
|
|
|
|
public WSServiceDelegate(URL wsdlDocumentLocation, QName serviceName, Class<? extends Service> serviceClass, WebServiceFeature... features) {
|
|
this(wsdlDocumentLocation, serviceName, serviceClass, new WebServiceFeatureList(features));
|
|
}
|
|
|
|
protected WSServiceDelegate(URL wsdlDocumentLocation, QName serviceName, Class<? extends Service> serviceClass, WebServiceFeatureList features) {
|
|
this(
|
|
wsdlDocumentLocation==null ? null : new StreamSource(wsdlDocumentLocation.toExternalForm()),
|
|
serviceName,serviceClass, features);
|
|
wsdlURL = wsdlDocumentLocation;
|
|
}
|
|
|
|
/**
|
|
* @param serviceClass
|
|
* Either {@link Service}.class or other generated service-derived classes.
|
|
*/
|
|
public WSServiceDelegate(@Nullable Source wsdl, @NotNull QName serviceName, @NotNull final Class<? extends Service> serviceClass, WebServiceFeature... features) {
|
|
this(wsdl, serviceName, serviceClass, new WebServiceFeatureList(features));
|
|
}
|
|
|
|
/**
|
|
* @param serviceClass
|
|
* Either {@link Service}.class or other generated service-derived classes.
|
|
*/
|
|
protected WSServiceDelegate(@Nullable Source wsdl, @NotNull QName serviceName, @NotNull final Class<? extends Service> serviceClass, WebServiceFeatureList features) {
|
|
this(wsdl, null, serviceName, serviceClass, features);
|
|
}
|
|
|
|
/**
|
|
* @param serviceClass
|
|
* Either {@link Service}.class or other generated service-derived classes.
|
|
*/
|
|
public WSServiceDelegate(@Nullable Source wsdl, @Nullable WSDLService service, @NotNull QName serviceName, @NotNull final Class<? extends Service> serviceClass, WebServiceFeature... features) {
|
|
this(wsdl, service, serviceName, serviceClass, new WebServiceFeatureList(features));
|
|
}
|
|
|
|
/**
|
|
* @param serviceClass
|
|
* Either {@link Service}.class or other generated service-derived classes.
|
|
*/
|
|
public WSServiceDelegate(@Nullable Source wsdl, @Nullable WSDLService service, @NotNull QName serviceName, @NotNull final Class<? extends Service> serviceClass, WebServiceFeatureList features) {
|
|
//we cant create a Service without serviceName
|
|
if (serviceName == null) {
|
|
throw new WebServiceException(ClientMessages.INVALID_SERVICE_NAME_NULL(null));
|
|
}
|
|
|
|
this.features = features;
|
|
|
|
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;
|
|
|
|
ComponentFeature cf = this.features.get(ComponentFeature.class);
|
|
if (cf != null) {
|
|
switch(cf.getTarget()) {
|
|
case SERVICE:
|
|
getComponents().add(cf.getComponent());
|
|
break;
|
|
case CONTAINER:
|
|
this.container.getComponents().add(cf.getComponent());
|
|
break;
|
|
default:
|
|
throw new IllegalArgumentException();
|
|
}
|
|
}
|
|
ComponentsFeature csf = this.features.get(ComponentsFeature.class);
|
|
if (csf != null) {
|
|
for (ComponentFeature cfi : csf.getComponentFeatures()) {
|
|
switch(cfi.getTarget()) {
|
|
case SERVICE:
|
|
getComponents().add(cfi.getComponent());
|
|
break;
|
|
case CONTAINER:
|
|
this.container.getComponents().add(cfi.getComponent());
|
|
break;
|
|
default:
|
|
throw new IllegalArgumentException();
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 (service == null) {
|
|
//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<WebServiceClient>() {
|
|
public WebServiceClient run() {
|
|
return serviceClass.getAnnotation(WebServiceClient.class);
|
|
}
|
|
});
|
|
String wsdlLocation = wsClient.wsdlLocation();
|
|
wsdlLocation = JAXWSUtils.absolutize(JAXWSUtils.getFileOrURLName(wsdlLocation));
|
|
wsdl = new StreamSource(wsdlLocation);
|
|
}
|
|
}
|
|
if (wsdl != null) {
|
|
try {
|
|
URL url = wsdl.getSystemId()==null ? null : JAXWSUtils.getEncodedURL(wsdl.getSystemId());
|
|
WSDLModel model = parseWSDL(url, wsdl, serviceClass);
|
|
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 (WSDLPort port : service.getPorts())
|
|
ports.put(port.getName(), new PortInfo(this, port));
|
|
} catch (MalformedURLException e) {
|
|
throw new WebServiceException(ClientMessages.INVALID_WSDL_URL(wsdl.getSystemId()));
|
|
}
|
|
}
|
|
} else {
|
|
// fill in statically known ports
|
|
for (WSDLPort port : service.getPorts())
|
|
ports.put(port.getName(), new PortInfo(this, port));
|
|
}
|
|
this.wsdlService = service;
|
|
|
|
if (serviceClass != Service.class) {
|
|
//if @HandlerChain present, set HandlerResolver on service context
|
|
HandlerChain handlerChain =
|
|
AccessController.doPrivileged(new PrivilegedAction<HandlerChain>() {
|
|
public HandlerChain run() {
|
|
return serviceClass.getAnnotation(HandlerChain.class);
|
|
}
|
|
});
|
|
if (handlerChain != null)
|
|
handlerConfigurator = new AnnotationConfigurator(this);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Parses the WSDL and builds {@link com.sun.xml.internal.ws.api.model.wsdl.WSDLModel}.
|
|
* @param wsdlDocumentLocation
|
|
* Either this or <tt>wsdl</tt> parameter must be given.
|
|
* Null location means the system won't be able to resolve relative references in the WSDL,
|
|
*/
|
|
private WSDLModel parseWSDL(URL wsdlDocumentLocation, Source wsdlSource, Class serviceClass) {
|
|
try {
|
|
return RuntimeWSDLParser.parse(wsdlDocumentLocation, wsdlSource, createCatalogResolver(),
|
|
true, getContainer(), serviceClass, 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);
|
|
}
|
|
}
|
|
|
|
protected EntityResolver createCatalogResolver() {
|
|
return createDefaultCatalogResolver();
|
|
}
|
|
|
|
public Executor getExecutor() {
|
|
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> T getPort(QName portName, Class<T> portInterface) throws WebServiceException {
|
|
return getPort(portName, portInterface, EMPTY_FEATURES);
|
|
}
|
|
|
|
public <T> T getPort(QName portName, Class<T> portInterface, WebServiceFeature... features) {
|
|
if (portName == null || portInterface == null)
|
|
throw new IllegalArgumentException();
|
|
WSDLService tWsdlService = this.wsdlService;
|
|
if (tWsdlService == null) {
|
|
// assigning it to local variable and not setting it back to this.wsdlService intentionally
|
|
// as we don't want to include the service instance with information gathered from sei
|
|
tWsdlService = getWSDLModelfromSEI(portInterface);
|
|
//still null? throw error need wsdl metadata to create a proxy
|
|
if (tWsdlService == null) {
|
|
throw new WebServiceException(ProviderApiMessages.NO_WSDL_NO_PORT(portInterface.getName()));
|
|
}
|
|
|
|
}
|
|
WSDLPort portModel = getPortModel(tWsdlService, portName);
|
|
return getPort(portModel.getEPR(), portName, portInterface, new WebServiceFeatureList(features));
|
|
}
|
|
|
|
public <T> T getPort(EndpointReference epr, Class<T> portInterface, WebServiceFeature... features) {
|
|
return getPort(WSEndpointReference.create(epr),portInterface,features);
|
|
}
|
|
|
|
public <T> T getPort(WSEndpointReference wsepr, Class<T> portInterface, WebServiceFeature... features) {
|
|
//get the portType from SEI, so that it can be used if EPR does n't have endpointName
|
|
WebServiceFeatureList featureList = new WebServiceFeatureList(features);
|
|
QName portTypeName = RuntimeModeler.getPortTypeName(portInterface, getMetadadaReader(featureList, portInterface.getClassLoader()));
|
|
//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, featureList);
|
|
}
|
|
|
|
protected <T> T getPort(WSEndpointReference wsepr, QName portName, Class<T> portInterface,
|
|
WebServiceFeatureList features) {
|
|
ComponentFeature cf = features.get(ComponentFeature.class);
|
|
if (cf != null && !Target.STUB.equals(cf.getTarget())) {
|
|
throw new IllegalArgumentException();
|
|
}
|
|
ComponentsFeature csf = features.get(ComponentsFeature.class);
|
|
if (csf != null) {
|
|
for (ComponentFeature cfi : csf.getComponentFeatures()) {
|
|
if (!Target.STUB.equals(cfi.getTarget()))
|
|
throw new IllegalArgumentException();
|
|
}
|
|
}
|
|
features.addAll(this.features);
|
|
|
|
SEIPortInfo spi = addSEI(portName, portInterface, features);
|
|
return createEndpointIFBaseProxy(wsepr,portName,portInterface,features, spi);
|
|
}
|
|
|
|
@Override
|
|
public <T> T getPort(Class<T> portInterface, WebServiceFeature... features) {
|
|
//get the portType from SEI
|
|
QName portTypeName = RuntimeModeler.getPortTypeName(portInterface, getMetadadaReader(new WebServiceFeatureList(features), portInterface.getClassLoader()));
|
|
WSDLService tmpWsdlService = this.wsdlService;
|
|
if (tmpWsdlService == null) {
|
|
// assigning it to local variable and not setting it back to this.wsdlService intentionally
|
|
// as we don't want to include the service instance with information gathered from sei
|
|
tmpWsdlService = getWSDLModelfromSEI(portInterface);
|
|
//still null? throw error need wsdl metadata to create a proxy
|
|
if(tmpWsdlService == null) {
|
|
throw new WebServiceException(ProviderApiMessages.NO_WSDL_NO_PORT(portInterface.getName()));
|
|
}
|
|
}
|
|
//get the first port corresponding to the SEI
|
|
WSDLPort port = tmpWsdlService.getMatchingPort(portTypeName);
|
|
if (port == null) {
|
|
throw new WebServiceException(ClientMessages.UNDEFINED_PORT_TYPE(portTypeName));
|
|
}
|
|
QName portName = port.getName();
|
|
return getPort(portName, portInterface,features);
|
|
}
|
|
|
|
public <T> T getPort(Class<T> 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 <T> Dispatch<T> createDispatch(QName portName, Class<T> aClass, Service.Mode mode) throws WebServiceException {
|
|
return createDispatch(portName, aClass, mode, EMPTY_FEATURES);
|
|
}
|
|
|
|
@Override
|
|
public <T> Dispatch<T> createDispatch(QName portName, WSEndpointReference wsepr, Class<T> aClass, Service.Mode mode, WebServiceFeature... features) {
|
|
return createDispatch(portName, wsepr, aClass, mode, new WebServiceFeatureList(features));
|
|
}
|
|
|
|
public <T> Dispatch<T> createDispatch(QName portName, WSEndpointReference wsepr, Class<T> aClass, Service.Mode mode, WebServiceFeatureList features) {
|
|
PortInfo port = safeGetPort(portName);
|
|
|
|
ComponentFeature cf = features.get(ComponentFeature.class);
|
|
if (cf != null && !Target.STUB.equals(cf.getTarget())) {
|
|
throw new IllegalArgumentException();
|
|
}
|
|
ComponentsFeature csf = features.get(ComponentsFeature.class);
|
|
if (csf != null) {
|
|
for (ComponentFeature cfi : csf.getComponentFeatures()) {
|
|
if (!Target.STUB.equals(cfi.getTarget()))
|
|
throw new IllegalArgumentException();
|
|
}
|
|
}
|
|
features.addAll(this.features);
|
|
|
|
BindingImpl binding = port.createBinding(features, null, null);
|
|
binding.setMode(mode);
|
|
Dispatch<T> dispatch = Stubs.createDispatch(port, this, binding, aClass, mode, wsepr);
|
|
serviceInterceptor.postCreateDispatch((WSBindingProvider) dispatch);
|
|
return dispatch;
|
|
}
|
|
|
|
public <T> Dispatch<T> createDispatch(QName portName, Class<T> aClass, Service.Mode mode, WebServiceFeature... features) {
|
|
return createDispatch(portName, aClass, mode, new WebServiceFeatureList(features));
|
|
}
|
|
|
|
public <T> Dispatch<T> createDispatch(QName portName, Class<T> aClass, Service.Mode mode, WebServiceFeatureList features) {
|
|
WSEndpointReference wsepr = null;
|
|
boolean isAddressingEnabled = false;
|
|
AddressingFeature af = features.get(AddressingFeature.class);
|
|
if (af == null) {
|
|
af = this.features.get(AddressingFeature.class);
|
|
}
|
|
if (af != null && af.isEnabled())
|
|
isAddressingEnabled = true;
|
|
MemberSubmissionAddressingFeature msa = features.get(MemberSubmissionAddressingFeature.class);
|
|
if (msa == null) {
|
|
msa = this.features.get(MemberSubmissionAddressingFeature.class);
|
|
}
|
|
if (msa != null && msa.isEnabled())
|
|
isAddressingEnabled = true;
|
|
if(isAddressingEnabled && wsdlService != null && wsdlService.get(portName) != null) {
|
|
wsepr = wsdlService.get(portName).getEPR();
|
|
}
|
|
return createDispatch(portName, wsepr, aClass, mode, features);
|
|
}
|
|
|
|
public <T> Dispatch<T> createDispatch(EndpointReference endpointReference, Class<T> 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<QName> names) {
|
|
StringBuilder sb = new StringBuilder();
|
|
for (QName qn : names) {
|
|
if (sb.length() > 0) sb.append(',');
|
|
sb.append(qn);
|
|
}
|
|
return sb;
|
|
}
|
|
|
|
public EndpointAddress getEndpointAddress(QName qName) {
|
|
PortInfo p = ports.get(qName);
|
|
return p != null ? p.targetEndpoint : null;
|
|
}
|
|
|
|
public Dispatch<Object> createDispatch(QName portName, JAXBContext jaxbContext, Service.Mode mode) throws WebServiceException {
|
|
return createDispatch(portName, jaxbContext, mode, EMPTY_FEATURES);
|
|
}
|
|
|
|
@Override
|
|
public Dispatch<Object> createDispatch(QName portName, WSEndpointReference wsepr, JAXBContext jaxbContext, Service.Mode mode, WebServiceFeature... features) {
|
|
return createDispatch(portName, wsepr, jaxbContext, mode, new WebServiceFeatureList(features));
|
|
}
|
|
|
|
protected Dispatch<Object> createDispatch(QName portName, WSEndpointReference wsepr, JAXBContext jaxbContext, Service.Mode mode, WebServiceFeatureList features) {
|
|
PortInfo port = safeGetPort(portName);
|
|
|
|
ComponentFeature cf = features.get(ComponentFeature.class);
|
|
if (cf != null && !Target.STUB.equals(cf.getTarget())) {
|
|
throw new IllegalArgumentException();
|
|
}
|
|
ComponentsFeature csf = features.get(ComponentsFeature.class);
|
|
if (csf != null) {
|
|
for (ComponentFeature cfi : csf.getComponentFeatures()) {
|
|
if (!Target.STUB.equals(cfi.getTarget()))
|
|
throw new IllegalArgumentException();
|
|
}
|
|
}
|
|
features.addAll(this.features);
|
|
|
|
BindingImpl binding = port.createBinding(features, null, null);
|
|
binding.setMode(mode);
|
|
Dispatch<Object> dispatch = Stubs.createJAXBDispatch(
|
|
port, binding, jaxbContext, mode,wsepr);
|
|
serviceInterceptor.postCreateDispatch((WSBindingProvider)dispatch);
|
|
return dispatch;
|
|
}
|
|
|
|
@Override
|
|
public @NotNull Container getContainer() {
|
|
return container;
|
|
}
|
|
|
|
public Dispatch<Object> createDispatch(QName portName, JAXBContext jaxbContext, Service.Mode mode, WebServiceFeature... webServiceFeatures) {
|
|
return createDispatch(portName, jaxbContext, mode, new WebServiceFeatureList(webServiceFeatures));
|
|
}
|
|
|
|
protected Dispatch<Object> createDispatch(QName portName, JAXBContext jaxbContext, Service.Mode mode, WebServiceFeatureList features) {
|
|
WSEndpointReference wsepr = null;
|
|
boolean isAddressingEnabled = false;
|
|
AddressingFeature af = features.get(AddressingFeature.class);
|
|
if (af == null) {
|
|
af = this.features.get(AddressingFeature.class);
|
|
}
|
|
if (af != null && af.isEnabled())
|
|
isAddressingEnabled = true;
|
|
MemberSubmissionAddressingFeature msa = features.get(MemberSubmissionAddressingFeature.class);
|
|
if (msa == null) {
|
|
msa = this.features.get(MemberSubmissionAddressingFeature.class);
|
|
}
|
|
if (msa != null && msa.isEnabled())
|
|
isAddressingEnabled = true;
|
|
if(isAddressingEnabled && wsdlService != null && wsdlService.get(portName) != null) {
|
|
wsepr = wsdlService.get(portName).getEPR();
|
|
}
|
|
return createDispatch(portName, wsepr, jaxbContext, mode, features);
|
|
}
|
|
|
|
public Dispatch<Object> 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(wsdlService, 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 {
|
|
WSDLModel eprWsdlMdl = parseWSDL(new URL(wsepr.getAddress()), eprWsdlSource, null);
|
|
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
|
|
WSDLPort 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;
|
|
|
|
}
|
|
|
|
private <T> T createProxy(final Class<T> portInterface, final InvocationHandler pis) {
|
|
|
|
// When creating the proxy, use a ClassLoader that can load classes
|
|
// from both the interface class and also from this classes
|
|
// classloader. This is necessary when this code is used in systems
|
|
// such as OSGi where the class loader for the interface class may
|
|
// not be able to load internal JAX-WS classes like
|
|
// "WSBindingProvider", but the class loader for this class may not
|
|
// be able to load the interface class.
|
|
final ClassLoader loader = getDelegatingLoader(portInterface.getClassLoader(),
|
|
WSServiceDelegate.class.getClassLoader());
|
|
|
|
// accessClassInPackage privilege needs to be granted ...
|
|
RuntimePermission perm = new RuntimePermission("accessClassInPackage.com.sun." + "xml.internal.*");
|
|
PermissionCollection perms = perm.newPermissionCollection();
|
|
perms.add(perm);
|
|
|
|
return AccessController.doPrivileged(
|
|
new PrivilegedAction<T>() {
|
|
@Override
|
|
public T run() {
|
|
Object proxy = Proxy.newProxyInstance(loader,
|
|
new Class[]{portInterface, WSBindingProvider.class, Closeable.class}, pis);
|
|
return portInterface.cast(proxy);
|
|
}
|
|
},
|
|
new AccessControlContext(
|
|
new ProtectionDomain[]{
|
|
new ProtectionDomain(null, perms)
|
|
})
|
|
);
|
|
}
|
|
|
|
private WSDLService getWSDLModelfromSEI(final Class sei) {
|
|
WebService ws = AccessController.doPrivileged(new PrivilegedAction<WebService>() {
|
|
public WebService run() {
|
|
return (WebService) sei.getAnnotation(WebService.class);
|
|
}
|
|
});
|
|
if (ws == null || ws.wsdlLocation().equals(""))
|
|
return null;
|
|
String wsdlLocation = ws.wsdlLocation();
|
|
wsdlLocation = JAXWSUtils.absolutize(JAXWSUtils.getFileOrURLName(wsdlLocation));
|
|
Source wsdl = new StreamSource(wsdlLocation);
|
|
WSDLService service = null;
|
|
|
|
try {
|
|
URL url = wsdl.getSystemId() == null ? null : new URL(wsdl.getSystemId());
|
|
WSDLModel model = parseWSDL(url, wsdl, sei);
|
|
service = model.getService(this.serviceName);
|
|
if (service == null)
|
|
throw new WebServiceException(
|
|
ClientMessages.INVALID_SERVICE_NAME(this.serviceName,
|
|
buildNameList(model.getServices().keySet())));
|
|
} catch (MalformedURLException e) {
|
|
throw new WebServiceException(ClientMessages.INVALID_WSDL_URL(wsdl.getSystemId()));
|
|
}
|
|
return service;
|
|
}
|
|
|
|
public QName getServiceName() {
|
|
return serviceName;
|
|
}
|
|
|
|
public Class getServiceClass() {
|
|
return serviceClass;
|
|
}
|
|
|
|
public Iterator<QName> getPorts() throws WebServiceException {
|
|
// KK: the spec seems to be ambigous about whether
|
|
// this returns ports that are dynamically added or not.
|
|
return ports.keySet().iterator();
|
|
}
|
|
|
|
@Override
|
|
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> T createEndpointIFBaseProxy(@Nullable WSEndpointReference epr, QName portName, Class<T> portInterface,
|
|
WebServiceFeatureList webServiceFeatures, SEIPortInfo eif) {
|
|
//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()));
|
|
}
|
|
|
|
BindingImpl binding = eif.createBinding(webServiceFeatures, portInterface);
|
|
InvocationHandler pis = getStubHandler(binding, eif, epr);
|
|
|
|
T proxy = createProxy(portInterface, pis);
|
|
|
|
if (serviceInterceptor != null) {
|
|
serviceInterceptor.postCreateProxy((WSBindingProvider)proxy, portInterface);
|
|
}
|
|
return proxy;
|
|
}
|
|
|
|
protected InvocationHandler getStubHandler(BindingImpl binding, SEIPortInfo eif, @Nullable WSEndpointReference epr) {
|
|
return new SEIStub(eif, binding, eif.model, epr);
|
|
}
|
|
|
|
/**
|
|
* Lists up the port names in WSDL. For error diagnostics.
|
|
*/
|
|
private StringBuilder buildWsdlPortNames() {
|
|
Set<QName> wsdlPortNames = new HashSet<QName>();
|
|
for (WSDLPort 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 WSDLPort getPortModel(WSDLService wsdlService, QName portName) {
|
|
WSDLPort 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 SEIPortInfo addSEI(QName portName, Class portInterface, WebServiceFeatureList features) throws WebServiceException {
|
|
boolean ownModel = useOwnSEIModel(features);
|
|
if (ownModel) {
|
|
// Create a new model and do not cache it
|
|
return createSEIPortInfo(portName, portInterface, features);
|
|
}
|
|
|
|
SEIPortInfo spi = seiContext.get(portName);
|
|
if (spi == null) {
|
|
spi = createSEIPortInfo(portName, portInterface, features);
|
|
seiContext.put(spi.portName, spi);
|
|
ports.put(spi.portName, spi);
|
|
}
|
|
return spi;
|
|
}
|
|
|
|
public SEIModel buildRuntimeModel(QName serviceName, QName portName, Class portInterface, WSDLPort wsdlPort, WebServiceFeatureList features) {
|
|
DatabindingFactory fac = DatabindingFactory.newInstance();
|
|
DatabindingConfig config = new DatabindingConfig();
|
|
config.setContractClass(portInterface);
|
|
config.getMappingInfo().setServiceName(serviceName);
|
|
config.setWsdlPort(wsdlPort);
|
|
config.setFeatures(features);
|
|
config.setClassLoader(portInterface.getClassLoader());
|
|
config.getMappingInfo().setPortName(portName);
|
|
config.setWsdlURL(wsdlURL);
|
|
// if ExternalMetadataFeature present, ExternalMetadataReader will be created ...
|
|
config.setMetadataReader(getMetadadaReader(features, portInterface.getClassLoader()));
|
|
|
|
com.sun.xml.internal.ws.db.DatabindingImpl rt = (com.sun.xml.internal.ws.db.DatabindingImpl)fac.createRuntime(config);
|
|
|
|
return rt.getModel();
|
|
}
|
|
|
|
private MetadataReader getMetadadaReader(WebServiceFeatureList features, ClassLoader classLoader) {
|
|
if (features == null) return null;
|
|
com.oracle.webservices.internal.api.databinding.ExternalMetadataFeature ef =
|
|
features.get(com.oracle.webservices.internal.api.databinding.ExternalMetadataFeature.class);
|
|
// TODO-Miran: would it be necessary to disable secure xml processing?
|
|
if (ef != null)
|
|
return ef.getMetadataReader(classLoader, false);
|
|
return null;
|
|
}
|
|
|
|
private SEIPortInfo createSEIPortInfo(QName portName, Class portInterface, WebServiceFeatureList features) {
|
|
WSDLPort wsdlPort = getPortModel(wsdlService, portName);
|
|
SEIModel model = buildRuntimeModel(serviceName, portName, portInterface, wsdlPort, features);
|
|
|
|
return new SEIPortInfo(this, portInterface, (SOAPSEIModel) model, wsdlPort);
|
|
}
|
|
|
|
private boolean useOwnSEIModel(WebServiceFeatureList features) {
|
|
return features.contains(UsesJAXBContextFeature.class);
|
|
}
|
|
|
|
public WSDLService getWsdlService() {
|
|
return wsdlService;
|
|
}
|
|
|
|
static class DaemonThreadFactory implements ThreadFactory {
|
|
@Override
|
|
public Thread newThread(Runnable r) {
|
|
Thread daemonThread = new Thread(r);
|
|
daemonThread.setDaemon(Boolean.TRUE);
|
|
return daemonThread;
|
|
}
|
|
}
|
|
|
|
protected static final WebServiceFeature[] EMPTY_FEATURES = new WebServiceFeature[0];
|
|
|
|
private static ClassLoader getDelegatingLoader(ClassLoader loader1, ClassLoader loader2) {
|
|
if (loader1 == null) return loader2;
|
|
if (loader2 == null) return loader1;
|
|
return new DelegatingLoader(loader1, loader2);
|
|
}
|
|
|
|
private static final class DelegatingLoader extends ClassLoader {
|
|
private final ClassLoader loader;
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
final int prime = 31;
|
|
int result = 1;
|
|
result = prime * result
|
|
+ ((loader == null) ? 0 : loader.hashCode());
|
|
result = prime * result
|
|
+ ((getParent() == null) ? 0 : getParent().hashCode());
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object obj) {
|
|
if (this == obj)
|
|
return true;
|
|
if (obj == null)
|
|
return false;
|
|
if (getClass() != obj.getClass())
|
|
return false;
|
|
DelegatingLoader other = (DelegatingLoader) obj;
|
|
if (loader == null) {
|
|
if (other.loader != null)
|
|
return false;
|
|
} else if (!loader.equals(other.loader))
|
|
return false;
|
|
if (getParent() == null) {
|
|
if (other.getParent() != null)
|
|
return false;
|
|
} else if (!getParent().equals(other.getParent()))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
DelegatingLoader(ClassLoader loader1, ClassLoader loader2) {
|
|
super(loader2);
|
|
this.loader = loader1;
|
|
}
|
|
|
|
protected Class findClass(String name) throws ClassNotFoundException {
|
|
return loader.loadClass(name);
|
|
}
|
|
|
|
protected URL findResource(String name) {
|
|
return loader.getResource(name);
|
|
}
|
|
}
|
|
}
|