/** * ************************************************************************* * Copyright (C) 2006-09-10 by Claudio Guidi and Francesco Bullini * <[email protected]> <[email protected]> * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * This program 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 General Public * License for more details. * * You should have received a copy of the GNU Library General Public * License along with this program; if * not, write to the * Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * For details about the * authors of this software, see the AUTHORS file. * ************************************************************************* */ package joliex.wsdl; import com.ibm.wsdl.PortTypeImpl; import com.ibm.wsdl.ServiceImpl; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; import javax.wsdl.Binding; import javax.wsdl.BindingFault; import javax.wsdl.BindingInput; import javax.wsdl.BindingOperation; import javax.wsdl.BindingOutput; import javax.wsdl.Definition; import javax.wsdl.Fault; import javax.wsdl.Input; import javax.wsdl.Message; import javax.wsdl.Operation; import javax.wsdl.OperationType; import javax.wsdl.Output; import javax.wsdl.Part; import javax.wsdl.Port; import javax.wsdl.PortType; import javax.wsdl.Service; import javax.wsdl.Types; import javax.wsdl.WSDLException; import javax.wsdl.extensions.ExtensionRegistry; import javax.wsdl.extensions.schema.Schema; import javax.wsdl.extensions.soap.SOAPAddress; import javax.wsdl.extensions.soap.SOAPBinding; import javax.wsdl.extensions.soap.SOAPBody; import javax.wsdl.extensions.soap.SOAPFault; import javax.wsdl.extensions.soap.SOAPOperation; import javax.wsdl.factory.WSDLFactory; import javax.wsdl.xml.WSDLWriter; import javax.xml.namespace.QName; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import jolie.lang.NativeType; import jolie.lang.parse.ast.InputPortInfo; import jolie.lang.parse.ast.InterfaceDefinition; import jolie.lang.parse.ast.OneWayOperationDeclaration; import jolie.lang.parse.ast.OperationDeclaration; import jolie.lang.parse.ast.OutputPortInfo; import jolie.lang.parse.ast.RequestResponseOperationDeclaration; import jolie.lang.parse.ast.types.TypeChoiceDefinition; import jolie.lang.parse.ast.types.TypeDefinition; import jolie.lang.parse.ast.types.TypeDefinitionLink; import jolie.lang.parse.ast.types.TypeInlineDefinition; import jolie.lang.parse.util.ProgramInspector; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * * @author Francesco Bullini and Claudio Guidi */ public class WSDLDocCreator { // Schema private Document schemaDocument; private Element schemaRootElement; private static final int MAX_CARD = Integer.MAX_VALUE; private String tns; private String tnsSchema; private static final String TNS_SCHEMA_PREFIX = "sch"; static ExtensionRegistry extensionRegistry; private static WSDLFactory wsdlFactory; private Definition localDef = null; private final List< String > rootTypes = new ArrayList<>(); private final ProgramInspector inspector; private final URI originalFile; public WSDLDocCreator( ProgramInspector inspector, URI originalFile ) { this.inspector = inspector; this.originalFile = originalFile; } public Definition initWsdl( String serviceName, String filename ) { try { wsdlFactory = WSDLFactory.newInstance(); localDef = wsdlFactory.newDefinition(); extensionRegistry = wsdlFactory.newPopulatedExtensionRegistry(); if( serviceName != null ) { QName servDefQN = new QName( serviceName ); localDef.setQName( servDefQN ); } localDef.addNamespace( NameSpacesEnum.WSDL.getNameSpacePrefix(), NameSpacesEnum.WSDL.getNameSpaceURI() ); localDef.addNamespace( NameSpacesEnum.SOAP.getNameSpacePrefix(), NameSpacesEnum.SOAP.getNameSpaceURI() ); localDef.addNamespace( "tns", tns ); localDef.addNamespace( NameSpacesEnum.XML_SCH.getNameSpacePrefix(), NameSpacesEnum.XML_SCH.getNameSpaceURI() ); localDef.setTargetNamespace( tns ); localDef.addNamespace( "xsd1", tnsSchema ); } catch( WSDLException ex ) { Logger.getLogger( WSDLDocCreator.class.getName() ).log( Level.SEVERE, null, ex ); } return localDef; } public void ConvertDocument( String filename, String tns, String portName, String location ) { System.out.println( "Starting conversion..." ); this.tns = tns + ".wsdl"; this.tnsSchema = tns + ".xsd"; initWsdl( null, filename ); try( Writer w = (filename != null) ? new FileWriter( filename ) : new OutputStreamWriter( System.out ); Writer fw = new BufferedWriter( w ) ) { schemaDocument = this.createDOMdocument(); schemaRootElement = this.createSchemaRootElement( schemaDocument ); // scans inputPorts OutputPortInfo[] outputPorts = inspector.getOutputPorts(); InputPortInfo[] inputPortList = inspector.getInputPorts( originalFile ); for( InputPortInfo inputPort : inputPortList ) { if( inputPort.id().equals( portName ) ) { // portType creation String portTypeName = inputPort.id(); PortType pt = createPortType( localDef, portTypeName ); // binding creation Binding bd = createBindingSOAP( localDef, pt, portTypeName + "SOAPBinding" ); // service creation String address; if( location.isEmpty() ) { if( inputPort.location().toString().equals( "local" ) ) { address = "local"; } else { address = inputPort.location().toString().substring( 6 ); // exclude socket word address = "http" + address; } } else { address = location; } createService( localDef, portTypeName + "Service", bd, address ); // scan aggregated ports for( int x = 0; x < inputPort.aggregationList().length; x++ ) { int i = 0; while( !inputPort.aggregationList()[ x ].outputPortList()[ 0 ] .equals( outputPorts[ i ].id() ) ) { i++; } for( InterfaceDefinition interfaceDefinition : outputPorts[ i ].getInterfaceList() ) { for( Entry< String, OperationDeclaration > entry : interfaceDefinition.operationsMap() .entrySet() ) { addOperation2WSDL( entry.getValue(), pt, bd ); } } } // scans port interfaces for( InterfaceDefinition interfaceDefinition : inputPort.getInterfaceList() ) { // scan operations Map< String, OperationDeclaration > operationMap = interfaceDefinition.operationsMap(); for( Entry< String, OperationDeclaration > entry : operationMap.entrySet() ) { addOperation2WSDL( entry.getValue(), pt, bd ); } } } } setSchemaDocIntoWSDLTypes( schemaDocument ); WSDLWriter wsdl_writer = wsdlFactory.newWSDLWriter(); wsdl_writer.writeWSDL( localDef, fw ); } catch( IOException | WSDLException e ) { System.err.println( e.getMessage() ); Logger.getLogger( WSDLDocCreator.class.getName() ).log( Level.SEVERE, null, e ); } System.out.println( "Success: WSDL document generated!" ); } private void addOperation2WSDL( OperationDeclaration operationDeclaration, PortType pt, Binding bd ) { if( operationDeclaration instanceof OneWayOperationDeclaration ) { // OW // -------------- adding operation OneWayOperationDeclaration oneWayOperation = (OneWayOperationDeclaration) operationDeclaration; Operation wsdlOp = addOWOperation2PT( localDef, pt, oneWayOperation ); // adding operation binding addOperationSOAPBinding( localDef, wsdlOp, bd ); } else { // RR // -------------- adding operation RequestResponseOperationDeclaration requestResponseOperation = (RequestResponseOperationDeclaration) operationDeclaration; Operation wsdlOp = addRROperation2PT( localDef, pt, requestResponseOperation ); // adding operation binding addOperationSOAPBinding( localDef, wsdlOp, bd ); } } private String getSchemaNativeType( NativeType nType ) { /* * TO DO: wsdl_types ANY, RAW, VOID */ String prefix = "xs:"; String suffix = ""; if( nType.equals( NativeType.STRING ) ) { suffix = "string"; } else if( nType.equals( NativeType.DOUBLE ) ) { suffix = "double"; } else if( nType.equals( NativeType.INT ) ) { suffix = "int"; } else if( nType.equals( NativeType.BOOL ) ) { suffix = "boolean"; } if( suffix.isEmpty() ) { return ""; } else { return prefix + suffix; } } private void addRootType( TypeDefinition type ) throws Exception { if( type instanceof TypeDefinitionLink ) { throw (new Exception( "ERROR, type " + type.id() + ":conversion not allowed when the types defined as operation messages are linked type!" )); } if( !rootTypes.contains( type.id() ) ) { schemaRootElement.appendChild( createTypeDefinition( (TypeInlineDefinition) type, false ) ); } rootTypes.add( type.id() ); } private Element createTypeDefinition( TypeInlineDefinition type, boolean inMessage ) throws Exception { if( type.nativeType() != NativeType.VOID ) { throw (new Exception( "ERROR, type " + type.id() + ": conversion not allowed when the types defined as operation messages have native type different from void!" )); } Element newEl = schemaDocument.createElement( "xs:complexType" ); if( inMessage == false ) { String typename = type.id(); newEl.setAttribute( "name", typename ); } Element sequence = schemaDocument.createElement( "xs:sequence" ); // adding subtypes if( type.hasSubTypes() ) { for( Entry< String, TypeDefinition > stringTypeDefinitionEntry : type.subTypes() ) { TypeDefinition curType = stringTypeDefinitionEntry.getValue(); Element subEl = schemaDocument.createElement( "xs:element" ); subEl.setAttribute( "name", curType.id() ); subEl.setAttribute( "minOccurs", Integer.toString( curType.cardinality().min() ) ); String maxOccurs = "unbounded"; if( curType.cardinality().max() < MAX_CARD ) { maxOccurs = Integer.toString( curType.cardinality().max() ); } subEl.setAttribute( "maxOccurs", maxOccurs ); if( curType instanceof TypeInlineDefinition ) { if( ((TypeInlineDefinition) curType).hasSubTypes() ) { if( ((TypeInlineDefinition) curType).nativeType() != NativeType.VOID ) { throw (new Exception( "ERROR, type " + curType.id() + ": conversion not allowed when the types defined as operation messages have native type different from void!" )); } else { subEl.appendChild( createTypeDefinition( (TypeInlineDefinition) curType, true ) ); } } else { subEl.setAttribute( "type", getSchemaNativeType( ((TypeInlineDefinition) curType).nativeType() ) ); } } else if( curType instanceof TypeDefinitionLink ) { subEl.setAttribute( "type", TNS_SCHEMA_PREFIX + ":" + ((TypeDefinitionLink) curType).linkedTypeName() ); addRootType( ((TypeDefinitionLink) curType).linkedType() ); } sequence.appendChild( subEl ); } } newEl.appendChild( sequence ); return newEl; } private void addMessageType( TypeDefinition rootType, String typename ) throws Exception { // when converting from Jolie type of messages must have root type = "void" // no type link are allowed for conversion // message types define elements if( !rootTypes.contains( rootType.id() ) ) { Element newEl = schemaDocument.createElement( "xs:element" ); newEl.setAttribute( "name", typename ); if( rootType instanceof TypeInlineDefinition ) { newEl.appendChild( createTypeDefinition( (TypeInlineDefinition) rootType, true ) ); rootTypes.add( typename ); schemaRootElement.appendChild( newEl ); if( ((TypeInlineDefinition) rootType).nativeType() != NativeType.VOID ) { throw (new Exception( "ERROR, type " + rootType.id() + ": conversion not allowed when the types defined as operation messages have native type different from void!" )); } } else if( rootType instanceof TypeDefinitionLink ) { throw (new Exception( "ERROR, type " + rootType.id() + ":conversion not allowed when the types defined as operation messages are linked type!" )); // newEl.appendChild( lookForLinkedType( (TypeDefinitionLink ) rootType, typename )); // schemaRootElement.appendChild( createTypeDefinitionLink( ( TypeDefinitionLink ) rootType, true, // typename )); } else if( rootType instanceof TypeChoiceDefinition ) { throw (new Exception( "ERROR, type " + rootType.id() + ":conversion not allowed when the types defined as operation messages are choice types!" )); } } } private Message addRequestMessage( Definition localDef, OperationDeclaration op ) { Message inputMessage = localDef.createMessage(); inputMessage.setUndefined( false ); Part inputPart = localDef.createPart(); inputPart.setName( "body" ); try { // adding wsdl_types related to this message if( op instanceof OneWayOperationDeclaration ) { OneWayOperationDeclaration op_ow = (OneWayOperationDeclaration) op; // set the message name as the name of the jolie request message type inputMessage.setQName( new QName( tns, op_ow.requestType().id() ) ); addMessageType( op_ow.requestType(), op_ow.id() ); } else { RequestResponseOperationDeclaration op_rr = (RequestResponseOperationDeclaration) op; // set the message name as the name of the jolie request message type inputMessage.setQName( new QName( tns, op_rr.requestType().id() ) ); addMessageType( op_rr.requestType(), op_rr.id() ); } // set the input part as the operation name inputPart.setElementName( new QName( tnsSchema, op.id() ) ); inputMessage.addPart( inputPart ); inputMessage.setUndefined( false ); localDef.addMessage( inputMessage ); } catch( Exception e ) { e.printStackTrace(); } return inputMessage; } private Message addResponseMessage( Definition localDef, OperationDeclaration op ) { Message outputMessage = localDef.createMessage(); outputMessage.setUndefined( false ); Part outputPart = localDef.createPart(); outputPart.setName( "body" ); // adding wsdl_types related to this message try { RequestResponseOperationDeclaration op_rr = (RequestResponseOperationDeclaration) op; String outputPartName = op_rr.id() + "Response"; // set the message name as the name of the jolie response message type outputMessage.setQName( new QName( tns, op_rr.responseType().id() ) ); addMessageType( op_rr.responseType(), outputPartName ); outputPart.setElementName( new QName( tnsSchema, outputPartName ) ); outputMessage.addPart( outputPart ); outputMessage.setUndefined( false ); localDef.addMessage( outputMessage ); } catch( Exception e ) { e.printStackTrace(); } return outputMessage; } private Message addFaultMessage( Definition localDef, TypeDefinition tp ) { Message faultMessage = localDef.createMessage(); faultMessage.setUndefined( false ); // set the fault message name as the name of the fault jolie message type faultMessage.setQName( new QName( tns, tp.id() ) ); Part faultPart = localDef.createPart(); faultPart.setName( "body" ); String faultPartName = tp.id(); try { // adding wsdl_types related to this message addMessageType( tp, faultPartName ); faultPart.setElementName( new QName( tnsSchema, faultPartName ) ); faultMessage.addPart( faultPart ); faultMessage.setUndefined( false ); localDef.addMessage( faultMessage ); } catch( Exception e ) { e.printStackTrace(); } return faultMessage; } private PortType createPortType( Definition def, String portTypeName ) { PortType pt = def.getPortType( new QName( portTypeName ) ); if( pt == null ) { pt = new PortTypeImpl(); } pt.setUndefined( false ); QName pt_QN = new QName( tns, portTypeName ); pt.setQName( pt_QN ); def.addPortType( pt ); return pt; } private Operation addOWOperation2PT( Definition def, PortType pt, OneWayOperationDeclaration op ) { Operation wsdlOp = def.createOperation(); wsdlOp.setName( op.id() ); wsdlOp.setStyle( OperationType.ONE_WAY ); wsdlOp.setUndefined( false ); Input in = def.createInput(); Message msg_req = addRequestMessage( localDef, op ); msg_req.setUndefined( false ); in.setMessage( msg_req ); wsdlOp.setInput( in ); wsdlOp.setUndefined( false ); pt.addOperation( wsdlOp ); return wsdlOp; } private Operation addRROperation2PT( Definition def, PortType pt, RequestResponseOperationDeclaration op ) { Operation wsdlOp = def.createOperation(); wsdlOp.setName( op.id() ); wsdlOp.setStyle( OperationType.REQUEST_RESPONSE ); wsdlOp.setUndefined( false ); // creating input Input in = def.createInput(); Message msg_req = addRequestMessage( localDef, op ); in.setMessage( msg_req ); wsdlOp.setInput( in ); // creating output Output out = def.createOutput(); Message msg_resp = addResponseMessage( localDef, op ); out.setMessage( msg_resp ); wsdlOp.setOutput( out ); // creating faults for( Entry< String, TypeDefinition > curFault : op.faults().entrySet() ) { Fault fault = localDef.createFault(); fault.setName( curFault.getKey() ); Message flt_msg = addFaultMessage( localDef, curFault.getValue() ); fault.setMessage( flt_msg ); wsdlOp.addFault( fault ); } pt.addOperation( wsdlOp ); return wsdlOp; } private Binding createBindingSOAP( Definition def, PortType pt, String bindingName ) { Binding bind = def.getBinding( new QName( bindingName ) ); if( bind == null ) { bind = def.createBinding(); bind.setQName( new QName( tns, bindingName ) ); } bind.setPortType( pt ); bind.setUndefined( false ); try { SOAPBinding soapBinding = (SOAPBinding) extensionRegistry.createExtension( Binding.class, new QName( NameSpacesEnum.SOAP.getNameSpaceURI(), "binding" ) ); soapBinding.setTransportURI( NameSpacesEnum.SOAP_OVER_HTTP.getNameSpaceURI() ); soapBinding.setStyle( "document" ); bind.addExtensibilityElement( soapBinding ); } catch( WSDLException ex ) { System.err.println( ex.getMessage() ); } def.addBinding( bind ); return bind; } private void addOperationSOAPBinding( Definition localDef, Operation wsdlOp, Binding bind ) { try { // creating operation binding BindingOperation bindOp = localDef.createBindingOperation(); bindOp.setName( wsdlOp.getName() ); // adding soap extensibility elements SOAPOperation soapOperation = (SOAPOperation) extensionRegistry.createExtension( BindingOperation.class, new QName( NameSpacesEnum.SOAP.getNameSpaceURI(), "operation" ) ); soapOperation.setStyle( "document" ); // NOTA-BENE: Come settare SOAPACTION? jolie usa SOAP1.1 o 1.2? COme usa la SoapAction? soapOperation.setSoapActionURI( wsdlOp.getName() ); bindOp.addExtensibilityElement( soapOperation ); bindOp.setOperation( wsdlOp ); // adding input BindingInput bindingInput = localDef.createBindingInput(); SOAPBody body = (SOAPBody) extensionRegistry.createExtension( BindingInput.class, new QName( NameSpacesEnum.SOAP.getNameSpaceURI(), "body" ) ); body.setUse( "literal" ); bindingInput.addExtensibilityElement( body ); bindOp.setBindingInput( bindingInput ); // adding output BindingOutput bindingOutput = localDef.createBindingOutput(); bindingOutput.addExtensibilityElement( body ); bindOp.setBindingOutput( bindingOutput ); // adding fault if( !wsdlOp.getFaults().isEmpty() ) { for( Object o : wsdlOp.getFaults().entrySet() ) { BindingFault bindingFault = localDef.createBindingFault(); SOAPFault soapFault = (SOAPFault) extensionRegistry.createExtension( BindingFault.class, new QName( NameSpacesEnum.SOAP.getNameSpaceURI(), "fault" ) ); soapFault.setUse( "literal" ); String faultName = ((Entry) o).getKey().toString(); bindingFault.setName( faultName ); soapFault.setName( faultName ); bindingFault.addExtensibilityElement( soapFault ); bindOp.addBindingFault( bindingFault ); } } bind.addBindingOperation( bindOp ); } catch( WSDLException ex ) { ex.printStackTrace(); } } public Service createService( Definition localdef, String serviceName, Binding bind, String mySOAPAddress ) { Port p = localDef.createPort(); p.setName( serviceName + "Port" ); try { SOAPAddress soapAddress = (SOAPAddress) extensionRegistry.createExtension( Port.class, new QName( NameSpacesEnum.SOAP.getNameSpaceURI(), "address" ) ); soapAddress.setLocationURI( mySOAPAddress ); p.addExtensibilityElement( soapAddress ); } catch( WSDLException ex ) { ex.printStackTrace(); } p.setBinding( bind ); Service s = new ServiceImpl(); QName serviceQName = new QName( serviceName ); s.setQName( serviceQName ); s.addPort( p ); localDef.addService( s ); return s; } private Document createDOMdocument() { Document document = null; DocumentBuilder db; try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware( true ); db = dbf.newDocumentBuilder(); document = db.newDocument(); } catch( Exception e ) { e.printStackTrace(); } return document; } private Element createSchemaRootElement( Document document ) { Element rootElement = document.createElement( "xs:schema" ); rootElement.setAttribute( "xmlns:xs", NameSpacesEnum.XML_SCH.getNameSpaceURI() ); rootElement.setAttribute( "targetNamespace", tnsSchema ); rootElement.setAttribute( "xmlns:" + TNS_SCHEMA_PREFIX, tnsSchema ); document.appendChild( rootElement ); return rootElement; } public void setSchemaDocIntoWSDLTypes( Document doc ) { try { Types wsdl_types = localDef.createTypes(); Schema typesExt = (Schema) extensionRegistry.createExtension( Types.class, new QName( NameSpacesEnum.XML_SCH.getNameSpaceURI(), "schema" ) ); typesExt.setElement( (Element) doc.getFirstChild() ); wsdl_types.addExtensibilityElement( typesExt ); localDef.setTypes( wsdl_types ); } catch( Exception ex ) { System.err.println( ex.getMessage() ); } } }