/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo 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 General Public License
* along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.wsdl;
import java.io.StringWriter;
import java.util.Iterator;
import java.util.Vector;
import javax.wsdl.Binding;
import javax.wsdl.BindingInput;
import javax.wsdl.BindingOperation;
import javax.wsdl.BindingOutput;
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.PortType;
import javax.wsdl.Service;
import javax.wsdl.Types;
import javax.wsdl.WSDLException;
import javax.wsdl.extensions.ExtensionRegistry;
import javax.wsdl.extensions.UnknownExtensibilityElement;
import javax.wsdl.extensions.soap.SOAPAddress;
import javax.wsdl.extensions.soap.SOAPBinding;
import javax.wsdl.extensions.soap.SOAPBody;
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 javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.openflexo.foundation.FlexoException;
import org.openflexo.foundation.bpel.BPELConstants;
import org.openflexo.foundation.bpel.BPELExportedPartnerLink;
import org.openflexo.foundation.bpel.BPELWriter;
import org.openflexo.foundation.dm.DMEntity;
import org.openflexo.foundation.dm.DMProperty;
import org.openflexo.foundation.wkf.ws.AbstractInPort;
import org.openflexo.foundation.wkf.ws.MessageEntry;
import org.openflexo.foundation.wkf.ws.OutputPort;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.ibm.wsdl.BindingImpl;
import com.ibm.wsdl.BindingInputImpl;
import com.ibm.wsdl.BindingOperationImpl;
import com.ibm.wsdl.BindingOutputImpl;
import com.ibm.wsdl.DefinitionImpl;
import com.ibm.wsdl.InputImpl;
import com.ibm.wsdl.OperationImpl;
import com.ibm.wsdl.OutputImpl;
import com.ibm.wsdl.PartImpl;
import com.ibm.wsdl.PortImpl;
import com.ibm.wsdl.PortTypeImpl;
import com.ibm.wsdl.ServiceImpl;
import com.ibm.wsdl.TypesImpl;
import com.ibm.wsdl.extensions.PopulatedExtensionRegistry;
import com.ibm.wsdl.extensions.schema.SchemaImpl;
import com.ibm.wsdl.extensions.soap.SOAPAddressImpl;
import com.ibm.wsdl.extensions.soap.SOAPBindingImpl;
import com.ibm.wsdl.extensions.soap.SOAPBodyImpl;
public class Exporter {
private static final String NAMESPACE_SCHEMA = "http://www.w3.org/2001/XMLSchema";
Definition wsd;
NamespacePrefixMapper namespacePrefixMapper = new NamespacePrefixMapper();
public Exporter() {
wsd = new DefinitionImpl();
ExtensionRegistry myReg = new PopulatedExtensionRegistry();
/*
QName schemaQN=new QName(NAMESPACE_SCHEMA,"schema");
myReg.registerDeserializer(Types.class,schemaQN,new SchemaDeserializer());
myReg.registerSerializer(Types.class, schemaQN, new SchemaSerializer());
myReg.mapExtensionTypes(Types.class, schemaQN, Schema.class);
*/
wsd.setExtensionRegistry(myReg);
}
public void addNamespace(String prefix, String namespace) {
if (wsd.getNamespaces().containsValue(namespace)) {
// do nothing
} else {
wsd.addNamespace(prefix, namespace);
}
}
/*
* Reminder : only one operation is currently supported per service
*/
public String export(BPELWriter writer, BPELExportedPartnerLink pro) throws Exception {
if (pro == null) {
return null;
}
try {
WSDLFactory wsdlFactory = WSDLFactory.newInstance();
WSDLWriter wsdlWriter = wsdlFactory.newWSDLWriter();
int nsIndex = 0;
wsd.setTargetNamespace(pro.getTargetNamespace());
wsd.addNamespace("xs", NAMESPACE_SCHEMA);
wsd.addNamespace("tns", pro.getTargetNamespace());
wsd.addNamespace("soap", "http://schemas.xmlsoap.org/wsdl/soap/");
namespacePrefixMapper.registerPrefixForNamespace(NAMESPACE_SCHEMA, "xs");
namespacePrefixMapper.registerPrefixForNamespace(pro.getTargetNamespace(), "tns");
/* Messages */
// let's generate Messages before than type, so that from the messages, we can know which type we need to define.
Vector<DMEntity> typesToBeDefined = new Vector<DMEntity>();
AbstractInPort portIN = pro.getPortIN();
Message mesIN = wsd.createMessage();
mesIN.setUndefined(false);
mesIN.setQName(pro.getMessageINType());
wsd.addMessage(mesIN);
Vector<MessageEntry> entriesIN = portIN.getInputMessageDefinition().getEntries();
for (int i = 0; i < entriesIN.size(); i++) {
MessageEntry ent = entriesIN.get(i);
Part p = new PartImpl();
p.setName(ent.getVariableName());
mesIN.addPart(p);
if (ent.getType().getBaseEntity() != null && ent.getType().getBaseEntity().getProperties().size() != 0) {
typesToBeDefined.add(ent.getType().getBaseEntity());
p.setElementName(new QName(ent.getType().getBaseEntity().getPackage().getName(), ent.getType().getName()));
addNamespace("ns" + nsIndex++, ent.getType().getBaseEntity().getPackage().getName());
} else {
p.setTypeName(new QName(NAMESPACE_SCHEMA, "string"));
}
}
OutputPort portOUT = pro.getPortOUT();
Message mesOUT = wsd.createMessage();
mesOUT.setUndefined(false);
mesOUT.setQName(pro.getMessageOUTType());
wsd.addMessage(mesOUT);
Vector<MessageEntry> entriesOUT = portOUT.getOutputMessageDefinition().getEntries();
for (int i = 0; i < entriesOUT.size(); i++) {
MessageEntry ent = entriesOUT.get(i);
Part p = new PartImpl();
p.setName(ent.getVariableName());
mesOUT.addPart(p);
if (ent.getType().getBaseEntity() != null && ent.getType().getBaseEntity().getProperties().size() != 0) {
typesToBeDefined.add(ent.getType().getBaseEntity());
p.setElementName(new QName(ent.getType().getBaseEntity().getPackage().getName(), ent.getType().getName()));
addNamespace("ns" + nsIndex++, ent.getType().getBaseEntity().getPackage().getName());
} else {
p.setElementName(new QName(NAMESPACE_SCHEMA, "string"));
}
}
/* Types */
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.newDocument();
Types types = new TypesImpl();
SchemaStructure ss = new SchemaStructure();
// this loop starts from the types to be defined by the message,
// and recursively builds a strucure "SchemaStructure" containing all the
// types definition, gathered by namespace.
for (DMEntity currentEntity : typesToBeDefined) {
getComplexTypeForDMEntity(doc, currentEntity, ss);
Element instanciableElement = doc.createElement("element");
instanciableElement.setAttribute("name", currentEntity.getName());
instanciableElement.setAttribute("type", namespacePrefixMapper.getPrefixForNamespace(currentEntity.getPackage().getName())
+ ":" + currentEntity.getName());
ss.addElementInNamespace(currentEntity.getPackage().getName(), instanciableElement);
}
// for every different target namespace, the schema element is added in the wsdl definition.
for (Element currentEl : ss.getSchemaDefinition(doc)) {
SchemaImpl currentSchema = new SchemaImpl();
currentSchema.setElement(currentEl);
currentSchema.setElementType(new QName(NAMESPACE_SCHEMA, "schema"));
// currentEl.setPrefix("xsd");
types.addExtensibilityElement(currentSchema);
}
wsd.setTypes(types);
/* Port types */
PortType pt = new PortTypeImpl();
pt.setQName(pro.getPortType());
pt.setUndefined(false);
wsd.addPortType(pt);
// a port type defines a set of operations (one for now)
Operation op = new OperationImpl();
op.setUndefined(false);
pt.addOperation(op);
op.setName(pro.getOperationName());
Input input = new InputImpl();
op.setInput(input);
input.setMessage(mesIN);
input.setName(pro.getOperationName() + "_IN");
Output output = new OutputImpl();
op.setOutput(output);
output.setMessage(mesOUT);
output.setName(pro.getOperationName() + "_OUT");
/* Binding */
// let's generate a SOAP binding over HTTP
Binding b = new BindingImpl();
b.setQName(new QName(pro.getTargetNamespace(), normalise(pro.getProcess().getName()) + "Binding"));
b.setUndefined(false);
b.setPortType(pt);
wsd.addBinding(b);
SOAPBinding sb = new SOAPBindingImpl();
sb.setStyle("document");
sb.setTransportURI("http://schemas.xmlsoap.org/soap/http");
b.addExtensibilityElement(sb);
BindingOperation bo = new BindingOperationImpl();
bo.setName(pro.getOperationName());
b.addBindingOperation(bo);
BindingInput bIN = new BindingInputImpl();
bo.setBindingInput(bIN);
bIN.setName(input.getName());
SOAPBody sBody = new SOAPBodyImpl();
sBody.setUse("literal");
sBody.setNamespaceURI(pro.getTargetNamespace());
bIN.addExtensibilityElement(sBody);
BindingOutput bOUT = new BindingOutputImpl();
bo.setBindingOutput(bOUT);
bOUT.setName(output.getName());
bOUT.addExtensibilityElement(sBody);
/* Service */
// let's make the service available on ODE deployement server
Service ser = new ServiceImpl();
wsd.addService(ser);
ser.setQName(new QName(pro.getTargetNamespace(), normalise(pro.getProcess().getName())));
Port port = new PortImpl();
ser.addPort(port);
port.setName(pro.getProcess().getName() + "Port");
port.setBinding(b);
SOAPAddress address = new SOAPAddressImpl();
address.setLocationURI("http://localhost:4092/" + normalise(pro.getProcess().getName()));
port.addExtensibilityElement(address);
/* Partner Links */
UnknownExtensibilityElement pLinkExtension = new UnknownExtensibilityElement();
Element n = (Element) writer.getPartnerLinkTypeDefinition(writer.getExportedPartnerLink());
pLinkExtension.setElement(n);
wsd.addExtensibilityElement(pLinkExtension);
/* Serialisation */
// wsdlWriter.writeWSDL(wsd, out);
Document docOutput = wsdlWriter.getDocument(wsd);
Transformer transfo = TransformerFactory.newInstance().newTransformer();
transfo.setOutputProperty(OutputKeys.METHOD, "xml");
transfo.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transfo.setOutputProperty(OutputKeys.ENCODING, "utf-8");
transfo.setOutputProperty(OutputKeys.INDENT, "yes");
Source source = new DOMSource(docOutput);
String stringResult = new String();
StringWriter sw = new StringWriter();
Result result = new StreamResult(sw);
transfo.transform(source, result);
stringResult = sw.toString();
return stringResult;
} catch (WSDLException e) {
System.out.println("WSDL exception occured");
e.printStackTrace();
throw new FlexoException(e.getMessage());
} catch (Exception e) {
System.out.println("Unexpected Exception occured");
e.printStackTrace();
throw e;
}
}
/*
public Vector<Element> getAllTypesForDMEntity(Document doc, DMEntity ent,SchemaStructure ss) {
SchemaStructure toReturn=new SchemaStructure();
getComplexTypeForDMEntity(doc,ent,toReturn);
return toReturn.getSchemaDefinition(doc);
}
*/
private Element getComplexTypeForDMEntity(Document doc, DMEntity ent, SchemaStructure acc) {
Element complexType = doc.createElement("complexType");
complexType.setAttribute("name", ent.getName());
Element sequence = doc.createElement("sequence");
complexType.appendChild(sequence);
namespacePrefixMapper.registerNamespace(ent.getPackage().getName());
acc.addElementInNamespace(ent.getPackage().getName(), complexType);
Iterator it = ent.getProperties().values().iterator();
while (it.hasNext()) {
DMProperty currentProp = (DMProperty) it.next();
Element currentPropEl = doc.createElement("element");
if (!currentProp.getType().isPrimitive() && currentProp.getType().getBaseEntity() != null
&& currentProp.getType().getBaseEntity().getProperties().size() != 0) {
getComplexTypeForDMEntity(doc, currentProp.getType().getBaseEntity(), acc);
acc.addNamespaceDependency(ent.getPackage().getName(), currentProp.getType().getBaseEntity().getPackage().getName());
}
currentPropEl.setAttribute("name", currentProp.getName());
currentPropEl.setAttribute("type", getXsdTypeForProperty(currentProp));
sequence.appendChild(currentPropEl);
}
for (String ns : acc.getNamespaceDependency(ent.getPackage().getName())) {
complexType.setAttribute("xmlns:" + namespacePrefixMapper.getPrefixForNamespace(ns), ns);
Element imp = doc.createElement("import");
complexType.insertBefore(imp, complexType.getFirstChild());
imp.setAttribute("namespace", ns);
}
return complexType;
}
private String getXsdTypeForProperty(DMProperty prop) {
if (!prop.getType().isPrimitive() && prop.getType().getBaseEntity() != null
&& prop.getType().getBaseEntity().getProperties().size() != 0) {
return namespacePrefixMapper.getPrefixForNamespace(prop.getType().getBaseEntity().getPackage().getName()) + ":"
+ prop.getType().getBaseEntity().getName();
} else {
return namespacePrefixMapper.getPrefixForNamespace(NAMESPACE_SCHEMA) + ":string";
}
}
public String normalise(String s) {
return BPELConstants.normalise(s);
}
}