/***************************************************************************
* Copyright (C) 2006-09-10 by Claudio Guidi and Francesco Bullini
* <cguidi@italianasoftware.com> <fbullini@italianasoftware.com> *
* *
* 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.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.net.URI;
import java.util.ArrayList;
import java.util.Iterator;
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.RequestResponseOperationDeclaration;
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 int MAX_CARD = 2147483647;
private String tns;
private String tns_schema;
private String tns_schema_prefix = "sch";
static ExtensionRegistry extensionRegistry;
private static WSDLFactory wsdlFactory;
private Definition localDef = null;
private WSDLWriter ww = null;
private Writer fw = null;
private List<String> rootTypes = new ArrayList<String>();
private ProgramInspector inspector;
private 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", tns_schema );
fw = new FileWriter( filename );
} catch( IOException ex ) {
Logger.getLogger( WSDLDocCreator.class.getName() ).log( Level.SEVERE, null, ex );
} catch( WSDLException ex ) {
Logger.getLogger( WSDLDocCreator.class.getName() ).log( Level.SEVERE, null, ex );
}
return localDef;
}
public void ConvertDocument( String filename, String tns ) throws Jolie2WsdlException
{
boolean soap_port_flag = false;
System.out.println( "Starting conversion..." );
this.tns = tns + ".wsdl";
this.tns_schema = tns + ".xsd";
try {
initWsdl( null, filename );
schemaDocument = this.createDOMdocument();
schemaRootElement = this.createSchemaRootElement( schemaDocument );
// scans inputPorts
InputPortInfo[] inputPortList = inspector.getInputPorts( originalFile );
for( InputPortInfo inputPort : inputPortList ) {
if ( inputPort.protocolId().equals( "soap" ) ) {
soap_port_flag = true;
// 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 ( inputPort.location().toString().equals( "local" )) {
address = "local";
} else {
address = inputPort.location().toString().substring( 6 ); // exclude socket word
address = "http" + address;
}
createService(localDef, portTypeName + "Service", bd, address );
System.out.println("Scanning interfaces...");
// scans interfaces
for ( InterfaceDefinition interfaceDefinition:inputPort.getInterfaceList() )
{
// scan operations
Map< String , OperationDeclaration > operationMap = interfaceDefinition.operationsMap();
for ( Entry< String , OperationDeclaration > operationEntry:operationMap.entrySet() ) {
if ( operationEntry.getValue() instanceof OneWayOperationDeclaration ) {
// OW
//-------------- adding operation
OneWayOperationDeclaration oneWayOperation = ( OneWayOperationDeclaration)operationEntry.getValue();
Operation wsdlOp = addOWOperation2PT( localDef, pt, oneWayOperation );
// adding operation binding
addOperationSOAPBinding( localDef, pt, wsdlOp, bd );
} else {
// RR
//-------------- adding operation
RequestResponseOperationDeclaration requestResponseOperation = ( RequestResponseOperationDeclaration ) operationEntry.getValue();
Operation wsdlOp = addRROperation2PT( localDef, pt, requestResponseOperation );
// adding operation binding
addOperationSOAPBinding( localDef, pt, wsdlOp, bd );
}
}
}
}
}
if ( soap_port_flag == false ) {
throw( new Jolie2WsdlException( "ERROR: jolie2wsdl only support soap port declarations in jolie files" ) );
}
setSchemaDocIntoWSDLTypes( schemaDocument );
WSDLWriter wsdl_writer = wsdlFactory.newWSDLWriter();
wsdl_writer.writeWSDL( localDef, fw );
}
catch( WSDLException ex ) {
System.err.println( ex.getMessage() );
Logger.getLogger( WSDLDocCreator.class.getName() ).log( Level.SEVERE, null, ex );
} catch( Exception ex ) {
System.err.println( ex.getMessage() );
Logger.getLogger( WSDLDocCreator.class.getName() ).log( Level.SEVERE, null, ex );
}
System.out.println( "Success: WSDL document generated!" );
}
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";
}
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() ) {
Iterator it = type.subTypes().iterator();
while ( it.hasNext() ) {
TypeDefinition curType = ((Entry<String,TypeDefinition>) it.next()).getValue();
Element subEl = schemaDocument.createElement("xs:element");
subEl.setAttribute("name", curType.id() );
subEl.setAttribute( "minOccurs", new Integer( curType.cardinality().min()).toString() );
String maxOccurs = "unbounded";
if ( curType.cardinality().max() < MAX_CARD ) {
maxOccurs = new Integer( curType.cardinality().max() ).toString();
}
subEl.setAttribute( "maxOccurs", maxOccurs );
if ( curType instanceof TypeInlineDefinition ) {
if ( curType.hasSubTypes( ) ) {
if ( 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( curType.nativeType() ));
}
} else {
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 ( 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 ));
}
}
}
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( tns_schema, 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( tns_schema, outputPartName ) );
outputMessage.addPart( outputPart );
outputMessage.setUndefined( false );
localDef.addMessage( outputMessage );
} catch( Exception e ) {
e.printStackTrace();
}
return outputMessage;
}
private Message addFaultMessage( Definition localDef, OperationDeclaration op, TypeDefinition tp, String faultName )
{
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( tns_schema, 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, op, curFault.getValue(), curFault.getKey() );
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.SOAPoverHTTP.getNameSpaceURI() );
soapBinding.setStyle( "document" );
bind.addExtensibilityElement( soapBinding );
} catch( WSDLException ex ) {
System.out.println( ( ex.getStackTrace() ));
}
def.addBinding( bind );
return bind;
}
private void addOperationSOAPBinding( Definition localDef, PortType portType, 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() ) {
Iterator it = wsdlOp.getFaults().entrySet().iterator();
while ( it.hasNext() ) {
BindingFault bindingFault = localDef.createBindingFault();
SOAPFault soapFault = ( SOAPFault ) extensionRegistry.createExtension( BindingFault.class, new QName( NameSpacesEnum.SOAP.getNameSpaceURI(), "fault" ) );
soapFault.setUse("literal");
String faultName = ((Entry) it.next()).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;
DocumentBuilder db = null;
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware( true );
db = dbf.newDocumentBuilder();
} catch( Exception e ) {
e.printStackTrace();
}
document = db.newDocument();
return document;
}
private Element createSchemaRootElement( Document document )
{
Element rootElement = document.createElement("xs:schema");
rootElement.setAttribute( "xmlns:xs", NameSpacesEnum.XML_SCH.getNameSpaceURI() );
rootElement.setAttribute( "targetNamespace", tns_schema );
rootElement.setAttribute( "xmlns:" + tns_schema_prefix, tns_schema);
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() );
}
}
}