/**
 * JBoss, the OpenSource J2EE webOS
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jboss.webservice.client;

// $Id: ServiceFactoryImpl.java,v 1.7.4.3 2005/06/28 09:25:20 tdiesler Exp $

import org.jboss.logging.Logger;
import org.jboss.util.NotImplementedException;
import org.jboss.webservice.deployment.ServiceDescription;
import org.jboss.webservice.metadata.jaxrpcmapping.JavaWsdlMapping;
import org.jboss.webservice.metadata.jaxrpcmapping.JavaWsdlMappingFactory;
import org.jboss.webservice.metadata.wsdl.WSDL11DefinitionFactory;

import javax.wsdl.Definition;
import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceException;
import javax.xml.rpc.ServiceFactory;
import java.net.URL;
import java.util.Properties;

/**
 * The javax.xml.rpc.ServiceFactory is a factory for the creation of instances of the type javax.xml.rpc.Service.
 * <p/>
 * This class follows the abstract static factory design pattern.
 * This enables a J2SE based client to create a Service instance in a portable
 * manner without using the constructor of the Service implementation class.
 *
 * @author Thomas.Diesler@jboss.org
 * @version 1.1
 */
public class ServiceFactoryImpl extends ServiceFactory
{
   // provide logging
   private final Logger log = Logger.getLogger(ServiceFactoryImpl.class);

   /**
    * Create an instance of the generated service implementation class for a given service interface, if available.
    *
    * @param serviceInterface Service interface
    * @return A Service
    * @throws ServiceException If there is any error while creating the specified service, including the case where a
    *                          generated service implementation class cannot be located
    */
   public Service loadService(Class serviceInterface) throws ServiceException
   {
      throw new NotImplementedException();
   }

   /**
    * Create an instance of the generated service implementation class for a given service interface, if available.
    * An implementation may use the provided wsdlDocumentLocation and properties to help locate the generated implementation class.
    * If no such class is present, a ServiceException will be thrown.
    *
    * @param wsdlDocumentLocation URL for the WSDL document location for the service or null
    * @param serviceInterface     Service interface
    * @param props                A set of implementation-specific properties to help locate the generated service implementation class
    * @return A Service
    * @throws ServiceException If there is any error while creating the specified service, including the case where a
    *                          generated service implementation class cannot be located
    */
   public Service loadService(URL wsdlDocumentLocation, Class serviceInterface, Properties props) throws ServiceException
   {
      throw new NotImplementedException();
   }

   /**
    * Create an instance of the generated service implementation class for a given service, if available.
    * The service is uniquely identified by the wsdlDocumentLocation and serviceName arguments.
    * An implementation may use the provided properties to help locate the generated implementation class.
    * If no such class is present, a ServiceException will be thrown.
    *
    * @param wsdlDocumentLocation URL for the WSDL document location for the service or null
    * @param serviceName          Qualified name for the service
    * @param props                A set of implementation-specific properties to help locate the generated service implementation class
    * @return A Service
    * @throws ServiceException If there is any error while creating the specified service, including the case where a generated service implementation class cannot be located
    */
   public Service loadService(URL wsdlDocumentLocation, QName serviceName, Properties props) throws ServiceException
   {
      throw new NotImplementedException();
   }

   /**
    * Create a <code>Service</code> instance.
    *
    * @param serviceName QName for the service
    * @return Service.
    * @throws ServiceException If any error in creation of the specified service
    */
   public Service createService(QName serviceName) throws ServiceException
   {
      return new ServiceImpl(serviceName);
   }

   /**
    * Create a <code>Service</code> instance.
    *
    * @param wsdlDocumentLocation URL for the WSDL document location
    * @param serviceName          QName for the service.
    * @return Service.
    * @throws ServiceException If any error in creation of the
    *                          specified service
    */
   public Service createService(URL wsdlDocumentLocation, QName serviceName) throws ServiceException
   {
      ServiceImpl service = new ServiceImpl(wsdlDocumentLocation, serviceName);

      try
      {
         WSDL11DefinitionFactory factory = WSDL11DefinitionFactory.newInstance();
         Definition wsdlDefinition = factory.parse(wsdlDocumentLocation);
         
         javax.wsdl.Service wsdlService = wsdlDefinition.getService(serviceName);
         if (wsdlDefinition.getService(serviceName) == null)
            throw new IllegalStateException("Cannot find wsd service: " + serviceName);
         
         // derive single port name from wsdl service 
         String portName = null;
         if (wsdlService.getPorts().size() == 1)
         {
            portName = (String)wsdlService.getPorts().keySet().iterator().next();
         }
         
         // In this case we have no jaxrpc-mapping.xml
         ServiceDescription serviceDesc = new ServiceDescription(wsdlDefinition, null, null, portName);
         service.initService(serviceDesc, portName);
      }
      catch (Exception e)
      {
         throw new ServiceException(e);
      }

      return service;
   }

   /**
    * Create a <code>Service</code> instance.
    * <p/>
    * Note, this method is not in the {@link ServiceFactory} interface, it provides the service
    * with additional ws4ee wsdl/java mapping information
    *
    * @param wsdlLocation    URL for the WSDL document location
    * @param mappingLocation An optional URL for the jaxrpc-mapping.xml location
    * @param ws4eeMetaData An optional URL for the jboss propriatary deployment descriptor, see wiki for details.  
    * @param serviceName     QName for the service.
    * @param portName        An optional port name
    * @return Service.
    * @throws ServiceException If any error in creation of the specified service
    */
   public Service createService(URL wsdlLocation, URL mappingLocation, URL ws4eeMetaData, QName serviceName, String portName) throws ServiceException
   {
      ServiceImpl service = new ServiceImpl(wsdlLocation, serviceName);

      try
      {
         WSDL11DefinitionFactory wsdlFactory = WSDL11DefinitionFactory.newInstance();
         Definition wsdlDefinition = wsdlFactory.parse(wsdlLocation);

         JavaWsdlMapping javaWsdlMapping = null;
         if (mappingLocation != null)
         {
            JavaWsdlMappingFactory mappingFactory = JavaWsdlMappingFactory.newInstance();
            javaWsdlMapping = mappingFactory.parse(mappingLocation);
         }

         ServiceDescription serviceDesc = new ServiceDescription(wsdlDefinition, javaWsdlMapping, ws4eeMetaData, portName);
         service.initService(serviceDesc, portName);
      }
      catch (Exception e)
      {
         log.error(e.getMessage(), e);
         throw new ServiceException(e);
      }

      return service;
   }
}
