/*
* $Id: WebService.java,v 1.22 2005/08/19 17:33:49 kwalsh Exp $
*
* Copyright (c) 2005 Sun Microsystems, Inc.
* All rights reserved.
*/
package com.sun.xml.ws.client;
import com.sun.xml.ws.client.dispatch.DispatchBase;
import com.sun.xml.ws.wsdl.WSDLContext;
import com.sun.xml.ws.wsdl.parser.WSDLDocument;
import com.sun.xml.ws.wsdl.parser.Binding;
import com.sun.xml.ws.model.RuntimeModel;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.StringRefAddr;
import javax.xml.bind.JAXBContext;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Dispatch;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.security.SecurityConfiguration;
import java.io.Serializable;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Proxy;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.rmi.Remote;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
/**
* Service
objects provide the client view of a Web service.
*
* 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.
*
* @since JAX-WS 2.0
*
* @see javax.xml.ws.ServiceFactory
* @see javax.xml.ws.handler.HandlerRegistry
* @see java.util.concurrent.Executor
* @author WS Development Team
*/
public class WebService
implements WebServiceInterface, Serializable, Referenceable {
protected static final String GET = "get";
protected HashSet ports;
protected HashMap dispatchPorts;
protected HandlerRegistryImpl handlerRegistry;
protected Object serviceProxy;
protected URL wsdlLocation;
protected final ServiceContext serviceContext;
protected Executor executor;
private HashSet seiProxies;
public WebService(ServiceContext scontext) {
serviceContext = scontext;
this.dispatchPorts = new HashMap();
seiProxies = new HashSet();
}
private void processServiceContext(QName portName, Class portInterface) throws WebServiceException {
ServiceContextBuilder.completeServiceContext(serviceContext, portInterface);
}
public URL getWSDLLocation() {
if (wsdlLocation == null)
setWSDLLocation(getWsdlLocation());
return wsdlLocation;
}
public void setWSDLLocation(URL location) {
wsdlLocation = location;
}
public Executor getExecutor() {
if (executor != null)
//todo:needs to be decoupled from service at execution
{
return (Executor) executor;
} else
executor = Executors.newFixedThreadPool(5, new DaemonThreadFactory());
return executor;
}
public void setExecutor(Executor executor) {
executor = this.executor;
}
public Object getPort(QName portName, Class portInterface)
throws WebServiceException {
Object seiProxy = createEndpointIFBaseProxy(portName, portInterface);
seiProxies.add(seiProxy);
if (portName != null) {
addPort(portName);
}
return seiProxy;
}
public Object getPort(Class portInterface) throws WebServiceException {
return createEndpointIFBaseProxy(null, portInterface);
}
//todo: rename addPort :spec tbd
public void addPort(QName portName, URI bindingId, String endpointAddress) throws WebServiceException {
if (!dispatchPorts.containsKey(portName)) {
dispatchPorts.put(portName, new PortInfoBase(endpointAddress, portName, bindingId));
} else
throw new WebServiceException("Port " + portName.toString() + " already exists can not create a port with the same name.");
// need to add port to list for HandlerRegistry
addPort(portName);
}
public Dispatch createDispatch(QName qName, Class aClass, Mode mode) throws WebServiceException {
return createDispatchClazz(qName, aClass, mode);
}
public Dispatch createDispatch(QName qName, JAXBContext jaxbContext, Mode mode) throws WebServiceException {
return createDispatchJAXB(qName, jaxbContext, mode);
}
public QName getServiceName() {
return serviceContext.getServiceName();
}
public Iterator getPorts() throws WebServiceException {
if (ports == null)
populatePorts();
if (ports.size() == 0)
throw noWsdlException();
return ports.iterator();
}
public java.net.URL getWSDLDocumentLocation() {
return getWsdlLocation();
}
public SecurityConfiguration getSecurityConfiguration() {
throw new UnsupportedOperationException("Security is not implemented for JAXWS 2.0 Early Access.");
}
public HandlerRegistryImpl getHandlerRegistry() {
//need to return handlerRegistryImpl?
if (handlerRegistry == null) {
if (serviceContext.getRegistry() != null)
handlerRegistry = serviceContext.getRegistry();
else {
handlerRegistry = new HandlerRegistryImpl(getPortsAsSet());
}
}
return (HandlerRegistryImpl) handlerRegistry;
}
public Reference getReference() throws NamingException {
Reference reference =
new Reference(getClass().getName(),
"com.sun.xml.rpc.naming.ServiceReferenceResolver",
null);
String serviceName = ServiceReferenceResolver.registerService(this);
reference.add(new StringRefAddr("ServiceName", serviceName));
return reference;
}
protected void addPorts(QName[] ports) {
if (ports != null) {
for (int i = 0; i < ports.length; ++i) {
addPort(ports[i]);
}
}
}
private void populatePorts() {
if (ports == null)
ports = new HashSet();
if (serviceContext.getServiceName() == null) {
if (serviceContext.getWsdlContext() != null) {
serviceContext.setServiceName(serviceContext.getWsdlContext().getFirstServiceName());
}
}
Set knownPorts = null;
if (serviceContext.getWsdlContext() != null) {
knownPorts =
serviceContext.getWsdlContext().getPortsAsSet(serviceContext.getServiceName());
if (knownPorts != null) {
QName[] portz = (QName[]) knownPorts.toArray(new QName[knownPorts.size()]);
addPorts(portz);
}
}
}
protected void addPort(QName port) {
if (ports == null)
populatePorts();
ports.add(port);
if (handlerRegistry != null) {
handlerRegistry.addPort(port);
}
}
protected WebServiceException noWsdlException() {
return new WebServiceException("dii.service.no.wsdl.available");
}
private Object createEndpointIFBaseProxy(QName portName, Class portInterface) throws WebServiceException {
processServiceContext(portName, portInterface);
if (serviceContext.getWsdlContext().contains(serviceContext.getServiceName(), portName).size() < 1) {
throw new WebServiceException("Port " + portName + "is not found in service " + serviceContext.getServiceName());
}
return buildEndpointIFProxy(portName, portInterface);
}
protected HashSet getPortsAsSet() {
if (ports == null)
populatePorts();
return ports;
}
/*
* Set the binding on the binding provider. Called by the service
* class when creating the binding provider.
*/
protected void setBindingOnProvider(InternalBindingProvider provider,
QName portName, URI bindingId) {
provider._setBinding(getHandlerRegistry().createBinding(portName, bindingId));
}
private Dispatch createDispatchClazz(QName port, Class clazz, Mode mode) throws WebServiceException {
PortInfoBase dispatchPort = getDispatchPort(port);
if (dispatchPort != null) {
DispatchBase dBase = new DispatchBase((PortInfoBase) dispatchPort, clazz, mode, this);
setBindingOnProvider(dBase, port, dBase._getBindingId());
return dBase;
} else {
throw new WebServiceException("Port must be defined in order to create Dispatch");
}
}
/**
* Looks up the dispatch port. If the port has not been added, then try look in the WSDL to find it.
*/
protected PortInfoBase getDispatchPort(QName port) {
PortInfoBase answer = dispatchPorts.get(port);
if (answer == null) {
// lets try find it in the WSDL
WSDLContext wsdlContext = serviceContext.getWsdlContext();
QName serviceName = serviceContext.getServiceName();
Binding binding = wsdlContext.getWsdlBinding(serviceName, port);
if (binding != null) {
try {
URI bindingId = new URI(binding.getBindingId());
String endpointAddress = wsdlContext.getEndpoint(serviceName);
addPort(port, bindingId, endpointAddress );
answer = dispatchPorts.get(port);
}
catch (URISyntaxException e) {
throw new WebServiceException(e);
}
}
}
return answer;
}
private Dispatch createDispatchJAXB(QName port, JAXBContext jaxbContext, Mode mode) throws WebServiceException {
PortInfoBase dispatchPort = getDispatchPort(port);
if (dispatchPort != null) {
DispatchBase dBase = new DispatchBase((PortInfoBase) dispatchPort, jaxbContext, mode, this);
setBindingOnProvider(dBase, port, dBase._getBindingId());
return dBase;
} else {
throw new WebServiceException("Port must be defined in order to create Dispatch");
}
}
private URL getWsdlLocation() {
return serviceContext.getWsdlContext().getWsdlLocation();
}
private Object buildEndpointIFProxy(QName portQName, Class portInterface)
throws WebServiceException {
EndpointIFContext eif = completeEndpointIFContext(serviceContext, portQName, portInterface);
//apply parameter bindings
RuntimeModel model = eif.getRuntimeContext().getModel();
if(portQName != null){
Binding binding = serviceContext.getWsdlContext().getWsdlBinding(serviceContext.getServiceName(), portQName);
model.applyParameterBinding(binding);
}
//needs cleaning up
EndpointIFInvocationHandler handler =
new EndpointIFInvocationHandler(portInterface,
eif, this, getServiceName()); //need handler registry passed in here
setBindingOnProvider(handler, portQName, handler._getBindingId());
Object proxy = Proxy.newProxyInstance(portInterface.getClassLoader(),
new Class[]{
portInterface, Remote.class, BindingProvider.class,
BindingProviderProperties.class, AnnotatedElement.class,
com.sun.xml.ws.spi.runtime.StubBase.class
}, handler);
handler.setProxy((Object) proxy);
return (BindingProvider) proxy;
}
private EndpointIFContext completeEndpointIFContext(ServiceContext serviceContext, QName portQName, Class portInterface) {
EndpointIFContext context = serviceContext.getEndpointIFContext(portInterface.getName());
WSDLContext wscontext = serviceContext.getWsdlContext();
if (wscontext != null) {
String endpoint = wscontext.getEndpoint(serviceContext.getServiceName(), portQName);
URI bindingID = wscontext.getBindingID();
context.setServiceName(serviceContext.getServiceName());
context.setPortInfo(portQName, endpoint, bindingID);
}
return context;
}
}
class DaemonThreadFactory implements ThreadFactory {
public Thread newThread(Runnable r) {
Thread daemonThread = new Thread(r);
daemonThread.setDaemon(Boolean.TRUE);
return daemonThread;
}
}