/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2010, Red Hat Middleware LLC, and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This 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 (at your option) any later version.
 *
 * This software 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.test.ws.jaxws.jbws3008;

import java.io.ByteArrayInputStream;
import java.net.URL;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPMessage;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Dispatch;
import javax.xml.ws.EndpointReference;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.WebServiceFeature;
import javax.xml.ws.soap.AddressingFeature;
import javax.xml.ws.soap.SOAPFaultException;
import javax.xml.ws.wsaddressing.W3CEndpointReference;

import junit.framework.Test;

import org.jboss.test.ws.jaxws.common.JAXWS_Util;
import org.jboss.test.ws.jaxws.jbws3008.generated.AddNumbers;
import org.jboss.test.ws.jaxws.jbws3008.generated.AddNumbersService;
import org.jboss.test.ws.jaxws.jbws3008.generated.DoAddNumbers;
import org.jboss.test.ws.jaxws.jbws3008.generated.DoAddNumbersResponse;
import org.jboss.test.ws.jaxws.jbws3008.generated.ObjectFactory;
import org.jboss.wsf.common.DOMUtils;
import org.jboss.wsf.test.JBossWSTest;
import org.jboss.wsf.test.JBossWSTestSetup;
import org.w3c.dom.Node;

/**
 * [JBWS-3008] Dispatch doesn't respect @Addressing features
 *
 * @author <a href="mailto:ropalka@redhat.com">Richard Opalka</a>
 */
public class JBWS3008TestCase extends JBossWSTest
{
   private String url = null;

   private URL wsdlurl = null;

   private static final String NAMESPACEURI = "http://foobar.org/";

   private static final String SERVICE_NAME = "AddNumbersService";

   private static final String PORT_NAME = "AddNumbersPort";

   private QName SERVICE_QNAME = new QName(NAMESPACEURI, SERVICE_NAME);

   private QName PORT_QNAME = new QName(NAMESPACEURI, PORT_NAME);

   private WebServiceFeature[] wsftrue =
   {new AddressingFeature(true, true)};

   private WebServiceFeature[] wsffalse =
   {new AddressingFeature(false, false)};

   AddNumbers port = null;

   BindingProvider bp = null;

   static AddNumbersService service = null;

   private Dispatch<Object> dispatchJaxb = null;

   private Dispatch<Source> dispatchSrc = null;

   private Dispatch<SOAPMessage> dispatchSM = null;

   // The elementFormDefault is "unqualified" in associated document literal WSDL, thus all the child elements have to be unqualified.
   private String doAddNumbersRequest = "<ns3:doAddNumbers xmlns:ns3=\"http://foobar.org/\"><arg0>10</arg0><arg1>10</arg1></ns3:doAddNumbers>"; 

   private String doAddNumbersRequestSM = "<?xml version=\"1.0\" ?><S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\"><S:Body><ns3:doAddNumbers xmlns:ns3=\"http://foobar.org/\"><arg0>10</arg0><arg1>10</arg1></ns3:doAddNumbers></S:Body></S:Envelope>";

   private static final Class<?> JAXB_OBJECT_FACTORY = ObjectFactory.class;

   public static Test suite()
   {
      return new JBossWSTestSetup(JBWS3008TestCase.class, "jaxws-jbws3008.war");
   }

   boolean initialized = false;

   protected void setUp() throws Exception
   {
      if (!this.initialized)
      {
         this.initialized = true;

         url = "http://" + getServerHost() + ":8080/jaxws-jbws3008";
         wsdlurl = new URL(url + "?wsdl");
         service = new AddNumbersService(wsdlurl, SERVICE_QNAME);
         port = service.getPort(PORT_QNAME, AddNumbers.class, wsftrue);
         bp = (BindingProvider) port;
      }
   }

   private JAXBContext createJAXBContext()
   {
      try
      {
         return JAXBContext.newInstance(JAXB_OBJECT_FACTORY);
      }
      catch (javax.xml.bind.JAXBException e)
      {
         throw new WebServiceException(e.getMessage(), e);
      }
   }

   private Dispatch<Object> createDispatchJAXB(W3CEndpointReference myepr, WebServiceFeature[] wsf) throws Exception
   {
      if (wsf == null)
         return service.createDispatch(myepr, createJAXBContext(), javax.xml.ws.Service.Mode.PAYLOAD);
      else
         return service.createDispatch(myepr, createJAXBContext(), javax.xml.ws.Service.Mode.PAYLOAD, wsf);
   }

   private Dispatch<Source> createDispatchSource(W3CEndpointReference myepr, WebServiceFeature[] wsf) throws Exception
   {
      if (wsf == null)
         return service.createDispatch(myepr, Source.class, javax.xml.ws.Service.Mode.PAYLOAD);
      else
         return service.createDispatch(myepr, Source.class, javax.xml.ws.Service.Mode.PAYLOAD, wsf);
   }

   private Dispatch<SOAPMessage> createDispatchSOAPMessage(W3CEndpointReference myepr, WebServiceFeature[] wsf)
         throws Exception
   {
      if (wsf == null)
         return service.createDispatch(myepr, SOAPMessage.class, javax.xml.ws.Service.Mode.MESSAGE);
      else
         return service.createDispatch(myepr, SOAPMessage.class, javax.xml.ws.Service.Mode.MESSAGE, wsf);
   }

   public void testEPRGetEPRViaWSCTest1() throws Exception
   {
      W3CEndpointReference epr = port.getW3CEPR1();
      String eprString = this.endpointReferenceAsString(epr);
      assertTrue("Service URL not found: ", eprString.indexOf(url) > 0);
   }

   public void testEPRGetEPRViaWSCTest2() throws Exception
   {
      W3CEndpointReference epr = port.getW3CEPR2();
      String eprString = this.endpointReferenceAsString(epr);
      assertTrue("Service URL not found: ", eprString.indexOf(url) > 0);
   }

   public void testEPRGetEPRViaBPTest1() throws Exception
   {
      W3CEndpointReference epr = (W3CEndpointReference) bp.getEndpointReference();
      String eprString = this.endpointReferenceAsString(epr);
      assertTrue("Service URL not found: ", eprString.indexOf(url) > 0);
   }

   public void testEPRGetEPRViaBPTest2() throws Exception
   {
      W3CEndpointReference epr = (W3CEndpointReference) bp.getEndpointReference(W3CEndpointReference.class);
      String eprString = this.endpointReferenceAsString(epr);
      assertTrue("Service URL not found: ", eprString.indexOf(url) > 0);
   }

   public void testEPRGetEPRViaBPWithUnsupportedEPRClassTest() throws Exception
   {
      try
      {
         bp.getEndpointReference(MyEPR.class);
         fail("Did not throw expected WebServiceException");
      }
      catch (WebServiceException e)
      {
         log.debug("Caught expected WebServiceException: " + e.getMessage());
      }
   }

   public void testEPRWriteToAndReadFromTest() throws Exception
   {
      W3CEndpointReference epr = (W3CEndpointReference) bp.getEndpointReference();
      String eprString = this.endpointReferenceAsString(epr);
      ByteArrayInputStream bais = new ByteArrayInputStream(eprString.getBytes());
      epr = (W3CEndpointReference) EndpointReference.readFrom(new StreamSource(bais));
   }

   public void testServiceGetPortViaWSCAndWSFTrueTest() throws Exception
   {
      W3CEndpointReference epr = port.getW3CEPR1();
      AddNumbers retport = (AddNumbers) service.getPort(epr, AddNumbers.class, wsftrue);
      int result = retport.doAddNumbers(10, 10);
      assertTrue(result == 20);
   }

   public void testServiceGetPortViaWSCAndWSFFalseTest() throws Exception
   {
      try
      {
         W3CEndpointReference epr = port.getW3CEPR1();
         AddNumbers retport = (AddNumbers) service.getPort(epr, AddNumbers.class, wsffalse);
         retport.doAddNumbers(10, 10);
         fail("Did not throw expected SOAPFaultException");
      }
      catch (SOAPFaultException e)
      {
         log.debug("Caught expected SOAPFaultException: " + e.getMessage());
      }
   }

   public void testServiceGetPortViaBPAndWSFTrueTest() throws Exception
   {
      W3CEndpointReference epr = (W3CEndpointReference) bp.getEndpointReference();
      AddNumbers retport = (AddNumbers) service.getPort(epr, AddNumbers.class, wsftrue);
      int result = retport.doAddNumbers(10, 10);
      assertTrue(result == 20);
   }

   public void testServiceGetPortViaBPAndWSFFalseTest() throws Exception
   {
      try
      {
         W3CEndpointReference epr = (W3CEndpointReference) bp.getEndpointReference();
         AddNumbers retport = (AddNumbers) service.getPort(epr, AddNumbers.class, wsffalse);
         retport.doAddNumbers(10, 10);
         fail("Did not throw expected SOAPFaultException");
      }
      catch (SOAPFaultException e)
      {
         log.debug("Caught expected SOAPFaultException: " + e.getMessage());
      }
   }

   public void testEPRGetPortViaWSCAndNoWSFTest() throws Exception
   {
      try
      {
         W3CEndpointReference epr = port.getW3CEPR1();
         AddNumbers retport = (AddNumbers) epr.getPort(AddNumbers.class);
         retport.doAddNumbers(10, 10);
      }
      catch (SOAPFaultException e)
      {
         log.debug("Caught expected SOAPFaultException: " + e.getMessage());
      }
   }

   public void testEPRGetPortViaWSCAndWSFTrueTest() throws Exception
   {
      W3CEndpointReference epr = port.getW3CEPR1();
      AddNumbers retport = (AddNumbers) epr.getPort(AddNumbers.class, wsftrue);
      int result = retport.doAddNumbers(10, 10);
      assertTrue(result == 20);
   }

   public void testEPRGetPortViaWSCAndWSFFalseTest() throws Exception
   {
      try
      {
         W3CEndpointReference epr = port.getW3CEPR1();
         AddNumbers retport = (AddNumbers) epr.getPort(AddNumbers.class, wsffalse);
         retport.doAddNumbers(10, 10);
         fail("Did not throw expected SOAPFaultException");
      }
      catch (SOAPFaultException e)
      {
         log.debug("Caught expected SOAPFaultException: " + e.getMessage());
      }
   }

   public void testEPRGetPortViaBPAndNoWSFTest() throws Exception
   {
      try
      {
         W3CEndpointReference epr = (W3CEndpointReference) bp.getEndpointReference();
         AddNumbers retport = (AddNumbers) epr.getPort(AddNumbers.class);
         retport.doAddNumbers(10, 10);
      }
      catch (SOAPFaultException e)
      {
         log.debug("Caught expected SOAPFaultException: " + e.getMessage());
      }
   }

   public void testEPRGetPortViaBPAndWSFTrueTest() throws Exception
   {
      W3CEndpointReference epr = (W3CEndpointReference) bp.getEndpointReference();
      AddNumbers retport = (AddNumbers) epr.getPort(AddNumbers.class, wsftrue);
      int result = retport.doAddNumbers(10, 10);
      assertTrue(result == 20);
   }

   public void testEPRGetPortViaBPAndWSFFalseTest() throws Exception
   {
      try
      {
         W3CEndpointReference epr = (W3CEndpointReference) bp.getEndpointReference();
         AddNumbers retport = (AddNumbers) epr.getPort(AddNumbers.class, wsffalse);
         retport.doAddNumbers(10, 10);
         fail("Did not throw expected SOAPFaultException");
      }
      catch (SOAPFaultException e)
      {
         log.debug("Caught expected SOAPFaultException: " + e.getMessage());
      }
   }

   public void testEPRViaWSCCreateDispatchWSFTrueAndInvokeTest1() throws Exception
   {
      W3CEndpointReference myepr = port.getW3CEPR1();
      dispatchSrc = createDispatchSource(myepr, wsftrue);
      Source requestMsg = JAXWS_Util.makeSource(doAddNumbersRequest, "StreamSource");
      Source responseMsg = dispatchSrc.invoke(requestMsg);
      String responseStr = JAXWS_Util.getDOMResultAsString(JAXWS_Util.getSourceAsDOMResult(responseMsg));
      if (responseStr.indexOf("doAddNumbersResponse") == -1 || responseStr.indexOf("return") == -1
            || responseStr.indexOf("20") == -1)
      {
         fail("Unexpected response results");
      }
   }

   public void testEPRViaWSCCreateDispatchWSFTrueAndInvokeTest2() throws Exception
   {
      W3CEndpointReference myepr = port.getW3CEPR2();
      dispatchSrc = createDispatchSource(myepr, wsftrue);
      Source requestMsg = JAXWS_Util.makeSource(doAddNumbersRequest, "StreamSource");
      Source responseMsg = dispatchSrc.invoke(requestMsg);
      String responseStr = JAXWS_Util.getDOMResultAsString(JAXWS_Util.getSourceAsDOMResult(responseMsg));
      if (responseStr.indexOf("doAddNumbersResponse") == -1 || responseStr.indexOf("return") == -1
            || responseStr.indexOf("20") == -1)
      {
         fail("Unexpected response results");
      }
   }

   public void testEPRViaWSCCreateDispatchWSFFalseAndInvokeTest3() throws Exception
   {
      try
      {
         W3CEndpointReference myepr = port.getW3CEPR1();
         dispatchSrc = createDispatchSource(myepr, wsffalse);
         Source requestMsg = JAXWS_Util.makeSource(doAddNumbersRequest, "StreamSource");
         dispatchSrc.invoke(requestMsg);
         fail("Did not throw expected WebServiceException");
      }
      catch (WebServiceException e)
      {
         log.debug("Caught expected WebServiceException: " + e.getMessage());
      }
   }

   @SuppressWarnings("unchecked")
   public void testEPRViaWSCCreateJAXBDispatchWSFTrueAndInvokeTest1() throws Exception
   {
      W3CEndpointReference myepr = port.getW3CEPR1();
      ObjectFactory of = new ObjectFactory();
      DoAddNumbers numbers = of.createDoAddNumbers();
      numbers.setArg0(10);
      numbers.setArg1(10);
      JAXBElement<DoAddNumbers> request = of.createDoAddNumbers(numbers);
      dispatchJaxb = createDispatchJAXB(myepr, wsftrue);
      JAXBElement<DoAddNumbersResponse> response = (JAXBElement<DoAddNumbersResponse>) dispatchJaxb.invoke(request);
      int result = response.getValue().getReturn();
      if (result != 20)
      {
         fail("Expected result=20, got result=" + result);
      }
   }

   public void testEPRViaWSCCreateJAXBDispatchWSFFalseAndInvokeTest2() throws Exception
   {
      try
      {
         W3CEndpointReference myepr = port.getW3CEPR1();
         ObjectFactory of = new ObjectFactory();
         DoAddNumbers numbers = of.createDoAddNumbers();
         numbers.setArg0(10);
         numbers.setArg1(10);
         JAXBElement<DoAddNumbers> request = of.createDoAddNumbers(numbers);
         dispatchJaxb = createDispatchJAXB(myepr, wsffalse);
         dispatchJaxb.invoke(request);
         fail("Did not throw expected WebServiceException");
      }
      catch (WebServiceException e)
      {
         log.debug("Caught expected WebServiceException: " + e.getMessage());
      }
   }

   public void testEPRViaBPCreateDispatchWSFTrueAndInvokeTest1() throws Exception
   {
      W3CEndpointReference myepr = (W3CEndpointReference) bp.getEndpointReference();
      dispatchSrc = createDispatchSource(myepr, wsftrue);
      Source requestMsg = JAXWS_Util.makeSource(doAddNumbersRequest, "StreamSource");
      Source responseMsg = dispatchSrc.invoke(requestMsg);
      String responseStr = JAXWS_Util.getDOMResultAsString(JAXWS_Util.getSourceAsDOMResult(responseMsg));
      if (responseStr.indexOf("doAddNumbersResponse") == -1 || responseStr.indexOf("return") == -1
            || responseStr.indexOf("20") == -1)
      {
         fail("Unexpected response results");
      }
   }

   public void testEPRViaBPCreateDispatchWSFTrueAndInvokeTest2() throws Exception
   {
      W3CEndpointReference myepr = (W3CEndpointReference) bp.getEndpointReference(W3CEndpointReference.class);
      dispatchSM = createDispatchSOAPMessage(myepr, wsftrue);
      SOAPMessage requestMsg = JAXWS_Util.makeSOAPMessage(doAddNumbersRequestSM);
      SOAPMessage responseMsg = dispatchSM.invoke(requestMsg);
      String responseStr = JAXWS_Util.getSOAPMessageAsString(responseMsg);
      if (responseStr.indexOf("doAddNumbersResponse") == -1 || responseStr.indexOf("return") == -1
            || responseStr.indexOf("20") == -1)
      {
         fail("Unexpected response results");
      }
   }

   public void testEPRViaBPCreateDispatchWSFFalseAndInvokeTest3() throws Exception
   {
      try
      {
         W3CEndpointReference myepr = (W3CEndpointReference) bp.getEndpointReference(W3CEndpointReference.class);
         dispatchSM = createDispatchSOAPMessage(myepr, wsffalse);
         SOAPMessage requestMsg = JAXWS_Util.makeSOAPMessage(doAddNumbersRequestSM);
         dispatchSM.invoke(requestMsg);
         fail("Did not throw expected WebServiceException");
      }
      catch (WebServiceException e)
      {
         log.debug("Caught expected WebServiceException: " + e.getMessage());
      }
   }

   @SuppressWarnings("unchecked")
   public void testEPRViaBPCreateJAXBDispatchWSFTrueAndInvokeTest1() throws Exception
   {
      W3CEndpointReference myepr = (W3CEndpointReference) bp.getEndpointReference(W3CEndpointReference.class);
      ObjectFactory of = new ObjectFactory();
      DoAddNumbers numbers = of.createDoAddNumbers();
      numbers.setArg0(10);
      numbers.setArg1(10);
      JAXBElement<DoAddNumbers> request = of.createDoAddNumbers(numbers);
      dispatchJaxb = createDispatchJAXB(myepr, wsftrue);
      JAXBElement<DoAddNumbersResponse> response = (JAXBElement<DoAddNumbersResponse>) dispatchJaxb.invoke(request);
      int result = response.getValue().getReturn();
      assertTrue(result == 20);
   }

   public void testEPRViaBPCreateJAXBDispatchWSFFalseAndInvokeTest2() throws Exception
   {
      try
      {
         W3CEndpointReference myepr = (W3CEndpointReference) bp.getEndpointReference(W3CEndpointReference.class);
         ObjectFactory of = new ObjectFactory();
         DoAddNumbers numbers = of.createDoAddNumbers();
         numbers.setArg0(10);
         numbers.setArg1(10);
         JAXBElement<DoAddNumbers> request = of.createDoAddNumbers(numbers);
         dispatchJaxb = createDispatchJAXB(myepr, wsffalse);
         dispatchJaxb.invoke(request);
         fail("Did not throw expected WebServiceException");
      }
      catch (WebServiceException e)
      {
         log.debug("Caught expected WebServiceException: " + e.getMessage());
      }
   }

   private String endpointReferenceAsString(final EndpointReference epr) throws Exception
   {
      DOMResult dr = new DOMResult();
      epr.writeTo(dr);
      Node endpointReferenceElement = dr.getNode();
      return DOMUtils.node2String(endpointReferenceElement);
   }
}
