package at.plot.soapclient;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Vector;

import org.apache.soap.rpc.Call;
import org.apache.soap.rpc.Parameter;
import org.apache.soap.rpc.Response;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.DOMBuilder;
import org.jdom.output.XMLOutputter;

 /**
 * <p>Title: SOAP-Client für Demonstrationszwecke</p>
 * <p>Description: Beispiel eines SOAP-Clients zum Aufruf des SOAP-Servers für Demonstrationszwecke</p>
 * <p>Copyright: Copyright (c) 2002</p>
 * <p>Company: Plot</p>
 * @version 1.0
 */
public class DemoSOAPClient {

    /**
     * Standard-Constructor
     */
    public DemoSOAPClient() {
    }

    /**
    * Methode zum Aufruf der Java-Applikation.
    * @param args beinhaltet die benötigten Aufrufparameter.
    */
    public static void main(String[] args) {

        // Erzeugen einer neuen Instanz dieser Klasse
        DemoSOAPClient myClient = new DemoSOAPClient();

        // Überprüfen der übergebenen Parameter
        if (args.length != 3) {
            printUsage();
            System.exit(1);
        }
        String inFileName  = args[0];
        String outFileName = args[1];
        String url         = args[2];

        // Ausgabe der übergebenen Paramter
        System.out.println("INFILE:  " + inFileName);
        System.out.println("OUTFILE: " + outFileName);
        System.out.println("URL:     " + url);

        // Erzeugen neuer File-Instanzen für die SOAP-Abfrage und die SOAP-Antwort
        File inFile  = new File (inFileName);
        File outFile = new File (outFileName);

        // Erzeugen eines neuen Datencontainers SoapSend für die SOAP-Abfrage und Prüfen der übergebenen Argumente
        SoapSend soapSend = myClient.new SoapSend();
        checkArgs(inFile, outFile, url, soapSend);
        // Service und Methode aus dem File lesen und in SoapSend hinterlegen
        myClient.extractSoapParms(inFile, soapSend);
        soapSend.message = toString(inFile);

        System.out.println("ServiceType: " + soapSend.service);
        System.out.println("Method:      " + soapSend.methode);
        // System.out.println("Message: \n" + soapSend.message);

        // Durchführen des SOAP-Aufrufs und Speichern der SOAP-Antwort in SoapReceive
        System.out.println("Der SOAP-Aufruf wird durchgeführt...");
        SoapReceive soapReceive = myClient.sendSOAPCall(soapSend);
        System.out.println("Der SOAP-Aufruf wurde beendet.");

        // Prüfen, ob ein Fehler aufgetreten ist
        if (soapReceive.hasError()) {
            System.out.println("Ein Fehler ist aufgetreten.");
            if (soapReceive.faultCode != null) {
                System.out.println("Fehlercode:    " + soapReceive.faultCode);
            }
            System.out.println("Fehlermeldung: " + soapReceive.faultString);
            System.out.println("Es wurde kein OUTFILE gespeichert.");
        }
        else {
            // Prüfen, ob die SOAP-Antwort befüllt ist
            if (soapReceive.message == null || soapReceive.message.length() == 0) {
              System.out.println("Der SOAP-Aufruf lieferte keine Antwort.");
            }
            else {
              // Speichern der SOAP-Antwort
              writeResponse(outFile, soapReceive);
              if(outFile.exists()){
                  System.out.println("Das OUTFILE " + outFile + " wurde gespeichert.");
              }
              else {
                  System.out.println("Das OUTFILE " + outFile + " konnte nicht gespeichert werden.");
              }
            }
        }
    }

    /**
    * Liest aus der Datei <code>pFile</code> die Elemente ServiceType und Method und legt diese in <code>pSoapSend</code> ab.
    * @param pFile das zu lesende File.
    * @param pSoapSend beinhaltet die für den SOAP-Aufruf benötigten Daten.
    */
    private void extractSoapParms(File pFile, SoapSend pSoapSend) {

        Document myDoc = null;
        DOMBuilder domBuilder =  new DOMBuilder();
        try {
            myDoc = domBuilder.build(pFile);
        }
        catch (JDOMException e) {
            System.out.println("Das INFILE " + pFile + " ist kein gültiges XML-File.");
            // e.printStackTrace();
            System.exit(1);
        }

        if (myDoc == null) {
            System.out.println("Das INFILE " + pFile + " ist kein gültiges XML-File.");
            System.exit(1);
        }

        Element rootElement     = myDoc.getRootElement();
        Element serviceTypeElem = rootElement.getChild("ServiceType");
        Element methodeElem     = rootElement.getChild("Method");

        if (serviceTypeElem == null || methodeElem == null) {
            System.out.println("Das INFILE " + pFile + " hat eine ungültige XML-Struktur.");
            System.out.println("Überprüfen Sie bitte, ob Order/ServiceType und Order/Method definiert sind.");
            System.exit(1);
        }

        pSoapSend.service = "urn:" + serviceTypeElem.getText();
        pSoapSend.methode = methodeElem.getText();
    }

    /**
    * Prüft die Argumente und gibt im Fehlerfall eine entsprechende Fehlermeldung aus.
    * @param inFile Die zu sendende Datei
    * @param outFile  Die Datei für die Antwort des SOAP-Calls
    * @param pUrl Die URL des SOAP-Services
    * @param pSoapSend  Die Parameter zum Senden der Meldung
    */
    private static void checkArgs(File inFile, File outFile, String pUrl, SoapSend pSoapSend) {

        if (!inFile.exists()) {
            System.out.println("Das INFILE " + inFile + " existiert nicht.");
            System.exit(1);
        }
        if (!inFile.canRead()) {
            System.out.println("Das INFILE " + inFile + " kann nicht gelesen werden.");
            System.exit(1);
        }
        String outPath = outFile.getParent();
        if(!(new File(outPath)).exists()){
            System.out.println("Der Pfad " + outPath + " zum Abspeichern vom OUTFILE ist ungültig.");
            System.exit(1);
        }
        if (outFile.exists()){
            if (!outFile.canWrite()) {
                System.out.println("Das OUTFILE " + outFile + " kann nicht geschrieben werden.");
                System.exit(1);
            }
        }
        try {
            pSoapSend.url = new URL(pUrl);
        }
        catch (MalformedURLException e) {
            System.out.println("Die angegebene URL " + pUrl + " ist nicht gültig.");
            System.exit(1);
        }
    }

    /**
    * Gibt den Inhalt des Files <code>pFile</code> als String zurück.
    * @param pFile das zu lesende File.
    * @return den Inhalt des Files als String.
    */
    public static String toString(File pFile) {

        String result = null;
        FileReader fr = null;
        try {
            fr = new FileReader(pFile);
            StringBuffer sbMessage = new StringBuffer();
            int ch = 0;
            while ((ch = fr.read()) >= 0) {
                sbMessage.append((char)ch);
            }
            result = sbMessage.toString();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * Führt einen SOAP-Aufruf durch.
     * @param pSoapSend Instanz der Klasse SoapSend mit den Daten, die für den SOAP-Aufruf benötigt werden.
     * @return die Antwort des SOAP-Aufrufs.
     */
    public SoapReceive sendSOAPCall(SoapSend pSoapSend) {

        SoapReceive result = new SoapReceive();
        Response r;

        if (pSoapSend.isValid()) {
            try{
                // Erstellen eines SOAP-Call Objects
                Call rpcCall = new Call();

                rpcCall.setTargetObjectURI(pSoapSend.service);
                rpcCall.setMethodName(pSoapSend.methode);
                rpcCall.setEncodingStyleURI(pSoapSend.NS_URI_SOAP_ENC);

                Vector v = new Vector();
                v.addElement(new Parameter("xmlMsg", String.class, pSoapSend.message, null));

                rpcCall.setParams(v);

                r = rpcCall.invoke(pSoapSend.url, null);

                if (r.generatedFault()){
                    result.faultCode = r.getFault().getFaultCode();
                    result.faultString = r.getFault().getFaultString();
                }
                else {
                    result.message = r.getReturnValue().getValue().toString();
                }
            }
            catch (Exception ex) {
                result.faultString = ex.getMessage();
            }
        }
        else {
            result.faultString = "Parameter nicht vollständig befüllt";
        }
        return result;
    }

    /**
    * Schreibt die Antwort des SOAP-Calls (<code>SoapReceive.message</code>) in die Datei <code>pFile</code>.
    * @param pFile die Datei, in der die SOAP-Antwort abgespeichert wird.
    * @param pSoapReceive
    */
    public static void writeResponse(File pFile, SoapReceive pSoapReceive) {

        try {
            InputStream is = new ByteArrayInputStream(pSoapReceive.message.getBytes());
            FileWriter fw = new FileWriter(pFile);
            (new XMLOutputter("  ", true)).output((new DOMBuilder()).build(is), fw);
            fw.close();
            is.close();
        }
        catch (JDOMException e) {
            e.printStackTrace();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
    * Gibt eine Hilfe zum Aufruf des Programms und den benötigten Parametern aus.
    */
    private static void printUsage() {

        System.out.println("Usage: DemoSOAPClient INFILE OUTFILE URL");
        System.out.println();
        System.out.println("Liest das INFILE und sendet es an die angegebene URL des SOAP-Servers.");
        System.out.println("Die erhaltene SOAP-Antwort wird als OUTFILE gespeichert.");
        System.out.println();
        System.out.println("Beispiel: java at.plot.soapclient.DemoSOAPClient c:/kuendigung01.xml c:/kuendigung01resp.xml http://127.0.0.1:7001/soap/servlet/rpcrouter");
    }

    // -----------------------------------------------------------------------------------------

    /**
    * Klasse mit den Daten, die zum Senden benötigt werden.
    */
    private class SoapSend {

        /** Encoding der Soap-Parameter */
        public static final String NS_URI_SOAP_ENC = "http://schemas.xmlsoap.org/soap/encoding/";
        /** URL des SOAP-Services */
        public URL url        = null;
        /** Name des SOAP-Services */
        public String service = null;
        /** Aufzurufende Methode */
        public String methode = null;
        /** Zu sendende Meldung */
        public String message = null;

        /**
        * Prüft die Befüllung der Daten.
        * Es wird <code>false</code> zurückgegeben, wenn nicht alle für den SOAP-Call benötigten Daten befüllt sind.
        * @return <code>false</code>, falls nicht alle Parameter befüllt sind.
        */
        public boolean isValid() {

            // Prüfen auf null
            if (url     == null) {return false;}
            if (service == null) {return false;}
            if (methode == null) {return false;}
            if (message == null) {return false;}
            // Prüfen auf Inhalt
            if (service.length() <= 0) {return false;}
            if (methode.length() <= 0) {return false;}
            if (message.length() <= 0) {return false;}
            return true;
        }
    }

    /**
    * Klasse mit den Daten der SOAP-Antwort.
    */
    private class SoapReceive {

        /** Fehlercode */
        public String faultCode   = null;
        /** Fehlermeldung */
        public String faultString = null;
        /** Antwort */
        public String message     = null;

        /**
        * Prüft, ob die Antwort einen Fehlercode (<code>faultCode</code>) oder eine Fehlermeldung (<code>faultString</code>) enthält.
        * Ist das der Fall, wird <code>true</code> zurückgegeben.
        * @return <code>true</code>, wenn ein Fehler passiert ist.
        */
        public boolean hasError() {

            // Prüfen, ob der Fehlercode befüllt wurde
            if (faultCode != null) {
                if (faultCode.length() > 0) return true;
            }
            // Prüfen, ob die Fehlermeldung befüllt wurde
            if (faultString != null) {
                if (faultString.length() > 0) return true;
            }
            return false;
        }
    }
}
