package com.abmuk.ims.client;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.rpc.Call;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceException;
import javax.xml.rpc.ServiceFactory;
import javax.xml.rpc.handler.HandlerInfo;
import javax.xml.rpc.handler.HandlerRegistry;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;

import org.apache.ws.security.handler.WSS4JHandler;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 * Test client for any secure document/literal web service using JAXM APIs and
 * the wss4j JAXRPCHandler.
 * @author nappinc
 */
public class CallSecureService {
    
    /**
     * Calls the web service. First command line argument is XML file containing
     * message to send.
     * @param args The command line arguments
     */
    public static void main (String[] args) {
        try {           
            // load the SOAP message to send
            String message = load(args[0]);
            
            // sets the Web Service endpoint url...
            String endpoint = 
                "http://localhost:9080/sidWS/services/SecureService";
         
            Map config = new HashMap();
            
            /* to do username token... 
            config.put("deployment", "client");
            config.put("flow", "request-only");
            config.put("action", "UsernameToken");
            config.put("user", "wss4j");
            config.put("passwordType", "PasswordDigest");
            config.put("passwordCallbackClass", "com.abmuk.ims.client.PWCallback");
            */
            
            /* to do signature... */
            config.put("deployment", "client");
            config.put("action", "Signature");
            config.put("user", "clientkey");
            //config.put("user", "42f7a28e");
            config.put("passwordCallbackClass", "com.abmuk.ims.client.PWCallback");
            config.put("signatureKeyIdentifier", "DirectReference");
            //config.put("signatureKeyIdentifier", "IssuerSerial");
            config.put("signaturePropFile", "client-signature.properties");
            
            // call the web service
            sendMessage(message, endpoint, config);
        
        } catch (IOException ex) {
            ex.printStackTrace();
            System.exit(-1);    
         }
    }
    
    /**
     * Sends a document/literal SOAP message to the sepcified endpoint.
     * @param message   The message
     * @param endpoint  The endpoint to send to
     * @param config    The wss4j security handelr configuration to use
     * @return The respone
     */
    private static void sendMessage(String message, String endpoint, Map config) {
        try {          
            // configure the security handler
            List handlerChain = new ArrayList();
            handlerChain.add(new HandlerInfo(WSS4JHandler.class, config, null));
            
            ServiceFactory sfactory = ServiceFactory.newInstance();
            Service service = sfactory.createService(
                    new File("./source/wsdl/testSecure.wsdl").toURL(), 
                    new QName("http://www.test.com/Test", "SecureService"));
            
            HandlerRegistry registry = service.getHandlerRegistry();
            
            // define which method we'd like to call
            QName port = new QName("http://www.test.com/Test", "SecurePort");
            registry.setHandlerChain(port, handlerChain);
            Call call = service.createCall(port, "Nominal");
            call.setTargetEndpointAddress(endpoint);
            
            // parse the message
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setNamespaceAware(true);
            DocumentBuilder builder = factory.newDocumentBuilder();
            InputSource source = new InputSource(new StringReader(message));
            Document document = builder.parse(source);
            
            // create a JAXM message, and populate the SOAP body
            SOAPMessage request = MessageFactory.newInstance().createMessage();
            request.getSOAPPart().getEnvelope().getBody().addDocument(document);
                        
            // send the message
            Object[] params =  new Object[] { request.getSOAPPart().getEnvelope().getBody().getFirstChild() };
            Object obj = call.invoke(params); 
            
            System.out.println("response is " + obj);
                   
        } catch (ServiceException ex) {
            ex.printStackTrace();
            System.exit(-1);     
            
        } catch (ParserConfigurationException ex) {
            ex.printStackTrace();
            System.exit(-1);    
        
        } catch (SAXException ex) {
            ex.printStackTrace();
            System.exit(-1);    
            
        } catch (SOAPException ex) {
            ex.printStackTrace();
            System.exit(-1);     
            
        } catch (IOException ex) {
            ex.printStackTrace();
            System.exit(-1);    
         }
    }
    
    /**
     * Loads the contents of the given file, returning it as a String.
     * @param filename The filename
     * @return The contents
     * @throws IOException error reading file (e.g. doesn't exist)
     */
    private static String load(String filename) throws IOException {
        File file = new File(filename);
        if (!file.exists() || !file.canRead()) {
            throw new IOException("File \"" + filename + "\" cannot be read");
        }
        
        BufferedReader in = new BufferedReader(new FileReader(filename));
        StringBuffer buffer = new StringBuffer();
        String line = null;
        while ((line = in.readLine()) != null) {
            buffer.append(line);
        }
        in.close();
        
        return buffer.toString();
    }
}
