All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.pustefixframework.maven.plugins.Wsdl2Js Maven / Gradle / Ivy

The newest version!
/*
 * This file is part of Pustefix.
 *
 * Pustefix is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Pustefix is distributed in the hope that it will be useful,
 * but WITHOUT ANY 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
 * along with Pustefix; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package org.pustefixframework.maven.plugins;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.wsdl.Binding;
import javax.wsdl.BindingOperation;
import javax.wsdl.Definition;
import javax.wsdl.Input;
import javax.wsdl.Message;
import javax.wsdl.Operation;
import javax.wsdl.Output;
import javax.wsdl.Part;
import javax.wsdl.Port;
import javax.wsdl.Service;
import javax.wsdl.Types;
import javax.wsdl.extensions.ExtensibilityElement;
import javax.wsdl.extensions.schema.Schema;
import javax.wsdl.extensions.schema.SchemaImport;
import javax.wsdl.factory.WSDLFactory;
import javax.wsdl.xml.WSDLReader;
import javax.xml.namespace.QName;

import org.pustefixframework.webservices.Constants;
import org.pustefixframework.webservices.jsgen.JsBlock;
import org.pustefixframework.webservices.jsgen.JsClass;
import org.pustefixframework.webservices.jsgen.JsMethod;
import org.pustefixframework.webservices.jsgen.JsParam;
import org.pustefixframework.webservices.jsgen.JsStatement;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import com.ibm.wsdl.util.xml.DOMUtils;

/**
 * This class generates a Javascript stub file from a WSDL description.
 *
 * @author [email protected]
 */
public class Wsdl2Js {

    private final static Pattern PREFIXPATTERN=Pattern.compile("(\\w+):(\\w+)");
    private final static String XMLNS_SCHEMA="http://www.w3.org/2001/XMLSchema";
    private final static String XMLNS_JAXB_ARRAY="http://jaxb.dev.java.net/array";
    
    private File outputFile;
    private File inputFile;
    
    private Map schemaComplexTypes;
    private Map schemaElementsToTypes;
    
    private HashMap typeInfoMap;
    private ArrayList typeInfoList;
    private ArrayList popInfoList;
    
    /**
     * Set WSDL input file
     */
    public void setInputFile(File inputFile) {
        this.inputFile=inputFile;
    }
    
    /**
     * Set Javascript output file
     */
    public void setOutputFile(File outputFile) {
        this.outputFile=outputFile;
    }
    
    /**
     * Generate Javascript stub file from WSDL file 
     */
    public void generate() throws Wsdl2JsException, IOException {
        if(inputFile==null) throw new Wsdl2JsException("No WSDL input file specified");
        if(outputFile==null) throw new Wsdl2JsException("No JS output file specified");
        if(!inputFile.exists()) throw new Wsdl2JsException("WSDL input file doesn't exist");
        Definition def=null;
        try {
            WSDLFactory wf=WSDLFactory.newInstance();
            WSDLReader wr=wf.newWSDLReader();
            wr.setFeature("javax.wsdl.verbose",false);
            wr.setFeature("javax.wsdl.importDocuments",true);
            InputSource inSrc=new InputSource(new FileInputStream(inputFile));
            def=wr.readWSDL(inputFile.getParentFile().toURI().toString(),inSrc);
        } catch(Exception x) {
            throw new Wsdl2JsException("Error reading WSDL from: "+inputFile.getAbsolutePath(),x);
        }      
        typeInfoMap=new HashMap();
        popInfoList=new ArrayList();
        typeInfoList=new ArrayList();    
        readSchema(def.getTypes());    
        Iterator srvIt=def.getServices().values().iterator();
        if(srvIt.hasNext()) {
            Service service=(Service)srvIt.next();
            Iterator prtIt=service.getPorts().values().iterator();
            if(prtIt.hasNext()) {  
                Port port=(Port)prtIt.next();   
                String stubClass=Constants.STUBGEN_DEFAULT_JSNAMESPACE+service.getQName().getLocalPart();
                JsParam[] constParams=new JsParam[] {new JsParam("cbObj")};
                JsClass jsClass=new JsClass(stubClass,"SOAP_Stub",constParams);
                JsBlock block=jsClass.getConstructorBody();
                block.addStatement(new JsStatement("this._cbObj=cbObj"));
                block.addStatement(new JsStatement("this._setService(\""+service.getQName().getLocalPart()+"\")"));
                block.addStatement(new JsStatement("this._setRequestPath(\"###REQUESTPATH###\")"));
                Binding binding=port.getBinding();
                Iterator bopIt=binding.getBindingOperations().iterator();
                while(bopIt.hasNext()) {
                    BindingOperation bop=(BindingOperation)bopIt.next();
                    Operation op=bop.getOperation();
                    try {
                        JsMethod jsMethod=new JsMethod(jsClass,op.getName());
                        JsBlock jsBlock=jsMethod.getBody();
                        jsBlock.addStatement(new JsStatement("var call=this._createCall()"));
                        jsBlock.addStatement(new JsStatement("call.setTargetNamespace(this._targetNamespace)"));
                        jsBlock.addStatement(new JsStatement("call.setOperationName(\""+jsMethod.getName()+"\")"));
                        //get input params
                        Input input=op.getInput();
                        Message inputMsg=input.getMessage();
                        Part inputPart=inputMsg.getPart("parameters");
                        if(inputPart!=null) {
                            QName typeName=schemaElementsToTypes.get(inputPart.getElementName());
                            Element typeElem=schemaComplexTypes.get(typeName);
                            Element[] children=getSchemaChildren(typeElem);
                            if(children.length>1 || (children.length==1 && !children[0].getLocalName().equals("sequence"))) 
                                throw new Wsdl2JsException("Expected only single sequence child element for complexType: "+typeName);
                            children=getSchemaChildren(children[0]);
                            for(int paraNo=0;paraNo1 || (children.length==1 && !children[0].getLocalName().equals("sequence"))) 
                                throw new Wsdl2JsException("Expected only single sequence child element for complexType: "+typeName);
                            children=getSchemaChildren(children[0]);
                            if(children.length>1) throw new Wsdl2JsException("Expected only one or none return parameters");
                            if(children.length==1) {
                                String info=createTypeInfo(children[0]);
                                jsBlock.addStatement(new JsStatement("call.setReturnType("+info+")"));
                            }
                        }
                        jsBlock.addStatement(new JsStatement("return call.invoke("+jsMethod.getParamList()+")"));
                        jsClass.addMethod(jsMethod);
                    } catch(Wsdl2JsTypeException x) {
                        System.out.println("WARNING: Skip method '"+op.getName()+"' as signature "+
                                "contains unsupported type '"+x.getTypeName()+"'.");
                    }
                }
                block.addStatement(new JsStatement("this._targetNamespace=\""+def.getTargetNamespace()+"\""));
                for(int i=0;i"+type.getNamespaceURI());
            }
            typeInfoList.set(ind,info);
        }
        return ret; 
    }

    /**
     * Create QName instance JS code (using JS constant for schema namespace)
     */
    private String createJsQName(QName name) {
        String nsuri="";
        if(name.getNamespaceURI().equals(Constants.XMLNS_XSD)) nsuri="XML_NS_XSD";
        else nsuri="\""+name.getNamespaceURI()+"\"";
        return "new XML_QName("+nsuri+",\""+name.getLocalPart()+"\")";
    }
  
    /**
     * Stores top-level schema elements and complexTypes in maps (for all imported schemas)
     */
    private void readSchema(Types wsdlTypes) throws Wsdl2JsException {
        schemaComplexTypes = new HashMap();
        schemaElementsToTypes = new HashMap();
        if (wsdlTypes != null) {
            List schemaList = wsdlTypes.getExtensibilityElements();
            Iterator schemaIt = schemaList.iterator();
            while (schemaIt.hasNext()) {
                ExtensibilityElement extElem = (ExtensibilityElement) schemaIt.next();
                if (extElem instanceof Schema) {
                    Schema schema = (Schema) extElem;
                    readSchema(schema);
                }
            }
        }
    }
    
    /**
     * Stores top-level schema elements and complexTypes in maps (for a single schema)
     */
    private void readSchema(Schema schema) throws Wsdl2JsException {
        // Process imported schemas
        Map imports = schema.getImports();
        Iterator impIt = imports.keySet().iterator();
        while (impIt.hasNext()) {
            String targetNS = (String) impIt.next();
            List list = (List) imports.get(targetNS);
            Iterator listIt = list.iterator();
            while (listIt.hasNext()) {
                Object obj = listIt.next();
                if (obj instanceof SchemaImport) {
                    SchemaImport schemaImp = (SchemaImport) obj;
                    Schema refSchema = schemaImp.getReferencedSchema();
                    readSchema(refSchema);
                }
            }
        }
        // Process schema type definitions
        NodeList nodes = schema.getElement().getChildNodes();
        for (int i = 0; i < nodes.getLength(); i++) {
            Node node = nodes.item(i);
            if (node.getNodeType() == Node.ELEMENT_NODE && node.getNamespaceURI().equals(Constants.XMLNS_XSD)) {
                if (node.getLocalName().equals("complexType")) {
                    Element typeElem = (Element) node;
                    String name = typeElem.getAttribute("name");
                    String targetNS = schema.getElement().getAttribute("targetNamespace");
                    QName qname = new QName(targetNS, name);
                    schemaComplexTypes.put(qname, typeElem);
                } else if (node.getLocalName().equals("element")) {
                    Element elemElem = (Element) node;
                    String name = elemElem.getAttribute("name");
                    String targetNS = schema.getElement().getAttribute("targetNamespace");
                    String type = elemElem.getAttribute("type");
                    Matcher matcher = PREFIXPATTERN.matcher(type);
                    if (!matcher.matches()) throw new Wsdl2JsException("Element type value isn't prefixed: " + type);
                    String prefix = matcher.group(1);
                    String localName = matcher.group(2);
                    String nsuri = DOMUtils.getNamespaceURIFromPrefix(elemElem, prefix);
                    if (nsuri == null) throw new Wsdl2JsException("No namespace declaration found for prefix: " + prefix);
                    QName elemQName = new QName(targetNS, name);
                    QName typeQName = new QName(nsuri, localName);
                    schemaElementsToTypes.put(elemQName, typeQName);
                }
            }
        }
    }
    
    /**
     * Check if complexType declaration represents an array
     */
    private boolean isComplexTypeArray(Element complexType) {
        if(!(complexType.getNamespaceURI().equals(XMLNS_SCHEMA) && complexType.getLocalName().equals("complexType")))
            throw new IllegalArgumentException("Expected complex type declaration element as argument");
        Element[] elems=getSchemaChildren(complexType);
        if(elems.length==1 && elems[0].getLocalName().equals("sequence")) {
            elems=getSchemaChildren(elems[0]);
            if(elems.length==1 && elems[0].getLocalName().equals("element")) {
                String min=elems[0].getAttribute("minOccurs");
                String max=elems[0].getAttribute("maxOccurs");
                if(min!=null&&max!=null&&min.equals("0")&&max.equals("unbounded")) return true;
            }
        }    
    	return false;
    }
    
    /**
     * Check if element declaration represents an array
     */
    private boolean isElementArray(Element element) {
        if(!(element.getNamespaceURI().equals(XMLNS_SCHEMA) && element.getLocalName().equals("element")))
            throw new IllegalArgumentException("Expected element declaration as argument");
        String min=element.getAttribute("minOccurs");
        String max=element.getAttribute("maxOccurs");
        if(min!=null && max!=null && min.equals("0") && max.equals("unbounded")) return true;
        return false;
    }
    
    /**
     * Extract array information from complexType
     */
    private ArrayInfo getArrayInfoFromComplexType(Element complexType) throws Wsdl2JsException {
        if(!(complexType.getNamespaceURI().equals(XMLNS_SCHEMA) && complexType.getLocalName().equals("complexType"))) 
            throw new IllegalArgumentException("Expected complex type declaration element as argument");
        Element[] elems=getSchemaChildren(complexType);
        if(elems.length==1 && elems[0].getLocalName().equals("sequence")) {
            elems=getSchemaChildren(elems[0]);
            if(elems.length==1 && elems[0].getLocalName().equals("element")) {
                if(isElementArray(elems[0])) {
                    return getArrayInfoFromElement(elems[0]);  
                }
            }
        }
        throw new IllegalArgumentException("Expected complex type declaration representing an array");
    }
    
    /**
     * Extract array information from element
     */
    private ArrayInfo getArrayInfoFromElement(Element element) throws Wsdl2JsException {
        if(!(element.getNamespaceURI().equals(XMLNS_SCHEMA) && element.getLocalName().equals("element")))
            throw new IllegalArgumentException("Expected element declaration as argument");
        if(!isElementArray(element)) 
            throw new IllegalArgumentException("Expected element declaration representing an array");
        String compType=element.getAttribute("type");
        Matcher mat=PREFIXPATTERN.matcher(compType);
        if(!mat.matches()) throw new Wsdl2JsException("Expected prefixed element array component type: "+compType);
        String compPrefix=mat.group(1);
        String compLocalName=mat.group(2);
        String compNSUri=DOMUtils.getNamespaceURIFromPrefix(element,compPrefix);
        QName compQName=new QName(compNSUri,compLocalName);
        Element complexType=(Element)schemaComplexTypes.get(compQName);
        if(complexType!=null) {
            if(isComplexTypeArray(complexType)) {
                ArrayInfo compInfo=getArrayInfoFromComplexType(complexType);
                ArrayInfo info=new ArrayInfo(compInfo.arrayType,compInfo.arrayDim+1);
                return info;
            } else return new ArrayInfo(compQName,1);
        } else return new ArrayInfo(compQName,1);           
    }
    
    /**
     * Create type description JS code for a bean using its complexType definition
     */
    private String getMemberInfos(Element complexType) throws Wsdl2JsException {
        if(!(complexType.getNamespaceURI().equals(XMLNS_SCHEMA) && complexType.getLocalName().equals("complexType")))
            throw new IllegalArgumentException("Expected complex type declaration element as argument: "+getXMLString(complexType));
            Element modelElem=getFirstSchemaChild(complexType);
            if(modelElem!=null) {
            	if(modelElem.getLocalName().equals("sequence")) {
                    return getSequenceMemberInfos(modelElem);
                } else if(modelElem.getLocalName().equals("complexContent")) {
                    Element extElem=getFirstSchemaChild(modelElem);
                    if(extElem!=null&&extElem.getLocalName().equals("extension")) {
                    	String base=extElem.getAttribute("base");     
                    	QName qname=getQName(base,extElem);
                    	Element baseElem=(Element)schemaComplexTypes.get(qname);
                    	String baseInfos=getMemberInfos(baseElem);
                    	Element seqElem=getFirstSchemaChild(extElem);
                    	if(seqElem!=null&&seqElem.getLocalName().equals("sequence")) {
                    	    String infos=getSequenceMemberInfos(seqElem);
                    	    if(infos.length()>0 && baseInfos.length()>0) {
                    	        infos=infos+","+baseInfos;
                    	    } else {
                    	        infos=infos+baseInfos;
                    	    }
                    	    return infos;
                    	} else throw new Wsdl2JsException("Illegal 'extension' content: "+complexType.getAttribute("name"));
                    } else throw new Wsdl2JsException("Illegal 'complexContent': "+complexType.getAttribute("name"));
                } else throw new Wsdl2JsException("Illegal content model: "+complexType.getAttribute("name"));
            } else throw new Wsdl2JsException("XML Schema child expected below complex type: "+getXMLString(complexType));
    }
    
    /**
     * Create type description JS code for a bean using its sequence definition
     */
    private String getSequenceMemberInfos(Element sequence) throws Wsdl2JsException {
        if(!(sequence.getNamespaceURI().equals(XMLNS_SCHEMA) && sequence.getLocalName().equals("sequence")))
            throw new IllegalArgumentException("Expected sequence declaration element as argument");
        String memberList="";
        Element[] elems=getSchemaChildren(sequence);
        for(int i=0;i al=new ArrayList();
        NodeList nl=parent.getChildNodes();
        for(int i=0;i");
            NodeList nodes=element.getChildNodes();
            for(int i=0;i");
        } else {
            sb.append("/>");
        }
    }
    
    
    /**
     * Helper class for storing information while traversing array schema definitions
     */
    class ArrayInfo {
     
        QName arrayType;
        int arrayDim;
        
        ArrayInfo(QName arrayType,int arrayDim) {
        	this.arrayType=arrayType;
            this.arrayDim=arrayDim;
        }
        
    }
    
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy