blob: 5cd334b08a0427f56b8566177598dec812132478 [file] [log] [blame]
/*
* Copyright 2006 The Apache Software Foundation.
* Copyright 2006 International Business Machines Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.axis2.jaxws.spi;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import javax.xml.bind.JAXBContext;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.Service.Mode;
import javax.xml.ws.handler.HandlerResolver;
import javax.xml.ws.http.HTTPBinding;
import javax.xml.ws.soap.SOAPBinding;
import org.apache.axis2.client.ServiceClient;
import org.apache.axis2.jaxws.ExceptionFactory;
import org.apache.axis2.jaxws.JAXWSClientContext;
import org.apache.axis2.jaxws.client.JAXBDispatch;
import org.apache.axis2.jaxws.client.XMLDispatch;
import org.apache.axis2.jaxws.client.factory.DescriptorFactory;
import org.apache.axis2.jaxws.client.factory.ProxyHandlerFactory;
import org.apache.axis2.jaxws.client.proxy.BaseProxyHandler;
import org.apache.axis2.jaxws.client.proxy.ProxyDescriptor;
import org.apache.axis2.jaxws.description.DescriptionFactory;
import org.apache.axis2.jaxws.description.ServiceDescription;
import org.apache.axis2.jaxws.handler.PortData;
import org.apache.axis2.jaxws.handler.PortInfoImpl;
import org.apache.axis2.jaxws.i18n.Messages;
import org.apache.axis2.jaxws.registry.FactoryRegistry;
import org.apache.axis2.jaxws.util.WSDLWrapper;
/**
* The ServiceDelegate serves as the backing implementation for all of the
* methods in the {@link javax.xml.ws.Service} API. This is the plug
* point for the client implementation.
*/
public class ServiceDelegate extends javax.xml.ws.spi.ServiceDelegate {
private Executor executor;
private Map<QName, org.apache.axis2.jaxws.handler.PortData> ports;
private ServiceDescription serviceDescription;
private QName serviceQname;
private ServiceClient serviceClient = null;
// If no binding ID is available, use this one
private static String DEFAULT_BINDING_ID = SOAPBinding.SOAP11HTTP_BINDING;
public ServiceDelegate(URL url, QName qname, Class clazz) throws WebServiceException{
super();
this.serviceQname = qname;
ports = new Hashtable<QName, PortData>();
if(!isValidServiceName()){
throw ExceptionFactory.makeWebServiceException(Messages.getMessage("serviceDelegateConstruct0", ""));
}
serviceDescription = DescriptionFactory.createServiceDescription(url, serviceQname, clazz);
if (isValidWSDLLocation()) {
if(!isServiceDefined(serviceQname)){
throw ExceptionFactory.makeWebServiceException(Messages.getMessage("serviceDelegateConstruct0", serviceQname.toString(), url.toString()));
}
readPorts();
}
}
//================================================
// JAX-WS API methods
//================================================
/*
* (non-Javadoc)
* @see javax.xml.ws.spi.ServiceDelegate#addPort(javax.xml.namespace.QName, java.lang.String, java.lang.String)
*/
// Creates a DISPATCH ONLY port. Per JAXWS Sec 4.1 javax.xm..ws.Service, p. 49, ports added via addPort method
// are only suitibale for creating Distpach instances.
public void addPort(QName portName, String bindingId, String endpointAddress)
throws WebServiceException {
addPortData(portName, bindingId, endpointAddress);
DescriptionFactory.updateEndpoint(serviceDescription, null, portName, ServiceDescription.UpdateType.ADD_PORT);
}
/**
* Add a new port (either endpoint based or dispatch based) to the portData table.
* @param portName
* @param bindingId
* @param endpointAddress
*/
private void addPortData(QName portName, String bindingId, String endpointAddress) {
if(portName == null ){
throw ExceptionFactory.makeWebServiceException(Messages.getMessage("addPortErr2"));
}
if(bindingId!=null && !(bindingId.equals(SOAPBinding.SOAP11HTTP_BINDING) ||
bindingId.equals(SOAPBinding.SOAP12HTTP_BINDING))){
// TODO Is this the correct exception. Shouldn't this be a WebServiceException ?
throw new UnsupportedOperationException(Messages.getMessage("addPortErr0", portName.toString()));
}
if (bindingId == null) {
bindingId = DEFAULT_BINDING_ID;
}
if(!ports.containsKey(portName)){
PortData port = new PortInfoImpl(serviceQname, portName, bindingId, endpointAddress);
ports.put(portName, port);
}
else{
//TODO: Can same port have two different set of SOAPAddress
/*PortInfoImpl port =(PortInfoImpl) ports.get(portName);
port.setBindingID(bindingId);
port.setEndPointAddress(endpointAddress);
*/
throw new WebServiceException(Messages.getMessage("addPortDup", portName.toString()));
}
}
/*
* (non-Javadoc)
* @see javax.xml.ws.spi.ServiceDelegate#createDispatch(javax.xml.namespace.QName, java.lang.Class, javax.xml.ws.Service.Mode)
*/
public <T> Dispatch<T> createDispatch(QName qname, Class<T> clazz, Mode mode) throws WebServiceException {
if(qname == null){
throw ExceptionFactory.makeWebServiceException(Messages.getMessage("createDispatchFail0"));
}
if(!isPortValid(qname)){
throw ExceptionFactory.makeWebServiceException(Messages.getMessage("createDispatchFail1", qname.toString()));
}
PortData portData = (PortData) ports.get(qname);
if(portData == null){
throw ExceptionFactory.makeWebServiceException(Messages.getMessage("createDispatchFail2", qname.toString()));
}
DescriptionFactory.updateEndpoint(serviceDescription, null, qname, ServiceDescription.UpdateType.CREATE_DISPATCH);
// FIXME: This call needs to be revisited. Not really sure what we're trying to do here.
addBinding(portData.getBindingID());
XMLDispatch<T> dispatch = new XMLDispatch<T>(portData);
if (mode != null) {
dispatch.setMode(mode);
}
else {
dispatch.setMode(Service.Mode.PAYLOAD);
}
if (serviceClient == null)
serviceClient = getServiceClient(qname);
dispatch.setServiceClient(serviceClient);
dispatch.setServiceDelegate(this);
return dispatch;
}
/*
* (non-Javadoc)
* @see javax.xml.ws.spi.ServiceDelegate#createDispatch(javax.xml.namespace.QName, javax.xml.bind.JAXBContext, javax.xml.ws.Service.Mode)
*/
public Dispatch<java.lang.Object> createDispatch(QName qname, JAXBContext context, Mode mode) {
if (qname == null) {
throw ExceptionFactory.makeWebServiceException(Messages.getMessage("createDispatchFail0"));
}
if (!isPortValid(qname)) {
throw ExceptionFactory.makeWebServiceException(Messages.getMessage("createDispatchFail1", qname.toString()));
}
DescriptionFactory.updateEndpoint(serviceDescription, null, qname, ServiceDescription.UpdateType.CREATE_DISPATCH);
PortData portData = (PortData) ports.get(qname);
addBinding(portData.getBindingID());
JAXWSClientContext clientCtx = createClientContext(portData, Object.class, mode);
clientCtx.setJAXBContext(context);
JAXBDispatch<Object> dispatch = new JAXBDispatch(portData);
if (mode != null) {
dispatch.setMode(mode);
}
else {
dispatch.setMode(Service.Mode.PAYLOAD);
}
if (serviceClient == null)
serviceClient = getServiceClient(qname);
dispatch.setJAXBContext(context);
dispatch.setServiceClient(serviceClient);
dispatch.setServiceDelegate(this);
return dispatch;
}
/*
* (non-Javadoc)
* @see javax.xml.ws.spi.ServiceDelegate#getPort(java.lang.Class)
*/
public <T> T getPort(Class<T> sei) throws WebServiceException {
return getPort(null, sei);
}
/*
* (non-Javadoc)
* @see javax.xml.ws.spi.ServiceDelegate#getPort(javax.xml.namespace.QName, java.lang.Class)
*/
public <T> T getPort(QName portName, Class<T> sei) throws WebServiceException {
/* TODO Check to see if WSDL Location is provided.
* if not check WebService annotation's WSDLLocation
* if both are not provided then throw exception.
* (JLB): I'm not sure lack of WSDL should cause an exception
*/
if(!isValidWSDLLocation()){
//TODO: Should I throw Exception if no WSDL
//throw ExceptionFactory.makeWebServiceException("WSLD Not found");
}
if(sei == null){
throw ExceptionFactory.makeWebServiceException(Messages.getMessage("getPortInvalidSEI", portName.toString(), "null"));
}
/*TODO: if portQname is null then fetch it from annotation.
* if portQname is provided then add that to the ports table.
*/
// TODO: Move the annotation processing to the DescriptionFactory
DescriptionFactory.updateEndpoint(serviceDescription, sei, portName, ServiceDescription.UpdateType.GET_PORT);
if(portName!=null){
String address = "";
if(isValidWSDLLocation()){
address = getWSDLWrapper().getSOAPAddress(serviceQname, portName);
}
if(ports.get(portName)==null){
addPortData(portName, null, address);
}
}
DescriptorFactory df = (DescriptorFactory)FactoryRegistry.getFactory(DescriptorFactory.class);
ProxyDescriptor pd = df.create(sei, serviceDescription);
pd.setPort(ports.get(portName));
ProxyHandlerFactory phf =(ProxyHandlerFactory) FactoryRegistry.getFactory(ProxyHandlerFactory.class);
BaseProxyHandler proxyHandler = phf.create(pd, this);
Class[] seiClazz = new Class[]{sei, BindingProvider.class};
Object proxyClass = Proxy.newProxyInstance(sei.getClassLoader(), seiClazz, proxyHandler);
return sei.cast(proxyClass);
}
/*
* (non-Javadoc)
* @see javax.xml.ws.spi.ServiceDelegate#getExecutor()
*/
public Executor getExecutor() {
//FIXME: Use client provider executor too.
executor = getDefaultExecutor();
return executor;
}
/*
* (non-Javadoc)
* @see javax.xml.ws.spi.ServiceDelegate#getHandlerResolver()
*/
public HandlerResolver getHandlerResolver() {
return null;
}
/*
* (non-Javadoc)
* @see javax.xml.ws.spi.ServiceDelegate#getPorts()
*/
public Iterator<QName> getPorts() {
return null;
}
/*
* (non-Javadoc)
* @see javax.xml.ws.spi.ServiceDelegate#getServiceName()
*/
public QName getServiceName() {
return serviceQname;
}
/*
* (non-Javadoc)
* @see javax.xml.ws.spi.ServiceDelegate#getWSDLDocumentLocation()
*/
public URL getWSDLDocumentLocation() {
return serviceDescription.getWSDLLocation();
}
/*
* (non-Javadoc)
* @see javax.xml.ws.spi.ServiceDelegate#setExecutor(java.util.concurrent.Executor)
*/
public void setExecutor(Executor e) {
if (e == null) {
throw ExceptionFactory.makeWebServiceException(Messages.getMessage("cannotSetExcutorToNull"));
}
executor = e;
}
/*
* (non-Javadoc)
* @see javax.xml.ws.spi.ServiceDelegate#setHandlerResolver(javax.xml.ws.handler.HandlerResolver)
*/
public void setHandlerResolver(HandlerResolver handlerresolver) {
}
//================================================
// Internal public APIs
//================================================
/**
* Get the ServiceDescription tree that this ServiceDelegate
*/
public ServiceDescription getServiceDescription() {
return serviceDescription;
}
//TODO Change when ServiceDescription has to return ServiceClient or OperationClient
/**
*
*/
public ServiceClient getServiceClient(QName portQName) throws WebServiceException {
return serviceDescription.getServiceClient(portQName);
}
//================================================
// Impl methods
//================================================
//TODO: Need to make the default number of threads configurable
private Executor getDefaultExecutor(){
return Executors.newFixedThreadPool(3);
}
private <T> JAXWSClientContext<T> createClientContext(PortData portData, Class<T> clazz, Mode mode){
JAXWSClientContext<T> clientContext = new JAXWSClientContext<T>();
clientContext.setServiceDescription(serviceDescription);
clientContext.setPort(portData);
clientContext.setClazz(clazz);
clientContext.setServiceMode(mode);
clientContext.setExecutor(this.getExecutor());
return clientContext;
}
private boolean isPortValid(QName portName){
return ports!=null && ports.size() >0 && ports.containsKey(portName);
}
private boolean isValidServiceName(){
return serviceQname != null && !"".equals(serviceQname.toString().trim());
}
private boolean isValidWSDLLocation(){
URL wsdlLocation = getWSDLDocumentLocation();
return wsdlLocation != null && !"".equals(wsdlLocation.toString().trim());
}
private void readPorts(){
String[] portNames = getWSDLWrapper().getPorts(serviceQname);
String targetNamespace = getWSDLWrapper().getTargetNamespace();
for(String portName: portNames){
QName portQname = new QName(targetNamespace, portName);
String address = getWSDLWrapper().getSOAPAddress(serviceQname, portQname);
//TODO: get Binding ID from WSDL and add it here.
PortData portInfo = new PortInfoImpl(serviceQname, portQname, DEFAULT_BINDING_ID, address);
ports.put(portQname, portInfo);
}
}
// TODO: Remove this method and put the WSDLWrapper methods on the ServiceDescriptor directly
private WSDLWrapper getWSDLWrapper() {
return serviceDescription.getWSDLWrapper();
}
private boolean isServiceDefined(QName serviceName){
return getWSDLWrapper().getService(serviceName)!= null;
}
private void addBinding(String bindingId){
// TODO: before creating binding do I have to do something with Handlers ... how is Binding related to Handler, this mistry sucks!!!
if(bindingId != null){
//TODO: create all the bindings here
if(bindingId.equals(SOAPBinding.SOAP11HTTP_BINDING)){
//instantiate soap11 binding implementation here and call setBinding in BindingProvider
}
if(bindingId.equals(SOAPBinding.SOAP12HTTP_BINDING)){
//instantiate soap11 binding implementation here and call setBinding in BindingProvider
}
if(bindingId.equals(HTTPBinding.HTTP_BINDING)){
//instantiate http binding implementation here and call setBinding in BindingProvider
}
}
}
}