/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2009 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or 1any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.ws.jaxws.test;

import java.io.ByteArrayOutputStream;
import javax.wsdl.Definition;
import javax.wsdl.WSDLException;
import javax.wsdl.factory.WSDLFactory;
import javax.wsdl.xml.WSDLReader;
import javax.xml.messaging.URLEndpoint;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPConnection;
import javax.xml.soap.SOAPConnectionFactory;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import java.net.HttpURLConnection;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import org.xml.sax.InputSource;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import java.io.InputStream;
import java.net.URL;
import java.io.StringReader;
import java.io.OutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.ow2.jonas.ws.jaxws.test.util.JWebServicesTestCase;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

public class PojoClientTestCases extends JWebServicesTestCase {
    
    private static final String SIMPLE_PORT = "/pojo/SimpleService";    
    private static final String COMPLEX_PORT = "/pojo/JAXWSBean3Service";  

    public PojoClientTestCases(String s) throws Exception {
        super(s);
    }

    @BeforeClass
    public void setUp() throws Exception {
     }

    @AfterClass
    public void tearDown() throws Exception {
    }
    
    @Test(groups = { "Pojo_WebserviceContext_Injection" })
    public void testWsContextInjectionWSDL() throws Exception {
        useWar("jaxws-pojo-injection");
        try {
            Assert.assertNotNull(getWSDLForService(SIMPLE_PORT),"Can not get wsdl for Pojo_WebserviceContext_Injection!");
        } finally {
            unUseWar("jaxws-pojo-injection");
        }
   }

    @Test(groups = { "Pojo_WebserviceContext_Injection" })
    public void testWsContextInjectionInvoke() throws Exception {
        useWar("jaxws-pojo-injection");
        try {
            Assert.assertTrue(invokePojoService(SIMPLE_PORT).contains("<return>helloJOnAS</return>"),"Reply of greetMe() is not right!");
        } finally {
            unUseWar("jaxws-pojo-injection");
        }
    }

    @Test(groups = { "Pojo_Simple_with_WSDL" })
    public void testSimpleWithWSDLWSDL() throws Exception {
        useWar("jaxws-pojo-simple-with-wsdl");    
        try {
            Assert.assertNotNull(getWSDLForService(SIMPLE_PORT),"Can not get wsdl for Pojo_Simple_with_WSDL!");
        } finally {
            unUseWar("jaxws-pojo-simple-with-wsdl");
        }
   }

    @Test(groups = { "Pojo_Simple_with_WSDL" })
    public void testSimpleWithWSDLInvoke() throws Exception {
        useWar("jaxws-pojo-simple-with-wsdl");
        try {
            Assert.assertTrue(invokePojoService(SIMPLE_PORT).contains("<return>helloJOnAS</return>"),"Reply of greetMe() is not right!");
        } finally {
            unUseWar("jaxws-pojo-simple-with-wsdl");
        }
    }


    @Test(groups = { "Pojo_Simple_without_WSDL" })
    public void testSimpleWithoutWSDLWSDL() throws Exception {
        useWar("jaxws-pojo-simple-without-wsdl");    
        try {
            Assert.assertNotNull(getWSDLForService(SIMPLE_PORT),"Can not get wsdl for Pojo_Simple_without_WSDL!");
        } finally {
            unUseWar("jaxws-pojo-simple-without-wsdl");
        }
   }

    @Test(groups = { "Pojo_Simple_with_WSDL" })
    public void testSimpleWithoutWSDLInvoke() throws Exception {
        useWar("jaxws-pojo-simple-without-wsdl");
        try {
            Assert.assertTrue(invokePojoService(SIMPLE_PORT).contains("<return>helloJOnAS</return>"),"Reply of SimpleService is not right!");
        } finally {
            unUseWar("jaxws-pojo-simple-without-wsdl");
        }
    }

    @Test(groups = { "Pojo_Descriptor" })
    public void testDescriptorWSDL() throws Exception {
        useWar("jaxws-pojo-descriptor");
        try {
            Assert.assertNotNull(getWSDLForService(SIMPLE_PORT),"Can not get wsdl for SimpleService!");
        } finally {
            unUseWar("jaxws-pojo-descriptor");
        }
    }

    @Test(groups = { "Pojo_Descriptor" })
    public void testDescriptorInvoke() throws Exception {
        useWar("jaxws-pojo-descriptor"); 
        try {
            Assert.assertTrue(invokePojoService(SIMPLE_PORT).contains("<return>helloJOnASSOAPHandlerSOAPHandler</return>"),"Reply of SimpleService is not right!");
        } finally {
            unUseWar("jaxws-pojo-descriptor");
        }
    }

    @Test(groups = { "Pojo_Handler" })
    public void testHandlerWSDL() throws Exception {
        useWar("jaxws-pojo-handlers");   
        Assert.assertNotNull(getWSDLForService(SIMPLE_PORT),"Can not get wsdl for SimpleService!");
        unUseWar("jaxws-pojo-handlers");
   }

    @Test(groups = { "Pojo_Handler" })
    public void testHandlerInvoke() throws Exception {
        useWar("jaxws-pojo-handlers");   
        try {
            Assert.assertTrue(invokePojoService(SIMPLE_PORT).contains("<return>helloJOnASSOAPHandler"),"Handler is not called correctly!");

        } finally {
            unUseWar("jaxws-pojo-handlers");
        }
    }


    @Test(groups = { "Pojo_Handler_Injection" })
    public void testHandlerInjectionInvoke() throws Exception {
        useWar("jaxws-pojo-handlers");   
        try {
            Assert.assertTrue(invokePojoService(SIMPLE_PORT).contains("<return>helloJOnASSOAPHandlerwsContext"),"WS Context injection in Handler is not correct!");
        } finally {
            unUseWar("jaxws-pojo-handlers");
        }
    }


    @Test(groups = { "Pojo_Complex" })
    public void testComplexWithWSDLWSDL() throws Exception {
        useWar("jaxws-pojo-complex");    
        try {
            Assert.assertNotNull(getWSDLForService(COMPLEX_PORT),"Can not get wsdl for SimpleService!");
        } finally {
            unUseWar("jaxws-pojo-complex");
        }
   }

    @Test(groups = { "Pojo_Complex" })
    public void testComplexWithWSDLInvoke() throws Exception {
        useWar("jaxws-pojo-complex");    
        try {
                String port = System.getProperty("http.port");
                if(port == null)
                    port = "9000";

                SOAPConnectionFactory soapConnFactory = 
                    SOAPConnectionFactory.newInstance();
                SOAPConnection connection  = soapConnFactory.createConnection();

                MessageFactory messageFactory = MessageFactory.newInstance();
                SOAPMessage message = messageFactory.createMessage();

                SOAPPart soapPart = message.getSOAPPart();
                SOAPEnvelope envelope = (SOAPEnvelope) soapPart.getEnvelope();
                SOAPBody body = envelope.getBody();


                SOAPElement bodyElement = body.addChildElement(envelope.createName("getPersons1"));

                message.saveChanges();

                URLEndpoint destination = new URLEndpoint("http://localhost:"+ port + "/pojo/JAXWSBean3Service");
                SOAPMessage reply = connection.call(message, destination);
                String response = null;
                try{
                    ByteArrayOutputStream os = new ByteArrayOutputStream();
                    reply.writeTo(os);
                    response = new String(os.toByteArray());
                } catch(Exception e) {
                    e.printStackTrace();
                }

                Assert.assertTrue(response.contains("<return><firstName>eric</firstName><lastName>cartman</lastName></return><return><firstName>homer</firstName><lastName>simpson</lastName></return>"),"Reply of SimpleService is not right!");


                soapConnFactory = SOAPConnectionFactory.newInstance();

                connection  = soapConnFactory.createConnection();

                messageFactory = MessageFactory.newInstance();
                message = messageFactory.createMessage();

                soapPart = message.getSOAPPart();
                envelope = (SOAPEnvelope) soapPart.getEnvelope();
                body = envelope.getBody();


                bodyElement = body.addChildElement(envelope.createName("getPersons2"));

                message.saveChanges();

                destination = new URLEndpoint("http://localhost:"+ port + "/pojo/JAXWSBean3Service");
                reply = connection.call(message, destination);
                response = null;
                try{
                    ByteArrayOutputStream os = new ByteArrayOutputStream();
                    reply.writeTo(os);
                    response = new String(os.toByteArray());
                } catch(Exception e) {
                    e.printStackTrace();
                }


                Assert.assertTrue(response.contains("<return xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"ns2:person\"><firstName>eric</firstName><lastName>cartman</lastName></return><return xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"ns2:person\"><firstName>homer</firstName><lastName>simpson</lastName></return>"),"Reply of SimpleService is not right!");

        } finally {
            unUseWar("jaxws-pojo-complex");
        }
    }
    @Test(groups = { "Pojo_SOAPBinding" })
    public void testSOAPBindingInvoke() throws Exception {
        //useWar("jaxws-pojo-SOAPBinding");    
        try {
            //Assert.assertTrue(invokePojoService(SIMPLE_PORT).contains("<return>helloJOnAS</return>"),"Reply of greetMe() is not right!");
        } finally {
            //unUseWar("jaxws-pojo-SOAPBinding");
        }
    }
    
    @Test(groups = { "Pojo_wsProvider" })
    public void testWSProviderInvoke() throws Exception {
        useWar("jaxws-pojo-wsProvider");    
        try {
                InputStream requestInput = this.getClass().getResourceAsStream("/request1.xml");

                URL url = new URL("http://localhost:9000/pojo/SimpleService");
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                try {
                    String reply = call(requestInput, conn);
                    //System.out.println("********************************"+reply);

                    InputSource is = new InputSource(new StringReader(reply));
                    Document doc = parseMessage(is);

                    Text replyMsg = findText(doc.getDocumentElement(), "120");
                    Assert.assertEquals(replyMsg.getData(), "120");


                } finally {
                    conn.disconnect();
                }
        } finally {
            unUseWar("jaxws-pojo-wsProvider");
        }
    }



    private Definition getWSDLForService(String servicePort) throws WSDLException {        
        String port = System.getProperty("http.port");
        if(port == null) 
            port = "9000";

        String url = "http://localhost:" + port + servicePort + "?WSDL";
        WSDLFactory factory = WSDLFactory.newInstance();
        WSDLReader reader = factory.newWSDLReader();
        reader.setFeature("javax.wsdl.importDocuments", true);
        return reader.readWSDL(url);
    }

    private String invokePojoService(String servicePort) throws UnsupportedOperationException, SOAPException {
        String port = System.getProperty("http.port");
        if(port == null)
            port = "9000";

        SOAPConnectionFactory soapConnFactory = 
            SOAPConnectionFactory.newInstance();
        SOAPConnection connection  = soapConnFactory.createConnection();

        MessageFactory messageFactory = MessageFactory.newInstance();
        SOAPMessage message = messageFactory.createMessage();

        SOAPPart soapPart = message.getSOAPPart();
        SOAPEnvelope envelope = (SOAPEnvelope) soapPart.getEnvelope();
        SOAPBody body = envelope.getBody();


        SOAPElement bodyElement = body.addChildElement(envelope.createName("greetMe"));
        SOAPElement strElement = bodyElement.addChildElement("arg0");
        strElement.setValue("JOnAS");

        message.saveChanges();
        //message.writeTo(System.out);
        //System.out.println();

        URLEndpoint destination = new URLEndpoint("http://localhost:"+ port + servicePort);
        SOAPMessage reply = connection.call(message, destination);

        Assert.assertNotNull(reply,"Reply of SimpleService is Null!");

        String response = null;
        try{
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            reply.writeTo(os);
            response = new String(os.toByteArray());
        } catch(Exception e) {
            e.printStackTrace();
        }
        connection.close();

        return response;
    }

    private static String call(InputStream requestInput, HttpURLConnection conn) throws IOException {  
        conn.setConnectTimeout(30 * 1000);
        conn.setReadTimeout(30 * 1000);
        conn.setDoInput(true);
        conn.setUseCaches(false);
        
        if (requestInput == null) {
            conn.setRequestMethod("GET");
            conn.setRequestProperty("Content-Type", "application/xml");
            
            conn.connect();
        } else {
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
            conn.setRequestProperty("Content-Type", "application/xml");

            conn.connect();
            
            OutputStream out = conn.getOutputStream();

            byte[] data = new byte[1024];
            int read = 0;
            while ((read = requestInput.read(data, 0, data.length)) != -1) {
                out.write(data, 0, read);
            }
            
            requestInput.close();
            
            out.flush();
            out.close();
        }

        InputStream is = null;
        
        try {
            is = conn.getInputStream();
        } catch (IOException e) {
            is = conn.getErrorStream();
        }
        
        StringBuffer buf = new StringBuffer();
        BufferedReader in = new BufferedReader(new InputStreamReader(is));
        String inputLine;
        while ((inputLine = in.readLine()) != null) {
            //System.out.println(inputLine);
            buf.append(inputLine);
        }
        in.close();
        
        return buf.toString();
    }

    
    private static Document parseMessage(InputSource is) throws Exception {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        DocumentBuilder db = dbf.newDocumentBuilder();

        Document doc = db.parse(is);
        return doc;
    }
    
    private static Text findText(Element element, String value) {
        NodeList list = element.getChildNodes();
        for (int i = 0; i < list.getLength(); i++) {
            Node child = (Node)list.item(i);
            if (child instanceof Text) {
                Text text = (Text)child;
                if (text.getData().indexOf(value) != -1) {
                    return text;
                }
            } else if (child instanceof Element) {
                Element childEl = (Element)child;
                Text text = findText(childEl, value);
                if (text != null) {
                    return text;
                }
            }
        }   
        return null;
    }    
}
