This project has retired. For details please refer to its Attic page.
WSDL2UDDI.java

WSDL2UDDI.java

/*
 * Copyright 2001-2011 The Apache Software Foundation.
 * 
 * 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.juddi.v3.client.mapping.wsdl;

import org.apache.juddi.v3.client.mapping.Common2UDDI;
import java.net.MalformedURLException;
import java.net.URL;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import javax.wsdl.Binding;
import javax.wsdl.Definition;
import javax.wsdl.Port;
import javax.wsdl.PortType;
import javax.wsdl.Service;
import javax.wsdl.WSDLException;
import javax.wsdl.extensions.http.HTTPAddress;
import javax.wsdl.extensions.http.HTTPBinding;
import javax.wsdl.extensions.soap.SOAPAddress;
import javax.wsdl.extensions.soap.SOAPBinding;
import javax.wsdl.extensions.soap12.SOAP12Address;
import javax.wsdl.extensions.soap12.SOAP12Binding;
import javax.xml.namespace.QName;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.juddi.api_v3.AccessPointType;
import org.apache.juddi.jaxb.PrintUDDI;
import org.apache.juddi.v3.client.config.Property;
import org.apache.juddi.v3.client.config.UDDIClerk;
import org.apache.juddi.v3.client.config.UDDIClient;
import org.apache.juddi.v3.client.config.UDDIKeyConvention;
import org.apache.juddi.v3.client.mapping.ServiceRegistrationResponse;
import org.apache.juddi.v3.client.mapping.URLLocalizer;
import org.apache.juddi.v3.client.mapping.wadl.WADL2UDDI;
import org.apache.juddi.v3.client.transport.TransportException;
import org.uddi.api_v3.AccessPoint;
import org.uddi.api_v3.BindingTemplate;
import org.uddi.api_v3.BindingTemplates;
import org.uddi.api_v3.BusinessService;
import org.uddi.api_v3.BusinessServices;
import org.uddi.api_v3.CategoryBag;
import org.uddi.api_v3.FindTModel;
import org.uddi.api_v3.InstanceDetails;
import org.uddi.api_v3.KeyedReference;
import org.uddi.api_v3.Name;
import org.uddi.api_v3.OverviewDoc;
import org.uddi.api_v3.OverviewURL;
import org.uddi.api_v3.TModel;
import org.uddi.api_v3.TModelDetail;
import org.uddi.api_v3.TModelInstanceDetails;
import org.uddi.api_v3.TModelInstanceInfo;
import org.w3c.dom.Element;

/**
 * This class implements the OASIS <a
 * href="http://www.oasis-open.org/committees/uddi-spec/doc/tn/uddi-spec-tc-tn-wsdl-v202-20040631.htm">
 * 'Using WSDL in a UDDI Registry, Version 2.0.2'</a> technote. This class
 * creates a detailed mapping of WSDL 1.1 artifacts to the UDDI V3 data model.
 * <ul> <th>Section 2.4 in the technote</th> <li>2.4.1 wsdl:portType ->
 * uddi:tModel - {@link #createWSDLPortTypeTModels(String, Map)}</li> <li>2.4.2
 * wsdl:binding -> uddi:tModel -
 * {@link #createWSDLBindingTModels(String, Map)}</li> <li>TODO: 2.4.3
 * wsdl:service -> uddi:businessService</li> <li>TODO: 2.4.4 wsdl:port ->
 * uddi:bindingTemplate</li> <li>TODO: 2.4.5 wsdl:port Address Extensions ->
 * uddi:bindingTemplate</li> </ul>
 *
 * @see WADL2UDDI
 * @see BPEL2UDDI
 * @author Kurt T Stam
 * @since 3.1.5
 */
public class WSDL2UDDI {

        private static final Log log = LogFactory.getLog(WSDL2UDDI.class);
        private String keyDomainURI;
        private String businessKey;
        private String lang;
        private UDDIClerk clerk = null;
        private Properties properties = null;
        private URLLocalizer urlLocalizer;

        /**
         * Required Properties are: businessName, for example: 'Apache'
         * nodeName, for example: 'uddi.example.org_80' keyDomain, for example:
         * juddi.apache.org
         *
         * Optional Properties are: lang: for example: 'nl'
         *
         * @param clerk - can be null if register/unregister methods are not
         * used.
         * @param urlLocalizer - A reference to an custom
         * @param properties - required values keyDomain, businessKey, nodeName
         * @throws ConfigurationException
         */
        public WSDL2UDDI(UDDIClerk clerk, URLLocalizer urlLocalizer, Properties properties) throws ConfigurationException {
                super();
                if (properties == null) {
                        throw new IllegalArgumentException("properties");
                }
                this.clerk = clerk;
                this.urlLocalizer = urlLocalizer;
                this.properties = properties;

                if (clerk != null) {
                        if (!properties.containsKey("keyDomain")) {
                                throw new ConfigurationException("Property keyDomain is a required property when using WSDL2UDDI.");
                        }
                        if (!properties.containsKey("businessKey") && !properties.containsKey("businessName")) {
                                throw new ConfigurationException("Either property businessKey, or businessName, is a required property when using WSDL2UDDI.");
                        }
                        if (!properties.containsKey("nodeName")) {
                                if (properties.containsKey("serverName") && properties.containsKey("serverPort")) {
                                        String nodeName = properties.getProperty("serverName") + "_" + properties.getProperty("serverPort");
                                        properties.setProperty("nodeName", nodeName);
                                } else {
                                        throw new ConfigurationException("Property nodeName is not defined and is a required property when using WSDL2UDDI.");
                                }
                        }
                }

                //Obtaining values from the properties
                this.keyDomainURI = "uddi:" + properties.getProperty("keyDomain") + ":";
                if (properties.contains(Property.BUSINESS_KEY)) {
                        this.businessKey = properties.getProperty(Property.BUSINESS_KEY);
                } else {
                        //using the BusinessKey Template, and the businessName to construct the key 
                        this.businessKey = UDDIKeyConvention.getBusinessKey(properties);
                }
                this.lang = properties.getProperty(Property.LANG, Property.DEFAULT_LANG);
        }

        public BusinessServices registerBusinessServices(Definition wsdlDefinition) throws RemoteException, ConfigurationException, TransportException, WSDLException, MalformedURLException {

                BusinessServices businessServices = new BusinessServices();

                for (Object serviceName : wsdlDefinition.getAllServices().keySet()) {
                        QName serviceQName = (QName) serviceName;
                        Service service = wsdlDefinition.getService(serviceQName);
                        BusinessService businessService = null;
                        //add service
                        URL serviceUrl = null;
                        if (service.getPorts() != null && service.getPorts().size() > 0) {
                                for (Object portName : service.getPorts().keySet()) {
                                        businessService = registerBusinessService(serviceQName, (String) portName, serviceUrl, wsdlDefinition).getBusinessService();
                                }
                        }
                        if (businessService != null) {
                                businessServices.getBusinessService().add(businessService);
                        }
                }

                return businessServices;

        }

        @SuppressWarnings("unchecked")
        public ServiceRegistrationResponse registerBusinessService(QName serviceQName, String portName, URL serviceUrl, Definition wsdlDefinition) throws RemoteException, ConfigurationException, TransportException, WSDLException, MalformedURLException {

                String genericWSDLURL = wsdlDefinition.getDocumentBaseURI();   //TODO maybe point to repository version
                ServiceRegistrationResponse response = new ServiceRegistrationResponse();
                String serviceKey = UDDIKeyConvention.getServiceKey(properties, serviceQName.getLocalPart());
                BusinessService businessService = lookupService(serviceKey);
                if (businessService == null) {
                        List<TModel> tModels = new ArrayList<TModel>();
                        // Create the PortType tModels
                        Map<QName, PortType> portTypes = (Map<QName, PortType>) wsdlDefinition.getAllPortTypes();
                        tModels.addAll(createWSDLPortTypeTModels(genericWSDLURL, portTypes));
                        // Create the Binding tModels
                        Map<QName, Binding> bindings = (Map<QName, Binding>) wsdlDefinition.getAllBindings();
                        tModels.addAll(createWSDLBindingTModels(genericWSDLURL, bindings));
                        // Register these tModels
                        for (TModel tModel : tModels) {
                                clerk.register(tModel);
                        }
                        // Service
                        businessService = createBusinessService(serviceQName, wsdlDefinition);
                        // Register this Service
                        clerk.register(businessService);
                }
                //Add the BindingTemplate to this Service
                BindingTemplate binding = createWSDLBinding(serviceQName, portName, serviceUrl, wsdlDefinition);
                // Register BindingTemplate
                if (binding.getAccessPoint() != null) {
                        clerk.register(binding);
                        if (businessService.getBindingTemplates() == null) {
                                businessService.setBindingTemplates(new BindingTemplates());
                        }
                        businessService.getBindingTemplates().getBindingTemplate().add(binding);
                        response.setBindingKey(binding.getBindingKey());
                }
                response.setBusinessService(businessService);
                return response;
        }

        public String[] unRegisterBusinessServices(Definition wsdlDefinition) throws RemoteException, ConfigurationException, TransportException, MalformedURLException {

                String[] businessServices = new String[wsdlDefinition.getAllServices().size()];
                int i = 0;
                for (Object serviceName : wsdlDefinition.getAllServices().keySet()) {
                        QName serviceQName = (QName) serviceName;
                        Service service = wsdlDefinition.getService(serviceQName);
                        //unregister service
                        URL serviceUrl = null;
                        if (service.getPorts() != null && service.getPorts().size() > 0) {
                                for (Object portName : service.getPorts().keySet()) {
                                        //construct the accessURL
                                        serviceUrl = new URL(getBindingURL((Port) service.getPorts().get(portName)));
                                        businessServices[i++] = unRegisterBusinessService(serviceQName, (String) portName, serviceUrl);
                                }
                        }
                }
                return businessServices;
        }

        public String unRegisterBusinessService(QName serviceName, String portName, URL serviceUrl) throws RemoteException, ConfigurationException, TransportException {

                String serviceKey = UDDIKeyConvention.getServiceKey(properties, serviceName.getLocalPart());
                BusinessService service = lookupService(serviceKey);
                boolean isRemoveServiceIfNoTemplates = true;
                String bindingKey = UDDIKeyConvention.getBindingKey(properties, serviceName, portName, serviceUrl);
                //check if this bindingKey is in the service's binding templates
                for (BindingTemplate bindingTemplate : service.getBindingTemplates().getBindingTemplate()) {
                        if (bindingKey.equals(bindingTemplate.getBindingKey())) {
                                clerk.unRegisterBinding(bindingKey);
                                //if this is the last binding for this service, and 
                                if (service.getBindingTemplates().getBindingTemplate().size() == 1 && isRemoveServiceIfNoTemplates) {
                                        clerk.unRegisterService(serviceKey);
                                        if (bindingTemplate.getTModelInstanceDetails() != null
                                                && bindingTemplate.getTModelInstanceDetails().getTModelInstanceInfo() != null) {
                                                for (TModelInstanceInfo tModelInstanceInfo : bindingTemplate.getTModelInstanceDetails().getTModelInstanceInfo()) {
                                                        String tModelKey = tModelInstanceInfo.getTModelKey();
                                                        TModelDetail tModelDetail = clerk.getTModelDetail(tModelKey);
                                                        //delete all tModels assuming they are the portType and Binding tModels.
                                                        if (tModelDetail.getTModel() != null && tModelDetail.getTModel().size() > 0) {
                                                                for (TModel tModel : tModelDetail.getTModel()) {
                                                                        if (!tModel.getTModelKey().startsWith("uddi:uddi.org:")) {
                                                                                clerk.unRegisterTModel(tModel.getTModelKey());
                                                                        } else {
                                                                                log.info("Skipping the removal of " + tModel.getTModelKey() + " because it starts with uddi.org");
                                                                        }
                                                                }
                                                        }
                                                }
                                        }
                                }
                        }
                }
                return serviceKey;
        }

        public String getKeyDomainURI() {
                return keyDomainURI;
        }

        public void setKeyDomain(String keyDomainURI) {
                this.keyDomainURI = keyDomainURI;
        }

        public String getLang() {
                return lang;
        }

        public void setLang(String lang) {
                this.lang = lang;
        }

        /**
         * <h3>2.4.2 wsdl:binding -> uddi:tModel</h3>
         *
         * <p>
         * A wsdl:binding MUST be modeled as a uddi:tModel. The minimum
         * information that must be captured about a binding is its entity type,
         * its local name, its namespace, the location of the WSDL document that
         * defines the binding, the portType that it implements, its protocol,
         * and, optionally, the transport information. Capturing the entity type
         * enables users to search for tModels that represent binding artifacts.
         * Capturing the local name, namespace, and WSDL location enables users
         * to locate the definition of the specified binding artifact. The link
         * to the portType enables users to search for bindings that implement a
         * particular portType.</p>
         *
         * <p>
         * A wsdl:binding corresponds to a WSDL service interface definition as
         * defined by the mapping in the Version 1 Best Practice. To maintain
         * compatibility with the previous mapping, the binding must also be
         * characterized as type "wsdlSpec".</p>
         *
         * <p>
         * The wsdl:binding information is captured as follows:</p>
         *
         * <p>
         * The uddi:name element of the tModel MUST be the value of the name
         * attribute of the wsdl:binding.</p>
         *
         * <p>
         * The tModel MUST contain a categoryBag, and the categoryBag MUST
         * contain at least the following keyedReference elements:</p> <ol> <li>
         * A keyedReference with a tModelKey of the WSDL Entity Type category
         * system and a keyValue of "binding".</li> <li> A keyedReference with a
         * tModelKey of the WSDL portType Reference category system and a
         * keyValue of the tModelKey that models the wsdl:portType to which the
         * wsdl:binding relates.</li> <li> A keyedReference with a tModelKey of
         * the UDDI Types category system and a keyValue of "wsdlSpec" for
         * backward compatibility[1].</li> <li> One or two keyedReferences as
         * required to capture the protocol and optionally the transport
         * information refer to the next section.</li> </ol>
         *
         * <p>
         * If the wsdl:binding has a targetNamespace then the categoryBag MUST
         * also contain an additional keyedReference with a tModelKey of the XML
         * Namespace category system and a keyValue of the target namespace of
         * the wsdl:definitions element that contains the wsdl:binding. If the
         * targetNamespace is absent from the binding, a categoryBag MUST NOT
         * contain a keyedReference to the XML Namespace category system.</p>
         *
         * <p>
         * The tModel MUST contain an overviewDoc with an overviewURL containing
         * the location of the WSDL document that describes the
         * wsdl:binding.</p>
         *
         * <h4>2.4.2.1 wsdl:binding Extensions</h4>
         *
         * <p>
         * Information about the protocol and transport, if applicable,
         * specified in an extension to the wsdl:binding is used to categorize
         * the binding tModel as described in the following sections. This
         * information is specified using two of the category systems defined in
         * this Technical Note:</p> <ol> <li> Protocol Categorization</li> <li>
         * Transport Categorization</li> </ol>
         * <p>
         * The valid values for the Protocol Categorization category system are
         * tModelKeys of tModels that are categorized as protocol tModels.
         * Similarly, the valid values for the Transport Categorization category
         * system are tModelKeys of tModels that are categorized as transport
         * tModels.</p>
         * <p>
         * The reason for having these two categorization schemes that take
         * tModel keys as values is to allow other standard or proprietary
         * protocols and transports to be defined and used in the same way as
         * the standard SOAP and HTTP protocols and transport.</p>
         *
         * <h4>2.4.2.1.1 soap:binding</h4>
         *
         * <p>
         * If the wsdl:binding contains a soap:binding extensibility element
         * from the http://schemas.xmlsoap.org/wsdl/soap/ namespace then the
         * categoryBag MUST include a keyedReference with a tModelKey of the
         * Protocol Categorization category system and a keyValue of the
         * tModelKey of the SOAP Protocol tModel.</p>
         *
         * <p>
         * If the value of the transport attribute of the soap:binding element
         * is http://schemas.xmlsoap.org/soap/http then the categoryBag MUST
         * include a keyedReference with a tModelKey of the Transport
         * Categorization category system and a keyValue of the tModelKey of the
         * HTTP Transport tModel.</p>
         *
         * <p>
         * If the value of the transport attribute is anything else, then the
         * bindingTemplate MUST include an additional keyedReference with a
         * tModelKey of the Transport Categorization category system and a
         * keyValue of the tModelKey of an appropriate transport tModel.</p>
         *
         * <h4>2.4.2.1.2 http:binding</h4>
         *
         * <p>
         * If the wsdl:binding contains an http:binding extensibility element
         * from the http://schemas.xmlsoap.org/wsdl/http/ namespace then the
         * categoryBag MUST include a keyedReference with a tModelKey of the
         * Protocol Categorization category system and a keyValue of the
         * tModelKey of the HTTP Protocol tModel.</p>
         *
         * <p>
         * Note that this is a different tModel from the HTTP Transport tModel,
         * and in this case there is no separate transport tModel, and therefore
         * no keyedReference in the categoryBag from the Transport
         * Categorization category system.</p>
         *
         * <h4>2.4.2.1.3 Other wsdl:binding Extensions</h4>
         *
         * <p>
         * Other wsdl:binding extensibility elements are handled in a similar
         * fashion. It is assumed that vendors who provide other bindings will
         * provide the appropriate protocol and transport tModels.</p>
         *
         * Example Code
         * <pre>
         * URL url = new URL("http://graphical.weather.gov/xml/SOAP_server/ndfdXMLserver.php?wsdl");
         * String domain = url.getHost();
         * ReadWSDL rw = new ReadWSDL();
         * Definition wsdlDefinition = rw.readWSDL(url);
         * properties.put("keyDomain", domain);
         * properties.put("businessName", domain);
         * properties.put("serverName", url.getHost());
         * properties.put("serverPort", url.getPort());
         * wsdlURL = wsdlDefinition.getDocumentBaseURI();
         * WSDL2UDDI wsdl2UDDI = new WSDL2UDDI(null, new URLLocalizerDefaultImpl(), properties);
         * Map allBindings = wsdlDefinition.getAllBindings();
         * Set<TModel> createWSDLBindingTModels = wsdl2UDDI.createWSDLBindingTModels(url.toString(), allBindings);
         * </pre>
         *
         * @param wsdlURL
         * @param bindings Map
         * @return set of WSDL Binding tModels
         * @throws WSDLException
         */
        public Set<TModel> createWSDLBindingTModels(String wsdlURL, Map<QName, Binding> bindings) throws WSDLException {

                Set<TModel> tModels = new HashSet<TModel>();
                //Register a tModel for each portType
                for (QName qName : bindings.keySet()) {
                        String localpart = qName.getLocalPart();
                        String namespace = qName.getNamespaceURI();
                        // Build the tModel
                        TModel tModel = new TModel();
                        // Set the Key
                        tModel.setTModelKey(keyDomainURI + localpart);
                        // Set the Name
                        Name name = new Name();
                        name.setLang(lang);
                        name.setValue(localpart);
                        tModel.setName(name);
                        // Set the OverviewURL
                        OverviewURL overviewURL = new OverviewURL();
                        overviewURL.setUseType(AccessPointType.WSDL_DEPLOYMENT.toString());
                        overviewURL.setValue(wsdlURL);
                        OverviewDoc overviewDoc = new OverviewDoc();
                        overviewDoc.setOverviewURL(overviewURL);
                        tModel.getOverviewDoc().add(overviewDoc);
                        // Set the categoryBag
                        CategoryBag categoryBag = new CategoryBag();

                        if (namespace != null && !"".equals(namespace)) {
                                // A keyedReference with a tModelKey of the WSDL Entity Type category system and a keyValue of "binding".
                                KeyedReference namespaceReference = newKeyedReference(
                                        "uddi:uddi.org:xml:namespace", "uddi-org:xml:namespace", namespace);
                                categoryBag.getKeyedReference().add(namespaceReference);
                        }

                        // A keyedReference with a tModelKey of the WSDL Entity Type category system and a keyValue of "binding".
                        KeyedReference typesReference = newKeyedReference(
                                "uddi:uddi.org:wsdl:types", "uddi-org:wsdl:types", "binding");
                        categoryBag.getKeyedReference().add(typesReference);

            // A keyedReference with a tModelKey of the WSDL portType Reference category system and a keyValue 
                        // of the tModelKey that models the wsdl:portType to which the wsdl:binding relates.
                        Binding binding = bindings.get(qName);
                        String portTypeKey = keyDomainURI + binding.getPortType().getQName().getLocalPart();
                        KeyedReference namespaceReference = newKeyedReference(
                                "uddi:uddi.org:wsdl:porttypereference", "uddi-org:wsdl:portTypeReference",
                                portTypeKey);
                        categoryBag.getKeyedReference().add(namespaceReference);

            //  A keyedReference with a tModelKey of the UDDI Types category system and a keyValue of 
                        // "wsdlSpec" for backward compatibility.
                        KeyedReference typesReferenceBackwardsCompatible = newKeyedReference(
                                "uddi:uddi.org:categorization:types", "uddi-org:types", "wsdlSpec");
                        categoryBag.getKeyedReference().add(typesReferenceBackwardsCompatible);

                        // One or two keyedReferences as required to capture the protocol
                        for (Object object : binding.getExtensibilityElements()) {
                                SOAPBinding sb = null;
                                SOAP12Binding sb12 = null;
                                HTTPBinding hb = null;

                                try {
                                        hb = (HTTPBinding) object;
                                } catch (Exception x) {
                                }
                                try {
                                        sb = (SOAPBinding) object;
                                } catch (Exception x) {
                                }
                                try {
                                        sb12 = (SOAP12Binding) object;
                                } catch (Exception x) {
                                }
                                if (sb != null) {
                    // If the wsdl:binding contains a soap:binding extensibility element from the 
                                        // 'http://schemas.xmlsoap.org/wsdl/soap/' namespace then the categoryBag MUST 
                                        //include a keyedReference with a tModelKey of the Protocol Categorization 
                                        // category system and a keyValue of the tModelKey of the SOAP Protocol tModel.
                                        SOAPBinding soapBinding = sb;
                                        KeyedReference soapProtocol = newKeyedReference(
                                                "uddi:uddi.org:wsdl:categorization:protocol", "uddi-org:protocol:soap", "uddi:uddi.org:protocol:soap");
                                        categoryBag.getKeyedReference().add(soapProtocol);
                    // If the value of the transport attribute of the soap:binding element 
                                        // is 'http://schemas.xmlsoap.org/soap/http' then the categoryBag MUST 
                                        // include a keyedReference with a tModelKey of the Transport Categorization 
                                        // category system and a keyValue of the tModelKey of the HTTP Transport tModel.
                                        if ("http://schemas.xmlsoap.org/soap/http".equals(soapBinding.getTransportURI())) {
                                                KeyedReference httpTransport = newKeyedReference(
                                                        "uddi:uddi.org:wsdl:categorization:transport", "uddi-org:http", "uddi:uddi.org:transport:http");
                                                categoryBag.getKeyedReference().add(httpTransport);
                                        } else if (soapBinding.getTransportURI() != null) {
                        // TODO If the value of the transport attribute is anything else, 
                                                // then the bindingTemplate MUST include an additional keyedReference with a tModelKey 
                                                // of the Transport Categorization category system and a keyValue of the tModelKey of 
                                                // an appropriate transport tModel.
                                                log.warn("not implemented, binding transport is " + soapBinding.getTransportURI());
                                        }

                                } else if (hb != null) {

                    // If the wsdl:binding contains an http:binding extensibility element from the 
                                        // http://schemas.xmlsoap.org/wsdl/http/ namespace then the categoryBag MUST 
                                        // include a keyedReference with a tModelKey of the Protocol Categorization 
                                        // category system and a keyValue of the tModelKey of the HTTP Protocol tModel.
                                        KeyedReference soapProtocol = newKeyedReference(
                                                "uddi:uddi.org:wsdl:categorization:protocol", "uddi-org:protocol:http", "uddi:uddi.org:protocol:http");
                                        categoryBag.getKeyedReference().add(soapProtocol);
                                } else if (sb12 != null) {
                    // If the wsdl:binding contains a soap:binding extensibility element from the 
                                        // 'http://schemas.xmlsoap.org/wsdl/soap/' namespace then the categoryBag MUST 
                                        //include a keyedReference with a tModelKey of the Protocol Categorization 
                                        // category system and a keyValue of the tModelKey of the SOAP Protocol tModel.

                                        KeyedReference soapProtocol = newKeyedReference(
                                                "uddi:uddi.org:wsdl:categorization:protocol", "uddi-org:protocol:soap", "uddi:uddi.org:protocol:soap");
                                        categoryBag.getKeyedReference().add(soapProtocol);
                    // If the value of the transport attribute of the soap:binding element 
                                        // is 'http://schemas.xmlsoap.org/soap/http' then the categoryBag MUST 
                                        // include a keyedReference with a tModelKey of the Transport Categorization 
                                        // category system and a keyValue of the tModelKey of the HTTP Transport tModel.
                                        if ("http://schemas.xmlsoap.org/soap/http".equals(sb12.getTransportURI())) {
                                                KeyedReference httpTransport = newKeyedReference(
                                                        "uddi:uddi.org:wsdl:categorization:transport", "uddi-org:http", "uddi:uddi.org:transport:http");
                                                categoryBag.getKeyedReference().add(httpTransport);
                                        } else if (sb12.getTransportURI() != null) {
                        // TODO If the value of the transport attribute is anything else, 
                                                // then the bindingTemplate MUST include an additional keyedReference with a tModelKey 
                                                // of the Transport Categorization category system and a keyValue of the tModelKey of 
                                                // an appropriate transport tModel.
                                                log.warn("not implemented, binding transport is " + sb12.getTransportURI());
                                        }

                                } else {
                                        log.warn("Unrecongnized binding type: " + object.getClass().getCanonicalName() + ". Generated"
                                                + "binding tModel may be missing the required (according to WSDL2UDDI spec) "
                                                + "uddi:uddi.org:wsdl:categorization:protocol keyedReference.");

                                }
                        }

                        tModel.setCategoryBag(categoryBag);
                        tModels.add(tModel);
                }
                return tModels;
        }

        /**
         * <h3>2.4.1 wsdl:portType -> uddi:tModel</h3>
         *
         * <p>
         * A wsdl:portType MUST be modeled as a uddi:tModel.</p>
         *
         * <p>
         * The minimum information that must be captured about a portType is its
         * entity type, its local name, its namespace, and the location of the
         * WSDL document that defines the portType. Capturing the entity type
         * enables users to search for tModels that represent portType
         * artifacts. Capturing the local name, namespace, and WSDL location
         * enables users to locate the definition of the specified portType
         * artifact.</p>
         *
         * <p>
         * The wsdl:portType information is captured as follows:</p>
         *
         * <p>
         * The uddi:name element of the tModel MUST be the value of the name
         * attribute of the wsdl:portType.</p>
         *
         * <p>
         * The tModel MUST contain a categoryBag, and the categoryBag MUST
         * contain a keyedReference with a tModelKey of the WSDL Entity Type
         * category system and a keyValue of "portType".</p>
         *
         * <p>
         * If the wsdl:portType has a targetNamespace then the categoryBag MUST
         * also contain an additional keyedReference with a tModelKey of the XML
         * Namespace category system and a keyValue of the target namespace of
         * the wsdl:definitions element that contains the wsdl:portType. If the
         * targetNamespace is absent from the portType, a categoryBag MUST NOT
         * contain a keyedReference to the XML Namespace category system.</p>
         *
         * <p>
         * The tModel MUST contain an overviewDoc with an overviewURL containing
         * the location of the WSDL document that describes the
         * wsdl:portType.</p>
         * Example Code
         * <pre>
         * URL url = new URL("http://graphical.weather.gov/xml/SOAP_server/ndfdXMLserver.php?wsdl");
         * String domain = url.getHost();
         * ReadWSDL rw = new ReadWSDL();
         * Definition wsdlDefinition = rw.readWSDL(url);
         * properties.put("keyDomain", domain);
         * properties.put("businessName", domain);
         * properties.put("serverName", url.getHost());
         * properties.put("serverPort", url.getPort());
         * wsdlURL = wsdlDefinition.getDocumentBaseURI();
         * WSDL2UDDI wsdl2UDDI = new WSDL2UDDI(null, new URLLocalizerDefaultImpl(), properties);
         * Map<QName, PortType> portTypes = (Map<QName, PortType>) wsdlDefinition.getAllPortTypes();
         * Set<TModel> portTypeTModels = wsdl2UDDI.createWSDLPortTypeTModels(wsdlURL, portTypes);
         * </pre>
         *
         * @param wsdlURL This is used to set the Overview URL
         * @param portTypes Map
         * @return set of WSDL PortType tModels
         * @throws WSDLException
         */
        public Set<TModel> createWSDLPortTypeTModels(String wsdlURL, Map<QName, PortType> portTypes) throws WSDLException {
                Set<TModel> tModels = new HashSet<TModel>();
                // Create a tModel for each portType
                for (QName qName : portTypes.keySet()) {
                        // Build the tModel
                        TModel tModel = new TModel();
                        String localpart = qName.getLocalPart();
                        String namespace = qName.getNamespaceURI();
                        // Set the Key
                        tModel.setTModelKey(keyDomainURI + localpart);
            // Set the Name. The uddi:name element of the tModel MUST be the value of
                        // the name attribute of the wsdl:portType.
                        Name name = new Name();
                        name.setLang(lang);
                        name.setValue(localpart);
                        tModel.setName(name);
            // Set the OverviewURL. The tModel MUST contain an overviewDoc with an 
                        // overviewURL containing the location of the WSDL document that 
                        // describes the wsdl:portType.
                        OverviewURL overviewURL = new OverviewURL();
                        overviewURL.setUseType(AccessPointType.WSDL_DEPLOYMENT.toString());
                        overviewURL.setValue(wsdlURL);
                        OverviewDoc overviewDoc = new OverviewDoc();
                        overviewDoc.setOverviewURL(overviewURL);
                        tModel.getOverviewDoc().add(overviewDoc);
                        // Create the categoryBag, The tModel MUST contain a categoryBag
                        CategoryBag categoryBag = new CategoryBag();

            // the categoryBag MUST contain a keyedReference with a tModelKey of the WSDL 
                        // Entity Type category system and a keyValue of "portType".
                        KeyedReference typesReference = newKeyedReference(
                                "uddi:uddi.org:wsdl:types", "uddi-org:wsdl:types", "portType");
                        categoryBag.getKeyedReference().add(typesReference);

            // If the wsdl:portType has a targetNamespace then the categoryBag MUST also contain an 
                        // additional keyedReference with a tModelKey of the XML Namespace category system and a 
                        // keyValue of the target namespace of the wsdl:definitions element that contains the 
                        // wsdl:portType. If the targetNamespace is absent from the portType, a categoryBag 
                        // MUST NOT contain a keyedReference to the XML Namespace category system.
                        if (namespace != null && !"".equals(namespace)) {
                                KeyedReference namespaceReference = newKeyedReference(
                                        "uddi:uddi.org:xml:namespace", "uddi-org:xml:namespace", namespace);
                                categoryBag.getKeyedReference().add(namespaceReference);
                        }

                        tModel.setCategoryBag(categoryBag);
                        tModels.add(tModel);
                }
                return tModels;
        }

        protected static KeyedReference newKeyedReference(String tModelKey, String keyName, String value) {
                KeyedReference typesReference = new KeyedReference();
                typesReference.setTModelKey(tModelKey);
                typesReference.setKeyName(keyName);
                typesReference.setKeyValue(value);
                return typesReference;
        }

        /**
         * Builds a finder to find the binding tModels for a portType.
         *
         * @param portType
         * @param namespace
         * @return tModel info
         */
        public static FindTModel createFindBindingTModelForPortType(String portType, String namespace) {

                FindTModel findTModel = new FindTModel();
                CategoryBag categoryBag = new CategoryBag();

                if (namespace != null && namespace.length() != 0) {
                        KeyedReference namespaceReference = newKeyedReference(
                                "uddi:uddi.org:xml:namespace", "uddi-org:xml:namespace", namespace);
                        categoryBag.getKeyedReference().add(namespaceReference);
                }
                KeyedReference bindingReference = newKeyedReference(
                        "uddi:uddi.org:wsdl:types", "uddi-org:wsdl:types", "binding");
                categoryBag.getKeyedReference().add(bindingReference);

                KeyedReference portTypeReference = newKeyedReference(
                        "uddi:uddi.org:wsdl:porttypereference", "uddi-org:wsdl:portTypeReference", portType);
                categoryBag.getKeyedReference().add(portTypeReference);

                findTModel.setCategoryBag(categoryBag);

                if (log.isDebugEnabled()) {
                        log.debug(new PrintUDDI<FindTModel>().print(findTModel));
                }
                return findTModel;
        }

        /**
         * Builds a finder to find the portType tModels for a portType.
         *
         * @param portTypeName
         * @param namespace
         * @return tModel info
         */
        public static FindTModel createFindPortTypeTModelForPortType(String portTypeName, String namespace) {

                FindTModel findTModel = new FindTModel();
                Name name = new Name();
                name.setLang("en");
                name.setValue(portTypeName);
                findTModel.setName(name);

                CategoryBag categoryBag = new CategoryBag();
                if (namespace != null && namespace.length() != 0) {
                        KeyedReference namespaceReference = newKeyedReference(
                                "uddi:uddi.org:xml:namespace", "uddi-org:xml:namespace", namespace);
                        categoryBag.getKeyedReference().add(namespaceReference);
                }
                KeyedReference bindingReference = newKeyedReference(
                        "uddi:uddi.org:wsdl:types", "uddi-org:wsdl:types", "portType");
                categoryBag.getKeyedReference().add(bindingReference);

                findTModel.setCategoryBag(categoryBag);

                if (log.isDebugEnabled()) {
                        log.debug(new PrintUDDI<FindTModel>().print(findTModel));
                }
                return findTModel;
        }

        /**
         * Perform a lookup by serviceKey, and will return null if not found.
         *
         * @param serviceKey
         * @return business service
         * @throws RemoteException
         * @throws ConfigurationException
         * @throws TransportException
         */
        private BusinessService lookupService(String serviceKey) throws RemoteException, ConfigurationException, TransportException {

                //Checking if this serviceKey already exist
                BusinessService service = clerk.getServiceDetail(serviceKey);
                return service;
        }

        /**
         * Creates a business service based off of a WSDL definition<Br>No
         * changes are made to the UDDI endpoints using this method
         * <br>
         * Example Code:
         * <pre>
         * URL url = new URL("http://graphical.weather.gov/xml/SOAP_server/ndfdXMLserver.php?wsdl");
         * String domain = url.getHost();
         * ReadWSDL rw = new ReadWSDL();
         * Definition wsdlDefinition = rw.readWSDL(url);
         * properties.put("keyDomain", domain);
         * properties.put("businessName", domain);
         * properties.put("serverName", url.getHost());
         * properties.put("serverPort", url.getPort());
         * wsdlURL = wsdlDefinition.getDocumentBaseURI();
         * WSDL2UDDI wsdl2UDDI = new WSDL2UDDI(null, new URLLocalizerDefaultImpl(), properties);
         * BusinessServices businessServices = wsdl2UDDI.createBusinessServices(wsdlDefinition);
         * </pre>
         *
         * @param wsdlDefinition must not be null
         * @return a business service
         * @throws MalformedURLException
         * @throws IllegalArgumentException if the wsdlDefinition is null
         */
        public BusinessServices createBusinessServices(Definition wsdlDefinition) throws MalformedURLException {
                if (wsdlDefinition == null) {
                        throw new IllegalArgumentException();
                }
                BusinessServices businessServices = new BusinessServices();
                for (Object serviceName : wsdlDefinition.getAllServices().keySet()) {
                        QName serviceQName = (QName) serviceName;
                        Service service = wsdlDefinition.getService(serviceQName);
                        BusinessService businessService = createBusinessService(serviceQName, wsdlDefinition);
            //service.getExtensibilityElements().
                        //add the bindingTemplates
                        URL serviceUrl = null;
                        if (service.getPorts() != null && service.getPorts().size() > 0) {
                                businessService.setBindingTemplates(new BindingTemplates());
                                for (Object portName : service.getPorts().keySet()) {
                                        BindingTemplate bindingTemplate = createWSDLBinding(serviceQName, (String) portName, serviceUrl, wsdlDefinition);
                                        businessService.getBindingTemplates().getBindingTemplate().add(bindingTemplate);
                                }
                        }
                        businessServices.getBusinessService().add(businessService);
                }

                return businessServices;
        }

        /**
         * Creates a UDDI Business Service.
         *
         * @param serviceQName
         * @param wsdlDefinition
         * @return a business service
         */
        protected BusinessService createBusinessService(QName serviceQName, Definition wsdlDefinition) {

                log.debug("Constructing Service UDDI Information for " + serviceQName);
                BusinessService service = new BusinessService();
                // BusinessKey
                service.setBusinessKey(businessKey);
                // ServiceKey
                service.setServiceKey(UDDIKeyConvention.getServiceKey(properties, serviceQName.getLocalPart()));
                // Description
                String serviceDescription = properties.getProperty(Property.SERVICE_DESCRIPTION, Property.DEFAULT_SERVICE_DESCRIPTION);
                // Override with the service description from the WSDL if present
                if (wsdlDefinition.getService(serviceQName) != null) {
                        Element docElement = wsdlDefinition.getService(serviceQName).getDocumentationElement();
                        if (docElement != null && docElement.getTextContent() != null) {
                                serviceDescription = docElement.getTextContent();
                        }
                }

                service.getDescription().addAll(Common2UDDI.mapDescription(serviceDescription, lang));

                // Service name
                Name sName = new Name();
                sName.setLang(lang);
                sName.setValue(serviceQName.getLocalPart());
                service.getName().add(sName);

                CategoryBag categoryBag = new CategoryBag();

                String namespace = serviceQName.getNamespaceURI();
                if (namespace != null && namespace.length() != 0) {
                        KeyedReference namespaceReference = newKeyedReference(
                                "uddi:uddi.org:xml:namespace", "uddi-org:xml:namespace", namespace);
                        categoryBag.getKeyedReference().add(namespaceReference);
                }

                KeyedReference serviceReference = newKeyedReference(
                        "uddi:uddi.org:wsdl:types", "uddi-org:wsdl:types", "service");
                categoryBag.getKeyedReference().add(serviceReference);

                KeyedReference localNameReference = newKeyedReference(
                        "uddi:uddi.org:xml:localname", "uddi-org:xml:localName", serviceQName.getLocalPart());
                categoryBag.getKeyedReference().add(localNameReference);

                service.setCategoryBag(categoryBag);

                return service;
        }

        protected BindingTemplate createWSDLBinding(QName serviceQName, String portName, URL serviceUrl, Definition wsdlDefinition) throws MalformedURLException {

                BindingTemplate bindingTemplate = new BindingTemplate();
                // Set BusinessService Key
                bindingTemplate.setServiceKey(UDDIKeyConvention.getServiceKey(properties, serviceQName.getLocalPart()));

                if (serviceUrl != null) {
                        // Set AccessPoint
                        AccessPoint accessPoint = new AccessPoint();
                        accessPoint.setUseType(AccessPointType.END_POINT.toString());
                        accessPoint.setValue(urlLocalizer.rewrite(serviceUrl));
                        bindingTemplate.setAccessPoint(accessPoint);
                        // Set Binding Key
                        String bindingKey = UDDIKeyConvention.getBindingKey(properties, serviceQName, portName, serviceUrl);
                        bindingTemplate.setBindingKey(bindingKey);
                }

                Service service = wsdlDefinition.getService(serviceQName);
                if (service != null) {
                        TModelInstanceDetails tModelInstanceDetails = new TModelInstanceDetails();

                        Port port = service.getPort(portName);
                        if (port != null) {
                                if (serviceUrl == null) {
                                        for (Object element : port.getExtensibilityElements()) {
                                                String location = null;
                                                if (element instanceof SOAPAddress) {
                                                        SOAPAddress address = (SOAPAddress) element;
                                                        location = urlLocalizer.rewrite(new URL(address.getLocationURI()));
                                                } else if (element instanceof HTTPAddress) {
                                                        HTTPAddress address = (HTTPAddress) element;
                                                        urlLocalizer.rewrite(new URL(location = address.getLocationURI()));
                                                } else if (element instanceof SOAP12Address) {
                                                        SOAP12Address address = (SOAP12Address) element;
                                                        location = urlLocalizer.rewrite(new URL(address.getLocationURI()));
                                                }
                                                if (location != null) {
                                                        try {
                                                                URL locationURI = new URL(location);
                                                                AccessPoint accessPoint = new AccessPoint();
                                                                accessPoint.setUseType(AccessPointType.END_POINT.toString());
                                                                accessPoint.setValue(urlLocalizer.rewrite(locationURI));
                                                                bindingTemplate.setAccessPoint(accessPoint);
                                                                // Set Binding Key
                                                                String bindingKey = UDDIKeyConvention.getBindingKey(properties, serviceQName, portName, locationURI);
                                                                bindingTemplate.setBindingKey(bindingKey);
                                                                break;
                                                        } catch (MalformedURLException e) {
                                                                log.error(e.getMessage());
                                                        }
                                                }
                                        }

                                }
                                Binding binding = port.getBinding();
                                // Set the Binding Description
                                String bindingDescription = properties.getProperty(Property.BINDING_DESCRIPTION, Property.DEFAULT_BINDING_DESCRIPTION);
                                // Override with the service description from the WSDL if present
                                Element docElement = binding.getDocumentationElement();
                                if (docElement != null && docElement.getTextContent() != null) {
                                        bindingDescription = docElement.getTextContent();
                                }

                                bindingTemplate.getDescription().addAll(Common2UDDI.mapDescription(bindingDescription, lang));

                                // reference wsdl:binding tModel
                                TModelInstanceInfo tModelInstanceInfoBinding = new TModelInstanceInfo();
                                tModelInstanceInfoBinding.setTModelKey(keyDomainURI + binding.getQName().getLocalPart());
                                InstanceDetails instanceDetails = new InstanceDetails();
                                instanceDetails.setInstanceParms(portName);
                                tModelInstanceInfoBinding.setInstanceDetails(instanceDetails);

                                tModelInstanceInfoBinding.getDescription().addAll(Common2UDDI.mapDescription("The wsdl:binding that this wsdl:port implements. " + bindingDescription
                                        + " The instanceParms specifies the port local name.", lang));
                                tModelInstanceDetails.getTModelInstanceInfo().add(tModelInstanceInfoBinding);

                                // reference wsdl:portType tModel
                                PortType portType = binding.getPortType();
                                TModelInstanceInfo tModelInstanceInfoPortType = new TModelInstanceInfo();
                                tModelInstanceInfoPortType.setTModelKey(keyDomainURI + portType.getQName().getLocalPart());
                                String portTypeDescription = "";
                                docElement = portType.getDocumentationElement();
                                if (docElement != null && docElement.getTextContent() != null) {
                                        portTypeDescription = docElement.getTextContent();
                                }

                                tModelInstanceInfoPortType.getDescription().addAll(Common2UDDI.mapDescription("The wsdl:portType that this wsdl:port implements." + portTypeDescription, lang));
                                tModelInstanceDetails.getTModelInstanceInfo().add(tModelInstanceInfoPortType);

                                bindingTemplate.setTModelInstanceDetails(tModelInstanceDetails);
                        } else {
                                log.error("Could not find Port with portName: " + portName);
                        }
                } else {
                        log.error("Could not find Service with serviceName: " + serviceQName.getLocalPart());
                }

                return UDDIClient.addSOAPtModels(bindingTemplate);
        }

        /**
         * Obtains the accessUrl from the WSDL
         *
         * @param port
         * @return a string url
         * @throws MalformedURLException
         */
        private String getBindingURL(Port port) throws MalformedURLException {

                String bindingUrl = null;
                for (Object element : port.getExtensibilityElements()) {
                        if (element instanceof SOAPAddress) {
                                SOAPAddress address = (SOAPAddress) element;
                                URL locationURI = new URL(address.getLocationURI());
                                if (locationURI != null) {
                                        bindingUrl = urlLocalizer.rewrite(locationURI);
                                        break;
                                }
                        }
                }
                return bindingUrl;
        }
}