/*
 * $Id: VKCSOAPToolAgent.java,v 1.4 2006/03/15 12:39:24 marbobo Exp $
 *
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 *
 * $Log: VKCSOAPToolAgent.java,v $
 * Revision 1.4  2006/03/15 12:39:24  marbobo
 * + Refactoring
 *
 * Revision 1.3  2005/09/13 09:51:44  jonyp
 * cleaning the code
 *
 * Revision 1.2  2005/01/03 20:14:18  jonyp
 * changed enum ==> en to be compliant with 1.5
 *
 * Revision 1.1  2004/10/06 14:14:14  koppy
 * initial check in
 *
 */
package vkc.workflow.shark.toolagent;


import java.text.DateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import javax.wsdl.Binding;
import javax.wsdl.Operation;
import javax.wsdl.Port;
import javax.wsdl.Service;
import javax.wsdl.extensions.soap.SOAPAddress;
import javax.xml.namespace.QName;
import javax.xml.rpc.Call;
import javax.xml.rpc.encoding.Deserializer;
import javax.xml.rpc.encoding.DeserializerFactory;

import org.apache.axis.Constants;
import org.apache.axis.encoding.ser.SimpleDeserializer;
import org.apache.axis.wsdl.gen.Parser;
import org.apache.axis.wsdl.symbolTable.BaseType;
import org.apache.axis.wsdl.symbolTable.BindingEntry;
import org.apache.axis.wsdl.symbolTable.Parameter;
import org.apache.axis.wsdl.symbolTable.Parameters;
import org.apache.axis.wsdl.symbolTable.ServiceEntry;
import org.apache.axis.wsdl.symbolTable.SymTabEntry;
import org.apache.axis.wsdl.symbolTable.SymbolTable;
import org.apache.axis.wsdl.symbolTable.TypeEntry;
import org.enhydra.shark.api.SharkTransaction;
import org.enhydra.shark.api.internal.toolagent.AppParameter;
import org.enhydra.shark.api.internal.toolagent.ApplicationBusy;
import org.enhydra.shark.api.internal.toolagent.ApplicationNotDefined;
import org.enhydra.shark.api.internal.toolagent.ApplicationNotStarted;
import org.enhydra.shark.api.internal.toolagent.ToolAgentGeneralException;
import org.enhydra.shark.toolagent.AbstractToolAgent;

/**
 * Tool agent that executes WEB Service using SOAP.
 * When calling invoke method, the wsdl document location is given as
 * application name, the first parameter that is passed has to represent
 * the WEB service method to be called.
 */
public class VKCSOAPToolAgent extends AbstractToolAgent {

   public void invokeApplication (SharkTransaction t,
                                  long handle,
                                  String applicationName,
                                  String procInstId,
                                  String assId,
                                  AppParameter[] parameters,
                                  Integer appMode)
      throws ApplicationNotStarted, ApplicationNotDefined,
      ApplicationBusy, ToolAgentGeneralException {

      super.invokeApplication(t,handle,applicationName,procInstId,assId,parameters,appMode);

      try {
         status=APP_STATUS_RUNNING;

         if (appName==null || appName.trim().length()==0) {
            readParamsFromExtAttributes((String)parameters[0].the_value);
         }

		 String appName = (String)parameters[1].the_value;
		 String operationName = (String)parameters[2].the_value;
		 String loginname = (String)parameters[3].the_value;
		 String password = (String)parameters[4].the_value;
		 
         AppParameter[] aps=new AppParameter[parameters.length-4];
         System.arraycopy(parameters,4,aps,0,aps.length);

/*  
		org.enhydra.shark.Shark shark = org.enhydra.shark.Shark.getInstance();
		AdminInterface ai = shark.getAdminInterface();
		org.enhydra.shark.api.client.wfservice.ParticipantMappingAdministration ma = ai.getParticipantMappingAdministration();
		ma.connect("admin");

		org.enhydra.shark.api.client.wfservice.ParticipantMap newPM = ma.createParticipantMap();
		newPM.setPackageId("Patentanmeldung");
		newPM.setParticipantId("vkc_patent_appl_group");
		newPM.setUsername("ichbins");
		
		ma.addParticipantMapping(newPM);

		org.enhydra.shark.api.client.wfservice.ParticipantMap [] allParticipants = ma.getAllParticipantMappings();
*/

         String portName = null;
         try {
            portName = operationName.substring(operationName.indexOf("(") + 1,
                                               operationName.indexOf(")"));
            operationName = operationName.substring(0,operationName.indexOf("("));
         } catch (Exception ignored) {}

         Parser wsdlParser = null;
         wsdlParser = new Parser();
         cus.info("SOAPToolAgent -> Reading WSDL document from '" + appName + "'");
         wsdlParser.run(appName);


         HashMap map = invokeMethod(wsdlParser,operationName,portName,loginname,password,aps);

         // print result
         cus.info("SOAPToolAgent -> Result:");
         for (Iterator it = map.keySet().iterator(); it.hasNext();) {
            String name = (String) it.next();
            cus.info("       " + name + "=" + map.get(name));
         }

         prepareResult(parameters,map);
         cus.info("\nSOAPToolAgent -> Done!");


         status=APP_STATUS_FINISHED;

      } catch (Throwable ex) {
         cus.error("SOAPToolAgent -> application "+appName+" terminated incorrectly: "+ex);
         status=APP_STATUS_INVALID;
         ex.printStackTrace();
         throw new ToolAgentGeneralException(ex);
      }
   }


   /**
    */
   public HashMap invokeMethod(Parser wsdlParser,String operationName,
                               String portName,String loginname,String password,AppParameter[] args) throws Exception {
      String serviceNS = null;
      String serviceName = null;


      cus.info("SOAPToolAgent -> Preparing Axis dynamic invocation");
      Service service = getService(wsdlParser,serviceNS,serviceName);
      Operation operation = null;
      org.apache.axis.client.Service dpf =
         new org.apache.axis.client.Service(wsdlParser,service.getQName());

      List inputs = new ArrayList();
      Port port = getPort(service.getPorts(),portName);
      if (portName == null) {
         portName = port.getName();
      }
      Binding binding = port.getBinding();
      Call call = dpf.createCall(QName.valueOf(portName),QName.valueOf(operationName));
	  cus.info("SOAPToolAgent -> portName: " + portName + "'");
	  cus.info("SOAPToolAgent -> operationName: " + operationName + "'");
	  
//	  call.setTargetEndpointAddress( new URL( serviceLocation ).toExternalForm() );
//	  call.setOperationName( new QName( "getDocumentCategoryMeta" ) );
	  call.setProperty( Call.USERNAME_PROPERTY, loginname );
	  call.setProperty( Call.PASSWORD_PROPERTY, password );
	  
	  cus.info("SOAPToolAgent -> loginname: " + loginname);

      //((org.apache.axis.client.Call)call).setTimeout(new Integer(15*1000));
      // Output types and names
      List outNames = new ArrayList();

      // Input types and names
      List  inNames = new ArrayList();
      List  inTypes = new ArrayList();
      SymbolTable symbolTable = wsdlParser.getSymbolTable();
      BindingEntry bEntry = symbolTable.getBindingEntry(binding.getQName());
      Parameters parameters = null;
      Iterator i = bEntry.getParameters().keySet().iterator();

      while (i.hasNext()) {
         Operation o = (Operation) i.next();
         if (o.getName().equals(operationName)) {
            operation = o;
            parameters = (Parameters) bEntry.getParameters().get(o);
            break;
         }
      }
      if ((operation == null) || (parameters == null)) {
         throw new RuntimeException(operationName + " was not found.");
      }

      // loop over paramters and set up in/out params
      for (int j=0; j<parameters.list.size(); ++j) {
         Parameter p = (Parameter) parameters.list.get(j);

         if (p.getMode() == 1) {           // IN
            inNames.add(p.getQName().getLocalPart());
            inTypes.add(p);
         } else if (p.getMode() == 2) {    // OUT
            outNames.add(p.getQName().getLocalPart());
         } else if (p.getMode() == 3) {    // INOUT
            inNames.add(p.getQName().getLocalPart());
            inTypes.add(p);
            outNames.add(p.getQName().getLocalPart());
         }
      }

      // set output type
      if (parameters.returnParam != null) {
         QName returnQName = parameters.returnParam.getQName();

         outNames.add(returnQName.getLocalPart());
      }

      if (inNames.size() > args.length - 1)
         throw new RuntimeException("Need " + inNames.size() + " arguments!!!");

      for (int pos=0; pos<inNames.size(); ++pos) {
         AppParameter arg = args[pos+1];
         Parameter p = (Parameter)inTypes.get(pos);
         inputs.add(getParamData((org.apache.axis.client.Call)call,p,arg));
      }
      cus.info("SOAPToolAgent -> Executing operation " + operationName + " with parameters:");
      for (int j=0; j<inputs.size(); j++) {
         cus.info("        "+ inNames.get(j) + "=" + inputs.get(j));
      }
      java.lang.Object ret = call.invoke(inputs.toArray());
      Map outputs = call.getOutputParams();
      HashMap map = new HashMap();

      for (int pos=0; pos<outNames.size(); ++pos) {
         String name = (String) outNames.get(pos);
         java.lang.Object value = outputs.get(name);

         if ((value == null) && (pos == 0)) {
            map.put(name, ret);
         } else {
            map.put(name, value);
         }
      }
      return map;
   }

   private void prepareResult (AppParameter[] context, HashMap hashret) throws Exception {
      int n = 0;
      Set set = hashret.entrySet();
      Iterator it = set.iterator();

      java.lang.Object[] val = new java.lang.Object[hashret.size()];
      while (it.hasNext())  {
         Map.Entry mapentry = (Map.Entry)it.next();
         val[n] = mapentry.getValue();
         n++;
      }

      if (context!=null) {
         int en = 0;
         for (int i = 0; i < context.length; i++) {
            if(context[i].the_mode.equals(AppParameter.MODE_OUT) || context[i].the_mode.equals(AppParameter.MODE_INOUT)) {
               context[i].the_value=convertToProperType(val[en],context[i].the_class);
               en++;
            }
         }
      }
   }

   private Object convertToProperType (Object toConvert,Class desiredType) throws Exception {
      if (desiredType.isInstance(toConvert)) return toConvert;

      if (desiredType.equals(Long.class)) {
         return new Long((new Double(toConvert.toString())).longValue());
      } else if (desiredType.equals(Boolean.class)) {
         return new Boolean(toConvert.toString());
      } else if (desiredType.equals(Double.class)) {
         return new Double(toConvert.toString());
      } else if (desiredType.equals(java.util.Date.class)) {
         return DateFormat.getInstance().parse(toConvert.toString());
      }
      return toConvert;
   }

   /**
    */
   private static java.lang.Object getParamData(org.apache.axis.client.Call c,
                                                Parameter p,
                                                AppParameter ap) throws Exception {
      // Get the QName representing the parameter type
      QName paramType = org.apache.axis.wsdl.toJava.Utils.getXSIType(p);
      String arg=null;

      TypeEntry type = p.getType();
      if (type instanceof BaseType && ((BaseType) type).isBaseType()) {
         DeserializerFactory factory = c.getTypeMapping().getDeserializer(paramType);
         Deserializer deserializer = factory.getDeserializerAs(Constants.AXIS_SAX);
         if (deserializer instanceof SimpleDeserializer) {
            java.lang.Object obj=ap.the_value;
            arg=String.valueOf(obj);
            return ((SimpleDeserializer)deserializer).makeValue(arg);
         }
      }
      throw new RuntimeException("don't know how to convert '" + arg + "' into " + c);
   }

   /**
    */
   public static Service getService (Parser wsdlParser,String serviceNS,String serviceName) throws Exception {
      QName serviceQName = null;
      if (serviceNS != null && serviceName != null) {
         serviceQName=new QName(serviceNS, serviceName);
      }
      ServiceEntry serviceEntry = (ServiceEntry)
         getSymTabEntry(wsdlParser,serviceQName,ServiceEntry.class);
      return serviceEntry.getService();
   }

   /**
    */
   public static Port getPort (Map ports,String portName) throws Exception {
      Iterator valueIterator = ports.keySet().iterator();
      while (valueIterator.hasNext()) {
         String name = (String) valueIterator.next();
         if ((portName == null) || (portName.length() == 0)) {
            Port port = (Port) ports.get(name);
            List list = port.getExtensibilityElements();

            for (int i = 0; (list != null) && (i < list.size()); i++) {
               java.lang.Object obj = list.get(i);
               if (obj instanceof SOAPAddress) {
                  return port;
               }
            }
         } else if ((name != null) && name.equals(portName)) {
            return (Port) ports.get(name);
         }
      }
      return null;
   }

   /**
    */
   public static SymTabEntry getSymTabEntry(Parser wsdlParser,QName qname,Class cls) {
      HashMap map = wsdlParser.getSymbolTable().getHashMap();
      Iterator iterator = map.entrySet().iterator();

      while (iterator.hasNext()) {
         Map.Entry entry = (Map.Entry) iterator.next();
         Vector v = (Vector) entry.getValue();

         if ((qname == null) || qname.equals(qname)) {
            for (int i = 0; i < v.size(); ++i) {
               SymTabEntry symTabEntry = (SymTabEntry) v.elementAt(i);

               if (cls.isInstance(symTabEntry)) {
                  return symTabEntry;
               }
            }
         }
      }
      return null;
   }
}

