/*
 * 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.jbws3022;

import java.net.URL;
import java.text.MessageFormat;
import java.util.UUID;

import javax.xml.namespace.QName;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.soap.AddressingFeature;
import javax.xml.ws.soap.SOAPFaultException;

import junit.framework.Test;

import org.jboss.test.ws.jaxws.common.JAXWS_Util;
import org.jboss.test.ws.jaxws.common.W3CAddressingConstants;
import org.jboss.test.ws.jaxws.common.WsaSOAPUtils;
import org.jboss.test.ws.jaxws.jbws3022.generated.AddNumbersPortType3;
import org.jboss.test.ws.jaxws.jbws3022.generated.AddNumbersPortType4;
import org.jboss.test.ws.jaxws.jbws3022.generated.AddNumbersService3;
import org.jboss.test.ws.jaxws.jbws3022.generated.AddNumbersService4;
import org.jboss.wsf.test.JBossWSTest;
import org.jboss.wsf.test.JBossWSTestSetup;

/**
 * [JBWS-3022] Respect @Addressing.responses attribute test.
 *
 * @author <a href="mailto:ropalka@redhat.com">Richard Opalka</a>
 */
public class JBWS3022TestCase extends JBossWSTest
{

   private static final String NAMESPACEURI = "http://example.com/";

   private static final String ANONYMOUS_PORT_NAME = "AddNumbersPort3";

   private static final String NON_ANONYMOUS_PORT_NAME = "AddNumbersPort4";

   private static QName ANONYMOUS_PORT_QNAME = new QName(NAMESPACEURI, ANONYMOUS_PORT_NAME);

   private static QName NON_ANONYMOUS_PORT_QNAME = new QName(NAMESPACEURI, NON_ANONYMOUS_PORT_NAME);

   private static AddressingFeature DISABLED_ADDRESSING_FEATURE = new AddressingFeature(false);

   private String anonymousURL = null;

   private String nonAnonymousURL = null;

   private URL anonymousWSDL = null;

   private URL nonAnonymousWSDL = null;

   AddNumbersPortType3 anonymousPort = null;

   AddNumbersPortType4 nonAnonymousPort = null;

   static AddNumbersService3 anonymousService = null;

   static AddNumbersService4 nonAnonymousService = null;

   String replyToHeaderForAnonymousResponsesSoapMsg = "<?xml version=\"1.0\" ?><S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\"><S:Header><To xmlns=\"http://www.w3.org/2005/08/addressing\">{0}</To><MessageID xmlns=\"http://www.w3.org/2005/08/addressing\">uuid:{1}</MessageID><ReplyTo xmlns=\"http://www.w3.org/2005/08/addressing\"><Address>{2}</Address></ReplyTo><Action xmlns=\"http://www.w3.org/2005/08/addressing\">http://example.com/AddNumbersPortType3/add</Action></S:Header><S:Body><addNumbers xmlns=\"http://example.com/\"><number1>10</number1><number2>10</number2><testName>testAnonymousResponsesReplyToHeader</testName></addNumbers></S:Body></S:Envelope>";

   String faultToHeaderForAnonymousResponsesSoapMsg = "<?xml version=\"1.0\" ?><S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\"><S:Header><To xmlns=\"http://www.w3.org/2005/08/addressing\">{0}</To><MessageID xmlns=\"http://www.w3.org/2005/08/addressing\">uuid:{1}</MessageID><FaultTo xmlns=\"http://www.w3.org/2005/08/addressing\"><Address>{2}</Address></FaultTo><ReplyTo xmlns=\"http://www.w3.org/2005/08/addressing\"><Address>{3}</Address></ReplyTo><Action xmlns=\"http://www.w3.org/2005/08/addressing\">http://example.com/AddNumbersPortType3/add</Action></S:Header><S:Body><addNumbers xmlns=\"http://example.com/\"><number1>-10</number1><number2>-10</number2><testName>testAnonymousResponsesFaultToHeader</testName></addNumbers></S:Body></S:Envelope>";

   String replyToHeaderForNonAnonymousResponsesSoapMsg = "<?xml version=\"1.0\" ?><S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\"><S:Header><To xmlns=\"http://www.w3.org/2005/08/addressing\">{0}</To><MessageID xmlns=\"http://www.w3.org/2005/08/addressing\">uuid:{1}</MessageID><ReplyTo xmlns=\"http://www.w3.org/2005/08/addressing\"><Address>{2}</Address></ReplyTo><Action xmlns=\"http://www.w3.org/2005/08/addressing\">http://example.com/AddNumbersPortType4/add</Action></S:Header><S:Body><addNumbers xmlns=\"http://example.com/\"><number1>10</number1><number2>10</number2><testName>testNonAnonymousResponsesReplyToHeader</testName></addNumbers></S:Body></S:Envelope>";

   String faultToHeaderForNonAnonymousResponsesSoapMsg = "<?xml version=\"1.0\" ?><S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\"><S:Header><To xmlns=\"http://www.w3.org/2005/08/addressing\">{0}</To><MessageID xmlns=\"http://www.w3.org/2005/08/addressing\">uuid:{1}</MessageID><FaultTo xmlns=\"http://www.w3.org/2005/08/addressing\"><Address>{2}</Address></FaultTo><ReplyTo xmlns=\"http://www.w3.org/2005/08/addressing\"><Address>{3}</Address></ReplyTo><Action xmlns=\"http://www.w3.org/2005/08/addressing\">http://example.com/AddNumbersPortType4/add</Action></S:Header><S:Body><addNumbers xmlns=\"http://example.com/\"><number1>-10</number1><number2>-10</number2><testName>testNonAnonymousResponsesFaultToHeader</testName></addNumbers></S:Body></S:Envelope>";

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

   protected void setUp() throws Exception
   {
      anonymousURL = "http://" + getServerHost() + ":8080/jaxws-jbws3022/jaxws/AddNumbers3";
      anonymousWSDL = new URL(anonymousURL + "?wsdl");
      anonymousService = new AddNumbersService3(anonymousWSDL);
      nonAnonymousURL = "http://" + getServerHost() + ":8080/jaxws-jbws3022/jaxws/AddNumbers4";
      nonAnonymousWSDL = new URL(nonAnonymousURL + "?wsdl");
      nonAnonymousService = new AddNumbersService4(nonAnonymousWSDL);
      anonymousPort = (AddNumbersPortType3) anonymousService.getPort(ANONYMOUS_PORT_QNAME, AddNumbersPortType3.class);
      nonAnonymousPort = (AddNumbersPortType4) nonAnonymousService.getPort(NON_ANONYMOUS_PORT_QNAME,
            AddNumbersPortType4.class);
   }

   private Dispatch<SOAPMessage> createDispatchSOAPMessage(Service service, QName port) throws Exception
   {
      return service.createDispatch(port, SOAPMessage.class, javax.xml.ws.Service.Mode.MESSAGE,
            DISABLED_ADDRESSING_FEATURE);
   }

   public void testAnonymousResponsesAssertion() throws Exception
   {
      anonymousPort.addNumbers(10, 10, "testAnonymousResponsesAssertion");
   }

   public void testNonAnonymousResponsesAssertion() throws Exception
   {
      try
      {
         nonAnonymousPort.addNumbers(10, 10, "testNonAnonymousResponsesAssertion");
         fail();
      }
      catch (SOAPFaultException e)
      {
         log.debug("Caught SOAPFaultException ignore: " + e.getMessage());
      }
      catch (WebServiceException e)
      {
         log.debug("Caught WebServiceException ignore: " + e.getMessage());
      }
   }

   public void testAnonymousResponsesWithReplyToSetToNone() throws Exception
   {
      try
      {
         String soapMsg = MessageFormat.format(replyToHeaderForAnonymousResponsesSoapMsg, anonymousURL, UUID
               .randomUUID(), W3CAddressingConstants.WSA_NONE_ADDRESS);
         Dispatch<SOAPMessage> dispatchSM = createDispatchSOAPMessage(anonymousService, ANONYMOUS_PORT_QNAME);
         SOAPMessage request = JAXWS_Util.makeSOAPMessage(soapMsg);
         dispatchSM.invoke(request);
         fail();
      }
      catch (SOAPFaultException e)
      {
         log.debug("Caught SOAPFaultException ignore: " + e.getMessage());
      }
      catch (WebServiceException e)
      {
         log.debug("Caught WebServiceException ignore: " + e.getMessage());
      }
   }

   public void testAnonymousResponsesWithFaultToSetToNone() throws Exception
   {
      try
      {
         String soapMsg = MessageFormat.format(faultToHeaderForAnonymousResponsesSoapMsg, anonymousURL, UUID
               .randomUUID(), W3CAddressingConstants.WSA_NONE_ADDRESS, W3CAddressingConstants.WSA_ANONYMOUS_ADDRESS);
         Dispatch<SOAPMessage> dispatchSM = createDispatchSOAPMessage(anonymousService, ANONYMOUS_PORT_QNAME);
         SOAPMessage request = JAXWS_Util.makeSOAPMessage(soapMsg);
         dispatchSM.invoke(request);
         fail();
      }
      catch (SOAPFaultException e)
      {
         log.debug("Caught SOAPFaultException ignore: " + e.getMessage());
      }
      catch (WebServiceException e)
      {
         log.debug("Caught WebServiceException ignore: " + e.getMessage());
      }
   }

   public void testNonAnonymousResponsesWithReplyToSetToNone() throws Exception
   {
      try
      {
         String soapMsg = MessageFormat.format(replyToHeaderForNonAnonymousResponsesSoapMsg, nonAnonymousURL, UUID
               .randomUUID(), W3CAddressingConstants.WSA_NONE_ADDRESS);
         Dispatch<SOAPMessage> dispatchSM = createDispatchSOAPMessage(nonAnonymousService, NON_ANONYMOUS_PORT_QNAME);
         SOAPMessage request = JAXWS_Util.makeSOAPMessage(soapMsg);
         dispatchSM.invoke(request);
         fail();
      }
      catch (SOAPFaultException e)
      {
         log.debug("Caught SOAPFaultException ignore: " + e.getMessage());
      }
      catch (WebServiceException e)
      {
         log.debug("Caught WebServiceException ignore: " + e.getMessage());
      }
   }

   public void testNonAnonymousResponsesWithFaultToSetToNone() throws Exception
   {
      try
      {
         String soapMsg = MessageFormat.format(faultToHeaderForNonAnonymousResponsesSoapMsg, nonAnonymousURL, UUID
               .randomUUID(), W3CAddressingConstants.WSA_NONE_ADDRESS, W3CAddressingConstants.WSA_ANONYMOUS_ADDRESS);
         Dispatch<SOAPMessage> dispatchSM = createDispatchSOAPMessage(nonAnonymousService, NON_ANONYMOUS_PORT_QNAME);
         SOAPMessage request = JAXWS_Util.makeSOAPMessage(soapMsg);
         dispatchSM.invoke(request);
         fail();
      }
      catch (SOAPFaultException e)
      {
         log.debug("Caught SOAPFaultException ignore: " + e.getMessage());
      }
      catch (WebServiceException e)
      {
         log.debug("Caught WebServiceException ignore: " + e.getMessage());
      }
   }

   public void testOnlyAnonymousAddressSupportedFaultBadReplyTo() throws Exception
   {
      try
      {
         String soapMsg = MessageFormat.format(replyToHeaderForAnonymousResponsesSoapMsg, anonymousURL, UUID
               .randomUUID(), anonymousURL + "/badurl");
         Dispatch<SOAPMessage> dispatchSM = createDispatchSOAPMessage(anonymousService, ANONYMOUS_PORT_QNAME);
         SOAPMessage request = JAXWS_Util.makeSOAPMessage(soapMsg);
         dispatchSM.invoke(request);
         fail();
      }
      catch (SOAPFaultException e)
      {
         this.assertFaultCode(e, true);
         this.assertProblemHeaderQName(e);
      }
   }

   public void testOnlyAnonymousAddressSupportedFaultBadFaultTo() throws Exception
   {
      try
      {
         String soapMsg = MessageFormat.format(faultToHeaderForAnonymousResponsesSoapMsg, anonymousURL, UUID
               .randomUUID(), anonymousURL + "/badurl", W3CAddressingConstants.WSA_ANONYMOUS_ADDRESS);
         Dispatch<SOAPMessage> dispatchSM = createDispatchSOAPMessage(anonymousService, ANONYMOUS_PORT_QNAME);
         SOAPMessage request = JAXWS_Util.makeSOAPMessage(soapMsg);
         dispatchSM.invoke(request);
         fail();
      }
      catch (SOAPFaultException e)
      {
         this.assertFaultCode(e, true);
         this.assertProblemHeaderQName(e);
      }
   }

   public void testOnlyNonAnonymousAddressSupportedFaultBadReplyTo() throws Exception
   {
      try
      {
         String soapMsg = MessageFormat.format(replyToHeaderForNonAnonymousResponsesSoapMsg, nonAnonymousURL, UUID
               .randomUUID(), W3CAddressingConstants.WSA_ANONYMOUS_ADDRESS);
         Dispatch<SOAPMessage> dispatchSM = createDispatchSOAPMessage(nonAnonymousService, NON_ANONYMOUS_PORT_QNAME);
         SOAPMessage request = JAXWS_Util.makeSOAPMessage(soapMsg);
         dispatchSM.invoke(request);
         fail();
      }
      catch (SOAPFaultException e)
      {
         this.assertFaultCode(e, false);
         this.assertProblemHeaderQName(e);
      }
   }

   public void testOnlyNonAnonymousAddressSupportedFaultBadFaultTo() throws Exception
   {
      try
      {
         String soapMsg = MessageFormat.format(faultToHeaderForNonAnonymousResponsesSoapMsg, nonAnonymousURL, UUID
               .randomUUID(), W3CAddressingConstants.WSA_ANONYMOUS_ADDRESS, anonymousURL + "/badurl");
         Dispatch<SOAPMessage> dispatchSM = createDispatchSOAPMessage(nonAnonymousService, NON_ANONYMOUS_PORT_QNAME);
         SOAPMessage request = JAXWS_Util.makeSOAPMessage(soapMsg);
         dispatchSM.invoke(request);
         fail();
      }
      catch (SOAPFaultException e)
      {
         this.assertFaultCode(e, false);
         this.assertProblemHeaderQName(e);
      }
   }

   private void assertProblemHeaderQName(final SOAPFaultException exception) throws SOAPException
   {
      String faultdetail[] = WsaSOAPUtils.getFaultDetail(exception);
      if (faultdetail != null)
      {
         final String msg = "FaultDetail contains unexpected value got: " + faultdetail[0]
               + ", expected: ProblemHeaderQName";
         assertTrue(msg, WsaSOAPUtils.isProblemHeaderQNameFaultDetail(faultdetail[0]));
      }
   }

   private void assertFaultCode(final SOAPFaultException exception, final boolean anonymous)
         throws SOAPException
   {
      final String faultCode = WsaSOAPUtils.getFaultCode(exception);

      if (anonymous)
      {
         final String msg = "SOAPFault contains unexpected faultcode got: " + faultCode
               + ", expected: OnlyAnonymousAddressSupported";
         assertTrue(msg, WsaSOAPUtils.isOnlyAnonymousAddressSupportedFaultCode(exception));
      }
      else
      {
         final String msg = "SOAPFault contains unexpected faultcode got: " + faultCode
               + ", expected: OnlyNonAnonymousAddressSupported";
         assertTrue(msg, WsaSOAPUtils.isOnlyNonAnonymousAddressSupportedFaultCode(exception));
      }
   }
}
