content: /**
   * ﻿Copyright (C) 2007 - 2014 52°North Initiative for Geospatial Open Source
   * Software GmbH
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 as published
   * by the Free Software Foundation.
   *
   * If the program is linked with libraries which are licensed under one of
   * the following licenses, the combination of the program with the linked
   * library is not considered a "derivative work" of the program:
   *
   *       • Apache License, version 2.0
   *       • Apache Software License, version 1.0
   *       • GNU Lesser General Public License, version 3
   *       • Mozilla Public License, versions 1.0, 1.1 and 2.0
   *       • Common Development and Distribution License (CDDL), version 1.0
   *
   * Therefore the distribution of the program linked with libraries licensed
   * under the aforementioned licenses, is permitted by the copyright holders
   * if the distribution is compliant with both the GNU General Public
   * License version 2 and the aforementioned licenses.
   *
   * 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.
   */
  package org.n52.wps.transactional.deploy.bpel.apache;
  
  import java.io.BufferedInputStream;
  import java.io.ByteArrayOutputStream;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.FileNotFoundException;
  import java.io.InputStream;
  import java.io.StringWriter;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  
  import javax.xml.namespace.NamespaceContext;
  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.TransformerException;
  import javax.xml.transform.TransformerFactory;
  import javax.xml.transform.TransformerFactoryConfigurationError;
  import javax.xml.transform.dom.DOMSource;
  import javax.xml.transform.stream.StreamResult;
  import javax.xml.xpath.XPath;
  import javax.xml.xpath.XPathFactory;
  
  import net.opengis.wps.x100.DataInputsType;
  import net.opengis.wps.x100.ExecuteDocument;
  import net.opengis.wps.x100.InputType;
  
  import org.apache.axiom.om.OMAbstractFactory;
  import org.apache.axiom.om.OMAttribute;
  import org.apache.axiom.om.OMElement;
  import org.apache.axiom.om.OMFactory;
  import org.apache.axiom.om.OMNamespace;
  import org.apache.axiom.om.OMText;
  import org.apache.axiom.om.util.Base64;
  import org.apache.axiom.soap.SOAPEnvelope;
  import org.apache.axiom.soap.SOAPFactory;
  import org.apache.axis2.AxisFault;
  import org.apache.axis2.addressing.EndpointReference;
  import org.apache.axis2.client.OperationClient;
  import org.apache.axis2.client.Options;
  import org.apache.axis2.client.ServiceClient;
  import org.apache.axis2.context.MessageContext;
  import org.apache.axis2.saaj.util.SAAJUtil;
  import org.n52.wps.PropertyDocument.Property;
  import org.n52.wps.commons.WPSConfig;
  import org.n52.wps.server.ITransactionalAlgorithmRepository;
  import org.n52.wps.transactional.deploy.AbstractProcessManager;
  import org.n52.wps.transactional.deploymentprofiles.BPELDeploymentProfile;
  import org.n52.wps.transactional.deploymentprofiles.DeploymentProfile;
  import org.n52.wps.transactional.request.DeployProcessRequest;
  import org.n52.wps.transactional.request.UndeployProcessRequest;
  import org.w3c.dom.Document;
  import org.w3c.dom.Node;
  
  public class ApacheBPELManager extends AbstractProcessManager {
  
      private ODEServiceClient _client;
      private OMFactory _factory;
      private String deploymentEndpoint;
      private String processManagerEndpoint;
      
      public ApacheBPELManager(ITransactionalAlgorithmRepository repository) {
          super(repository);
  
      	Property[] properties = WPSConfig.getInstance().getPropertiesForRepositoryClass(repository.getClass().getName());
          //TODO think of multiple instance of this class registered (yet not possible since singleton)
          Property deployEndpointProperty = WPSConfig.getInstance().getPropertyForKey(properties, "ODE-Engine_DeploymentEndpoint");
          if(deployEndpointProperty==null){
                  throw new RuntimeException("Error. Could not find ODE-Engine_DeploymentEndpoint");
          }
          deploymentEndpoint = deployEndpointProperty.getStringValue();
          Property processManagerEndpointProperty = WPSConfig.getInstance().getPropertyForKey(properties, "ODE-Engine_ProcessManagerEndpoint");
          if(processManagerEndpointProperty==null){
                  throw new RuntimeException("Error. Could not find ODE-Engine_ProcessManagerEndpoint");
          }
          processManagerEndpoint = processManagerEndpointProperty.getStringValue();
  
           try{
              _factory = OMAbstractFactory.getOMFactory();
              _client = new ODEServiceClient();
          }catch(Exception e){
              e.printStackTrace();
          }
      }
  
      public boolean deployProcess(DeployProcessRequest request) throws Exception {
  
          boolean blnresult = false;
          // WPS-T preparation
          DeploymentProfile profile = request.getDeploymentProfile();
          if(! (profile instanceof BPELDeploymentProfile)){
                  throw new Exception("Requested Deployment Profile not supported");
          }
          BPELDeploymentProfile deploymentProfile = (BPELDeploymentProfile) profile; 
          String processID = deploymentProfile.getProcessID();
          Node suitcase = deploymentProfile.getSuitCase();
          System.out.println(nodeToString(suitcase));
          Node workflow = deploymentProfile.getBPEL();
          System.out.println(nodeToString(workflow));
          Node clientWSDL = deploymentProfile.getClientWSDL();
          System.out.println(nodeToString(clientWSDL));
          Map<Integer, Node> wsdlList = deploymentProfile.getWSDLList();
  
          //ODE preparation
          OMElement result = null;
          try{
              OMNamespace pmapi = _factory.createOMNamespace("http://www.apache.org/ode/pmapi", "pmapi");
              OMElement root = _factory.createOMElement("deploy", null); // qualified operation name
              OMElement namePart = _factory.createOMElement("name", null);
              namePart.setNamespace(pmapi);
              namePart.setText(processID);
              //OMElement zipPart = _factory.createOMElement("package", null);
              OMElement zipPart = _factory.createOMElement("package", pmapi);
              //OMElement zipElmt = _factory.createOMElement("zip", null);
              OMElement zipElmt = _factory.createOMElement("zip", pmapi);
  
              File zipFile = File.createTempFile("wpsbpel", ".zip", null);
  
              int res = ZipCreator.makeZIP(processID, suitcase, workflow, clientWSDL, wsdlList, zipFile);
               try{
                  InputStream is = new BufferedInputStream(new FileInputStream(zipFile));
  
                  ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                  try{
                      for (int b = is.read(); b >= 0; b = is.read()) {
                          outputStream.write((byte) b);
                      }
                      String base64Enc = Base64.encode(outputStream.toByteArray());
                      OMText zipContent = _factory.createOMText(base64Enc, "application/zip", true);
                      root.addChild(namePart);
                      root.addChild(zipPart);
                      zipPart.addChild(zipElmt);
                      zipElmt.addChild(zipContent);
  
                      
                  }catch (Exception e){
                      e.printStackTrace();
                  }
              }catch(FileNotFoundException e){
                  e.printStackTrace();
              }
  
              // Deploy
              try{
                  result = sendToDeployment(root);
              }catch(Exception e){
                  e.printStackTrace();
                  blnresult = false;
              }
          }catch(NullPointerException npe){
              npe.printStackTrace();
          }
          //if (result != null){
              if (result.toString().contains("response"))
                  blnresult = true;
          //}
          return blnresult;
          //throw new UnsupportedOperationException("Not supported yet.");
      }
  
      public boolean unDeployProcess(String processID) throws Exception {
          // Prepare undeploy message
          OMNamespace pmapi = _factory.createOMNamespace("http://www.apache.org/ode/pmapi", "pmapi");
          OMElement root = _factory.createOMElement("undeploy", pmapi);  // qualified operation name
          OMElement part = _factory.createOMElement("processName", pmapi);
          part.setText(processID);
          root.addChild(part);
  
          // Undeploy
          sendToDeployment(root);
  
          return true;
  	}
      
      public Document invoke(ExecuteDocument doc, String algorithmID) throws Exception {
          
          Node domNode = doc.getDomNode();
  
  
          //doc.save(new File("d:\\tmp\\execdoc.xml"));
          //String serializedXML = writeXMLToStream(new DOMSource(domNode)).toString();
          
  //      serializedXML = serializedXML.replace("<?xml version=\"1.0\" encoding=\"UTF-8\"?>","");
  //	serializedXML = serializedXML.replace("<Execute xmlns=\"http://www.opengis.net/wps/1.0.0\">","<Execute xmlns=\"http://www.opengis.net/wps/1.0.0\" version=\"1.0.0\" service=\"WPS\">");
  //	serializedXML = serializedXML.replace(" href"," xmlns:xlin=\"http://www.w3.org/1999/xlink\" xlin:href");
          ServiceClient client = null;
          DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
          DocumentBuilder builder = factory.newDocumentBuilder();
          SOAPEnvelope response = null;
          try{
              client = new ServiceClient();
              OperationClient operationClient = client.createClient(ServiceClient.ANON_OUT_IN_OP);
              //creating message context
              MessageContext outMsgCtx = new MessageContext();
              //assigning message context's option object into instance variable
              Options opts = outMsgCtx.getOptions();
              //setting properties into option
  
              //TODO is this correct?
  
              opts.setTo(new EndpointReference(deploymentEndpoint.replace("DeploymentService", algorithmID)));
              opts.setAction("");
              outMsgCtx.setEnvelope(createSOAPEnvelope(doc));
              operationClient.addMessageContext(outMsgCtx);
              operationClient.execute(true);
              MessageContext inMsgtCtx = operationClient.getMessageContext("In");
              response = inMsgtCtx.getEnvelope();
  
              
          }catch(AxisFault af){
  
          }finally{
  //            if (client != null){
  //                try{
  //                    client.cleanupTransport();
  //
  //                }catch(Exception e){
  //                    e.printStackTrace();
  //                }
  //                try{
  //                    client.cleanup();
  //                }catch(Exception e){
  //                    e.printStackTrace();
  //                }
  //            }
          }
  
  
          //TODO: Parse SoapEnvelope to DOM Document
  
         Document result = SAAJUtil.getDocumentFromSOAPEnvelope(response);
             
  		if (client != null) {
  			try {
  				client.cleanupTransport();
  
  			} catch (Exception e) {
  				e.printStackTrace();
  			}
  			try {
  				client.cleanup();
  			} catch (Exception e) {
  				e.printStackTrace();
  			}
  		}
         
         
          //Document result = builder.parse((InputStream)response.getXMLStreamReader());
  
          //System.out.print(result.toString());
          return result;
          //throw new UnsupportedOperationException("Not supported yet.");
  
          
      }
  
      public Collection<String> getAllProcesses() throws Exception {
         try{
      	   List<String> allProcesses = new ArrayList<String>();
  
             //ServiceClient sc = new ServiceClient(null, null);
  
  
      	   OMElement listRoot = _client.buildMessage("listAllProcesses", new String[] {}, new String[] {});
                  
      	   OMElement result = sendToPM(listRoot);
      	   Iterator<OMElement> pi = result.getFirstElement().getChildrenWithName(
                         new QName("http://www.apache.org/ode/pmapi/types/2006/08/02/","process-info"));
          
      	   while (pi.hasNext()) {
      		   OMElement omPID = pi.next();
      		   
      		   String fullName = omPID.getFirstChildWithName(
                         new QName("http://www.apache.org/ode/pmapi/types/2006/08/02/","pid")).getText();
      		   
      		   /*just take the name as defined by the user...
      		    * whats returned originally was something like
      		    * {http://xy.z}ProcessName-XXX (-XXX is attached due to the ODE-versioning)
      		    * this lead to problems with the processdescription, which
      		    * has the name "ProcessName"
      		    */
      		   allProcesses.add(fullName.substring(fullName.indexOf("}") + 1, fullName.indexOf("-")));
      	   }
      	   return allProcesses;
         }catch(Exception e){
      	   return new ArrayList<String>();
         }
        
          
      }
  
      public boolean containsProcess(String processID) throws Exception {
          boolean containsProcess = false;
          //need to filter out the namespace if it is passed in.
          if (processID.contains("}"))
              processID = processID.split("}")[1];
          try{
              OMElement listRoot = _client.buildMessage("listProcesses", new String[] {"filter", "orderKeys"},
                  new String[] {"name="+processID+"", ""});
              OMElement result = sendToPM(listRoot);
  
              if (result.toString().contains(processID))
                  containsProcess = true;
          }catch(AxisFault af){
              containsProcess = false;
              af.printStackTrace();
          }catch(Exception e){
              containsProcess = false;
              e.printStackTrace();
          }
          //return getAllProcesses().contains(processID);
          return containsProcess;
          //throw new UnsupportedOperationException("Not supported yet.");
      }
  
      public boolean unDeployProcess(UndeployProcessRequest request) throws Exception {
          //unDeployProcess(String processID) is implemented though...
          return unDeployProcess((String)request.getProcessID());
          //return false;
          //throw new UnsupportedOperationException("Not supported yet.");
      }
  
      
       private OMElement sendToPM(OMElement msg) throws AxisFault {
      	return _client.send(msg,this.processManagerEndpoint);
          //return _PMclient.send(msg, this.processManagerEndpoint,10000);
      }
  
      private OMElement sendToDeployment(OMElement msg) throws AxisFault {
          return _client.send(msg,this.deploymentEndpoint);
  
          //return _DEPclient.send(msg,this.deploymentEndpoint,10000);
      }
  
      private ByteArrayOutputStream writeXMLToStream(Source source) throws TransformerException {
  //		 Prepare the DOM document for writing
         
          ByteArrayOutputStream out = new ByteArrayOutputStream();
          // Prepare the output file
         
          Result result = new StreamResult(out);
          //System.etProperty("javax.xml.transform.TransformerFactory","com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl");
          // Write the DOM document to the file
          TransformerFactory x = TransformerFactory.newInstance();
          Transformer xformer = x.newTransformer();
          xformer.transform(source, result);
          
          return out;
  	}
      
  	private String nodeToString(Node node) throws TransformerFactoryConfigurationError, TransformerException {
  		StringWriter stringWriter = new StringWriter();
  		Transformer transformer = TransformerFactory.newInstance().newTransformer();
  		transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
  		transformer.transform(new DOMSource(node), new StreamResult(stringWriter));
  		
  		return stringWriter.toString();
  	}
      
      
      public  SOAPEnvelope createSOAPEnvelope(Node domNode) {
          SOAPFactory fac = OMAbstractFactory.getSOAP11Factory();
          SOAPEnvelope envelope = fac.getDefaultEnvelope();
  
           NamespaceContext ctx = new NamespaceContext() {
  
              public String getNamespaceURI(String prefix) {
                  String uri;
                            if (prefix.equals("wps"))
                                uri = "http://www.opengis.net/wps/1.0.0";
                            else if (prefix.equals("ows"))
                                uri = "http://www.opengis.net/ows/1.1";
                            else
                                uri = null;
                            return uri;
              }
  
              public String getPrefix(String namespaceURI) {
                  return null;
              }
  
              public Iterator getPrefixes(String namespaceURI) {
                  return null;
              }
          };
  
          XPathFactory xpathFact =
                        XPathFactory.newInstance();
          XPath xpath = xpathFact.newXPath();
          xpath.setNamespaceContext(ctx);
  
          String identifier = null;
          String input = null;
          String xpathidentifier = "//ows:Identifier";
          String xpathinput = "//wps:DataInputs/wps:Input/wps:Data/wps:LiteralData";
  
          try{
             identifier = xpath.evaluate(xpathidentifier, domNode);
             input =  xpath.evaluate(xpathinput, domNode);
          }catch(Exception e){
              e.printStackTrace();
          }
          //OMNamespace wpsNs = fac.createOMNamespace("http://scenz.lcr.co.nz/wpsHelloWorld", "wps");
          OMNamespace wpsNs = fac.createOMNamespace("http://scenz.lcr.co.nz/"+ identifier, "wps");
          // creating the payload
          
          //TODO: parse the domNode to a request doc
          //OMElement method = fac.createOMElement("wpsHelloWorldRequest", wpsNs);
          OMElement method = fac.createOMElement(identifier + "Request", wpsNs);
          OMElement value = fac.createOMElement("input", wpsNs);
          //value.setText("Niels");
          value.setText(input);
          method.addChild(value);
          envelope.getBody().addChild(method);
          return envelope;
      }
      
  	@SuppressWarnings("unchecked")
  	private SOAPEnvelope createSOAPEnvelope(ExecuteDocument execDoc) {
  
  		SOAPFactory fac = OMAbstractFactory.getSOAP11Factory();
  		SOAPEnvelope envelope = fac.getDefaultEnvelope();
  
  		NamespaceContext ctx = new NamespaceContext() {
  
  			public String getNamespaceURI(String prefix) {
  				String uri;
  				if (prefix.equals("wps"))
  					uri = "http://www.opengis.net/wps/1.0.0";
  				else if (prefix.equals("ows"))
  					uri = "http://www.opengis.net/ows/1.1";
  				else
  					uri = null;
  				return uri;
  			}
  
  			public String getPrefix(String namespaceURI) {
  				return null;
  			}
  
  			public Iterator getPrefixes(String namespaceURI) {
  				return null;
  			}
  		};
  
  		_client = new ODEServiceClient();
  		HashMap<String, String> allProcesses = new HashMap<String, String>();
  
  		OMElement listRoot = _client.buildMessage("listAllProcesses",
  				new String[] {}, new String[] {});
  
  		OMElement result = null;
  		try {
  			result = sendToPM(listRoot);
  		} catch (AxisFault e) {
  			// TODO Auto-generated catch block
  			e.printStackTrace();
  		}
  		Iterator<OMElement> pi = result.getFirstElement().getChildrenWithName(
  				new QName("http://www.apache.org/ode/pmapi/types/2006/08/02/",
  						"process-info"));
  
  		while (pi.hasNext()) {
  			OMElement omPID = pi.next();
  
  			String fullName = omPID
  					.getFirstChildWithName(
  							new QName(
  									"http://www.apache.org/ode/pmapi/types/2006/08/02/",
  									"pid")).getText();
  			allProcesses.put(
  					fullName.substring(fullName.indexOf("}") + 1,
  							fullName.indexOf("-")),
  					fullName.substring(1, fullName.indexOf("}")));
  
  		}
  
  		String identifier = execDoc.getExecute().getIdentifier()
  				.getStringValue();
  
  		OMNamespace wpsNs = null;
  
  		for (String string : allProcesses.keySet()) {
  
  			if (string.equals(identifier)) {
  				wpsNs = fac.createOMNamespace(allProcesses.get(string), "nas");
  				break;
  			}
  
  		}
  		// creating the payload
  
  		// TODO: parse the domNode to a request doc
  		// OMElement method = fac.createOMElement("wpsHelloWorldRequest",
  		// wpsNs);
  		OMElement method = fac.createOMElement(identifier + "Request", wpsNs);
  		envelope.getBody().addChild(method);
  
  		DataInputsType datainputs = execDoc.getExecute().getDataInputs();
  
  		for (InputType input1 : datainputs.getInputArray()) {
  
  			String inputIdentifier = input1.getIdentifier().getStringValue();
  			OMElement value = fac.createOMElement(inputIdentifier, "", "");
  			if (input1.getData() != null
  					&& input1.getData().getLiteralData() != null) {
  				value.setText(input1.getData().getLiteralData()
  						.getStringValue());
  			} else {
  				// Node no =
  				// input1.getData().getComplexData().getDomNode().getChildNodes().item(1);
  				// value.setText("<![CDATA[" + nodeToString(no) + "]>");
  				// value.addChild(no);
  				OMElement reference = fac.createOMElement("Reference",
  						"http://www.opengis.net/wps/1.0.0", "wps");
  				OMNamespace xlin = fac.createOMNamespace(
  						"http://www.w3.org/1999/xlink", "xlin");
  
  				OMAttribute attr = fac.createOMAttribute("href", xlin, input1
  						.getReference().getHref());
  				reference.addAttribute(attr);
  				reference.addAttribute("schema", input1.getReference()
  						.getSchema(), fac.createOMNamespace("", ""));
  				value.addChild(reference);
  			}
  			method.addChild(value);
  		}
  
  		return envelope;
  
  	}
  }
  