/* * JBoss, Home of Professional Open Source * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated * by the @authors tag. All rights reserved. * See the copyright.txt in the distribution for a * full listing of individual contributors. * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU Lesser General Public License, v. 2.1. * This program is distributed in the hope that it will be useful, but WITHOUT A * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public License, * v.2.1 along with this distribution; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ package org.savara.wsdl.internal.generator; import java.util.Collections; import java.util.Comparator; import java.util.logging.Level; import java.util.logging.Logger; import javax.wsdl.Part; import javax.xml.namespace.QName; import org.savara.protocol.model.util.TypeSystem; import org.savara.common.logging.FeedbackHandler; import org.savara.common.logging.MessageFormatter; import org.savara.common.model.annotation.AnnotationDefinitions; import org.savara.contract.model.FaultDetails; import org.savara.contract.model.Interface; import org.savara.contract.model.MessageExchangePattern; import org.savara.contract.model.Namespace; import org.savara.contract.model.RequestResponseMEP; import org.savara.contract.model.Type; import org.savara.contract.model.TypeDefinition; import org.savara.wsdl.generator.WSDLBinding; import org.savara.wsdl.generator.WSDLGenerator; import org.savara.wsdl.util.WSDLGeneratorUtil; /** * This class generates a WSDL definition from a Contract model. * */ public class WSDLGeneratorImpl implements WSDLGenerator { public static final String BINDING_SUFFIX = "Binding"; private static Logger logger = Logger.getLogger(WSDLGeneratorImpl.class.getName()); public WSDLGeneratorImpl() { } /** * This method generates a WSDL definition from a Scribble contract model. * * @param contract The contract model * @param wsdlBinding The WSDL binding to use, or null if no binding * @param journal The journal * @return The WSDL definition */ public java.util.List<javax.wsdl.Definition> generate(org.savara.contract.model.Contract contract, WSDLBinding wsdlBinding, FeedbackHandler handler) { java.util.List<javax.wsdl.Definition> ret=new java.util.Vector<javax.wsdl.Definition>(); try { // Create definition for contract's target namespace javax.wsdl.Definition main=getDefinition(ret, contract, contract.getNamespace(), wsdlBinding, handler); // If no definition, then return if (main == null) { return(ret); } // Create service javax.wsdl.Service service=main.createService(); service.setQName(new javax.xml.namespace.QName(contract.getNamespace(), contract.getName()+"Service")); main.addService(service); // Define a port type per interface java.util.Iterator<Interface> iter=contract.getInterfaces().iterator(); while (iter.hasNext()) { Interface intf=iter.next(); javax.wsdl.PortType portType= createPortType(ret, contract, intf, wsdlBinding, handler); javax.wsdl.Binding binding= createBinding(ret, contract, intf, portType, wsdlBinding, handler); // Create service port for interface javax.wsdl.Port port=main.createPort(); port.setName(intf.getName()+"Port"); port.setBinding(binding); service.addPort(port); if (wsdlBinding != null) { wsdlBinding.updatePort(main, port); } } } catch(Exception e) { logger.log(Level.SEVERE, "Failed to generate WSDL", e); } return(ret); } /** * This method returns the definition associated with the supplied target namespace. * * @param wsdls The list of current WSDL definitions * @param contract The contract * @param targetNamespace The target namespace * @param wsdlBinding The WSDL binding to use, or null if no binding * @param handler The feedback handler * @return The WSDL definition for the target namespace, or null if unable to find or create */ protected javax.wsdl.Definition getDefinition(java.util.List<javax.wsdl.Definition> wsdls, org.savara.contract.model.Contract contract, String targetNamespace, WSDLBinding wsdlBinding, FeedbackHandler handler) { javax.wsdl.Definition ret=null; if (targetNamespace != null) { for (int i=0; ret == null && i < wsdls.size(); i++) { ret = wsdls.get(i); if (ret.getTargetNamespace() == null || ret.getTargetNamespace().equals(targetNamespace) == false) { ret = null; } } if (ret == null) { ret = createDefinition(contract, targetNamespace); if (ret != null) { // Initialize definition using the WSDL binding if (wsdlBinding != null) { wsdlBinding.initDefinition(ret); } wsdls.add(ret); } } } else { handler.error(MessageFormatter.format(java.util.PropertyResourceBundle.getBundle( "org.savara.wsdl.Messages"), "SAVARA-WSDL-00002"), null); } return(ret); } /** * This method creates a new WSDL definition associated with the supplied * target namespace. * * @param contract The contract * @param targetNamespace The target namespace * @return The WSDL definition */ protected javax.wsdl.Definition createDefinition(org.savara.contract.model.Contract contract, String targetNamespace) { javax.wsdl.Definition ret=null; try { javax.wsdl.factory.WSDLFactory fact= javax.wsdl.factory.WSDLFactory.newInstance(); ret = fact.newDefinition(); // Set details on the definition if (contract.getName() != null) { ret.setQName(new javax.xml.namespace.QName(contract.getName())); } ret.setTargetNamespace(targetNamespace); //ret.addNamespace("tns", targetNamespace); // Set up namespace mappings java.util.List<Namespace> nss=new java.util.Vector<Namespace>(contract.getNamespaces()); Collections.sort(nss, new Comparator<Namespace>() { public int compare(Namespace o1, Namespace o2) { return o2.getPrefix().compareTo(o1.getPrefix()); } }); for (Namespace ns : nss) { ret.addNamespace(ns.getPrefix(), ns.getURI()); } } catch(Exception e) { logger.log(Level.SEVERE, "Failed to create WSDL definition for target namespace '"+targetNamespace+"'", e); } return(ret); } /** * This method generates a port type, using the supplied WSDL definition, * based on the information in the supplied interface definition. * * @param wsdls The list of current WSDL definitions * @param contract The contract * @param intf The interface model * @return The WSDL port type */ public javax.wsdl.PortType createPortType(java.util.List<javax.wsdl.Definition> wsdls, org.savara.contract.model.Contract contract, org.savara.contract.model.Interface intf, WSDLBinding wsdlBinding, FeedbackHandler handler) { javax.wsdl.PortType ret=null; if (intf != null) { javax.wsdl.Definition defn=getDefinition(wsdls, contract, intf.getNamespace(), wsdlBinding, handler); if (defn != null) { ret = defn.createPortType(); ret.setUndefined(false); if (intf.getName() != null) { ret.setQName(new javax.xml.namespace.QName(intf.getNamespace(), intf.getName())); } for (int i=0; i < intf.getMessageExchangePatterns().size(); i++) { // Only create operations for meps with type parameters MessageExchangePattern mep=intf.getMessageExchangePatterns().get(i); if (mep.getTypes().size() > 0) { createOperation(wsdls, contract, ret, mep, wsdlBinding, handler); } } // Only add portType to definition if they have atleast one operation if (ret != null && ret.getOperations().size() > 0) { defn.addPortType(ret); } } } return(ret); } /** * This method generates a port type binding, using the supplied WSDL definition, * based on the information in the supplied interface definition. * * @param wsdls The list of current WSDL definitions * @param contract The contract * @param intf The interface model * @param portType The port type * @return The WSDL port type binding */ public javax.wsdl.Binding createBinding(java.util.List<javax.wsdl.Definition> wsdls, org.savara.contract.model.Contract contract, org.savara.contract.model.Interface intf, javax.wsdl.PortType portType, WSDLBinding wsdlBinding, FeedbackHandler handler) { javax.wsdl.Binding ret=null; if (intf != null) { javax.wsdl.Definition defn=getDefinition(wsdls, contract, intf.getNamespace(), wsdlBinding, handler); if (defn != null) { ret = defn.createBinding(); ret.setUndefined(false); // Check if WSDL binding details if (wsdlBinding != null) { wsdlBinding.updateBinding(defn, ret); } if (intf.getName() != null) { ret.setQName(new javax.xml.namespace.QName(intf.getNamespace(), intf.getName()+BINDING_SUFFIX)); } ret.setPortType(portType); for (int i=0; i < intf.getMessageExchangePatterns().size(); i++) { // Only create operations for meps with type parameters MessageExchangePattern mep=intf.getMessageExchangePatterns().get(i); if (mep.getTypes().size() > 0) { createBindingOperation(wsdls, contract, ret, mep, wsdlBinding, handler); } } // Only add portType to definition if they have atleast one operation if (ret != null && ret.getBindingOperations().size() > 0) { defn.addBinding(ret); } } } return(ret); } /** * This method generates an operation, using the supplied WSDL definition, * based on the information in the supplied message exchange pattern. * * @param wsdls The list of current WSDL definitions * @param contract The contract * @param portType The port type * @param mep The message exchange pattern * @return The WSDL operation */ public javax.wsdl.Operation createOperation(java.util.List<javax.wsdl.Definition> wsdls, org.savara.contract.model.Contract contract, javax.wsdl.PortType portType, org.savara.contract.model.MessageExchangePattern mep, WSDLBinding wsdlBinding, FeedbackHandler handler) { javax.wsdl.Operation ret=null; javax.wsdl.Definition defn=null; if (portType != null) { defn = getDefinition(wsdls, contract, portType.getQName().getNamespaceURI(), wsdlBinding, handler); } if (defn != null && mep != null) { ret = defn.createOperation(); ret.setUndefined(false); ret.setName(mep.getOperation()); QName msgname=WSDLGeneratorUtil.getRequestMessageType(portType.getQName().getNamespaceURI(), mep.getOperation(), null); javax.wsdl.Message mesg=getMessage(wsdls, contract, msgname, mep.getTypes(), wsdlBinding, handler); if (mesg != null) { javax.wsdl.Input input=defn.createInput(); input.setMessage(mesg); ret.setInput(input); } else { // TODO: Maybe report missing request message type here? } // Check if a request/response MEP if (mep instanceof RequestResponseMEP) { RequestResponseMEP rr=(RequestResponseMEP)mep; msgname=WSDLGeneratorUtil.getResponseMessageType(portType.getQName().getNamespaceURI(), mep.getOperation(), null); javax.wsdl.Message om=getMessage(wsdls, contract, msgname, rr.getResponseTypes(), wsdlBinding, handler); if (om != null) { javax.wsdl.Output output=defn.createOutput(); output.setMessage(om); ret.setOutput(output); } // Generate fault details if (rr.getFaultDetails() != null) { for (int i=0; i < rr.getFaultDetails().size(); i++) { FaultDetails fd=rr.getFaultDetails().get(i); msgname = WSDLGeneratorUtil.getFaultMessageType(portType.getQName().getNamespaceURI(), fd.getName(), null); javax.wsdl.Message fm=getMessage(wsdls, contract, msgname, fd.getTypes(), wsdlBinding, handler); if (fm != null) { javax.wsdl.Fault fault=defn.createFault(); fault.setName(fd.getName()); fault.setMessage(fm); ret.addFault(fault); } } } } portType.addOperation(ret); } return(ret); } /** * This method generates a binding operation, using the supplied WSDL definition, * based on the information in the supplied message exchange pattern. * * @param wsdls The list of current WSDL definitions * @param contract The contract * @param binding The port type binding * @param mep The message exchange pattern * @return The WSDL binding operation */ public javax.wsdl.BindingOperation createBindingOperation(java.util.List<javax.wsdl.Definition> wsdls, org.savara.contract.model.Contract contract, javax.wsdl.Binding binding, org.savara.contract.model.MessageExchangePattern mep, WSDLBinding wsdlBinding, FeedbackHandler handler) { javax.wsdl.BindingOperation ret=null; javax.wsdl.Definition defn=null; if (binding != null) { defn = getDefinition(wsdls, contract, binding.getQName().getNamespaceURI(), wsdlBinding, handler); } if (defn != null && mep != null) { ret = defn.createBindingOperation(); //ret.setUndefined(false); // Check if WSDL operation details if (wsdlBinding != null) { wsdlBinding.updateOperation(defn, mep, ret); } ret.setName(mep.getOperation()); QName msgname=WSDLGeneratorUtil.getRequestMessageType(binding.getQName().getNamespaceURI(), mep.getOperation(), null); javax.wsdl.Message mesg=getMessage(wsdls, contract,msgname, mep.getTypes(), wsdlBinding, handler); if (mesg != null) { javax.wsdl.BindingInput input=defn.createBindingInput(); // Check if WSDL operation details if (wsdlBinding != null) { wsdlBinding.updateInput(defn, input); } ret.setBindingInput(input); } // Check if a request/response MEP if (mep instanceof RequestResponseMEP) { RequestResponseMEP rr=(RequestResponseMEP)mep; msgname=WSDLGeneratorUtil.getResponseMessageType(binding.getQName().getNamespaceURI(), mep.getOperation(), null); javax.wsdl.Message om=getMessage(wsdls, contract, msgname, rr.getResponseTypes(), wsdlBinding, handler); if (om != null) { javax.wsdl.BindingOutput output=defn.createBindingOutput(); // Check if WSDL operation details if (wsdlBinding != null) { wsdlBinding.updateOutput(defn, output); } ret.setBindingOutput(output); } // Generate fault details if (rr.getFaultDetails() != null) { for (int i=0; i < rr.getFaultDetails().size(); i++) { FaultDetails fd=rr.getFaultDetails().get(i); msgname = WSDLGeneratorUtil.getFaultMessageType(binding.getQName().getNamespaceURI(), fd.getName(), null); javax.wsdl.Message fm=getMessage(wsdls, contract, msgname, fd.getTypes(), wsdlBinding, handler); if (fm != null) { javax.wsdl.BindingFault fault=defn.createBindingFault(); fault.setName(fd.getName()); // Check if WSDL operation details if (wsdlBinding != null) { wsdlBinding.updateFault(defn, fault); } ret.addBindingFault(fault); } } } } binding.addBindingOperation(ret); } return(ret); } /** * This method returns a message, using the supplied WSDL definition, * based on the information supplied in the list of type references. If * a single type reference is supplied, this will be the message type, * if multiple type references are supplied, then these will be considered * the message parts.<p> * <p> * The returned message will be part of the supplied definition. If it * does not exist prior to the call to this method, then it will be * created and added to the definition upon returning the message. * * @param wsdls The list of current WSDL definitions * @param contract The contract * @param msgname The message name * @param types The list of type references * @return The WSDL message */ public javax.wsdl.Message getMessage(java.util.List<javax.wsdl.Definition> wsdls, org.savara.contract.model.Contract contract, javax.xml.namespace.QName msgname, java.util.List<org.savara.contract.model.Type> types, WSDLBinding wsdlBinding, FeedbackHandler handler) { javax.wsdl.Message ret=null; if (types != null && types.size() > 0) { if (types.size() > 1) { handler.error("Currently only supports single type reference", null); } else { Type ref=types.get(0); TypeDefinition td=contract.getTypeDefinition(ref.getName()); if (td != null && TypeSystem.XSD.equals(td.getTypeSystem())) { javax.xml.namespace.QName qname= javax.xml.namespace.QName.valueOf(td.getDataType()); javax.wsdl.Definition defn= getDefinition(wsdls, contract, msgname.getNamespaceURI(), wsdlBinding, handler); if (defn != null && qname != null && (ret = defn.getMessage(msgname)) == null) { ret = defn.createMessage(); ret.setUndefined(false); ret.setQName(msgname); Part part=createPart(defn, td, qname, wsdlBinding, handler); ret.addPart(part); defn.addMessage(ret); } } } } return(ret); } protected Part createPart(javax.wsdl.Definition defn, TypeDefinition td, javax.xml.namespace.QName qname, WSDLBinding wsdlBinding, FeedbackHandler handler) { // Create single part for type or element Part part=defn.createPart(); part.setName("content"); if (AnnotationDefinitions.getAnnotation(td.getAnnotations(), AnnotationDefinitions.XSD_ELEMENT) != null) { part.setElementName(qname); } else if (AnnotationDefinitions.getAnnotation(td.getAnnotations(), AnnotationDefinitions.XSD_TYPE) != null) { part.setTypeName(qname); if (!wsdlBinding.isXSDTypeMessagePartSupported()) { // Raise error handler.error(MessageFormatter.format(java.util.PropertyResourceBundle.getBundle( "org.savara.wsdl.Messages"), "SAVARA-WSDL-00001", wsdlBinding.getName(), qname.toString()), null); } } else { // No annotation, so use the appropriate attribute for the binding type if (wsdlBinding.isXSDTypeMessagePartSupported()) { part.setTypeName(qname); } else { part.setElementName(qname); } } return(part); } }