/*
 * JBoss, Home of Professional Open Source
 * Copyright 2005, JBoss Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt 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.smoke.tools;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;

import javax.xml.ws.WebServiceClient;
import javax.xml.ws.WebServiceFeature;

import junit.framework.TestCase;

import org.jboss.wsf.spi.tools.WSContractConsumer;

/**
 * Test the WSContractConsumer API across different implementations.
 * NOTE: All tests expect to be executed below 'output/tests'.
 * 
 * @author Heiko.Braun@jboss.com
 * @version $Revision: 9490 $
 */
public class WSConsumerTestCase extends TestCase
{

   // Tools delegate. Recreated for every test. See setup(...)
   WSContractConsumer consumer;

   // common output dir for all tests. Tests need to be executed below 'output/tests'
   File outputDirectory;

   // default is off
   boolean toogleMessageOut = false;

   /**
    * Recreates a tools delegate for every test
    * @throws Exception
    */
   protected void setUp() throws Exception
   {
      super.setUp();

      // create a new consumer for every test case
      consumer = WSContractConsumer.newInstance();
      if(toogleMessageOut) consumer.setMessageStream(System.out);

      // shared output directory, relative to test execution
      outputDirectory = new File("wsconsume/java");
   }

   /**
    * Specifies the JAX-WS and JAXB binding files to use on import operations.
    * See http://java.sun.com/webservices/docs/2.0/jaxws/customizations.html
    */
   public void testBindingFiles() throws Exception
   {
      List<File> files = new ArrayList<File>();
      files.add( new File("resources/jaxws/smoke/tools/wsdl/async-binding.xml") );
      
      consumer.setBindingFiles(files);
      consumer.setTargetPackage("org.jboss.test.ws.tools.testBindingFiles");
      consumer.setGenerateSource(true);      

      consumeWSDL();

      URLClassLoader loader = new URLClassLoader(new URL[] { new URL("file:"+System.getProperty("user.dir")+"/wsconsume/java/") });
      String seiClassName = "org.jboss.test.ws.tools.testBindingFiles.EndpointInterface";
      Class sei = loader.loadClass(seiClassName);

      boolean containsAsyncOperations = false;
      for(Method m : sei.getDeclaredMethods())
      {
         if(m.getName().equals("echoAsync"))
         {
            containsAsyncOperations = true;
            break;
         }
      }

      assertTrue("External binding file was ignored", containsAsyncOperations);

   }

   /**
    * Sets the OASIS XML Catalog file to use for entity resolution.
    *
    */
   public void testCatalog() throws Exception
   {
      consumer.setTargetPackage("org.jboss.test.ws.tools.testCatalog");
      consumer.setCatalog( new File("resources/jaxws/spi/tools/wsdl/jax-ws-catalog.xml") );
      consumer.setGenerateSource(true);
      consumer.setOutputDirectory(outputDirectory);
      consumer.consume("resources/jaxws/smoke/tools/wsdl/TestServiceCatalog.wsdl");
   }

   /**
    * Sets the main output directory. If the directory does not exist, it will be created.
    *
    */
   public void testOutputDirectory() throws Exception
   {
      consumer.setTargetPackage("org.jboss.test.ws.tools.testOutputDirectory");
      consumer.setGenerateSource(true);
      consumer.setSourceDirectory( new File("work/testOutputDirectory/java/") );
   
      consumeWSDL();

      File sei = new File("work/testOutputDirectory/java/org/jboss/test/ws/tools/testOutputDirectory/EndpointInterface.java");
      assertTrue("Output directory switch ignored", sei.exists());
   }

   /**
    * Sets the source directory. This directory will contain any generated Java source.
    * If the directory does not exist, it will be created. If not specified,
    * the output directory will be used instead.
    *
    */
   public void testSourceDirectory() throws Exception
   {
      consumer.setTargetPackage("org.jboss.test.ws.tools.testSourceDirectory");      
      consumer.setGenerateSource(true);
      consumer.setSourceDirectory( new File("work/wsconsumeSource/java/") );

      consumeWSDL();

      File sei = new File("work/wsconsumeSource/java/org/jboss/test/ws/tools/testSourceDirectory/EndpointInterface.java");
      assertTrue("Source directory switch ignored", sei.exists());
   }

   /**
    * Enables/Disables Java source generation.
    *
    */
   public void testGenerateSource() throws Exception
   {
      testTargetPackage();
   }

   /**
    * Sets the target package for generated source. If not specified the default
    * is based off of the XML namespace.
    *    
    */
   public void testTargetPackage() throws Exception
   {
      consumer.setTargetPackage("org.jboss.test.ws.tools.testTargetPackage");      
      consumer.setGenerateSource(true);

      consumeWSDL();

      File packageDir = new File("wsconsume/java/org/jboss/test/ws/tools/testTargetPackage");
      assertTrue("Package not created", packageDir.exists());

      File sei = new File("wsconsume/java/org/jboss/test/ws/tools/testTargetPackage/EndpointInterface.java");
      assertTrue("SEI not generated", sei.exists());
   }

   /**
    * Sets the @@WebService.wsdlLocation and @@WebServiceClient.wsdlLocation attributes to a custom value.
    *
    */
   public void testWsdlLocation() throws Exception
   {
      consumer.setTargetPackage("org.jboss.test.ws.tools.testWsdlLocation");
      consumer.setWsdlLocation("http://foo.bar.com/endpoint?wsdl");
      consumer.setGenerateSource(true);
      
      consumeWSDL();
                             
      URLClassLoader loader = new URLClassLoader(new URL[] { new URL("file:"+System.getProperty("user.dir")+"/wsconsume/java/") });
      String seiClassName = "org.jboss.test.ws.tools.testWsdlLocation.TestService";
      Class sei = loader.loadClass(seiClassName);

      WebServiceClient webServiceClient = (WebServiceClient) sei.getAnnotation(WebServiceClient.class);
      assertNotNull("@WebServiceClient not generated on service interface", webServiceClient);
      assertEquals("@WebServiceClient.wsdlLocation not set", "http://foo.bar.com/endpoint?wsdl", webServiceClient.wsdlLocation());
   }

   /**
    * Sets the PrintStream to use for status feedback.
    * The simplest example would be to use System.out.      
    */
   public void testMessageStream() throws Exception
   {
      ByteArrayOutputStream bout = new ByteArrayOutputStream();
      PrintStream pout = new PrintStream(bout);

      consumer.setTargetPackage("org.jboss.test.ws.tools.testMessageStream");
      consumer.setMessageStream(pout);
      
      consumeWSDL();

      String messageOut = new String (bout.toByteArray());
      System.out.println("-- Begin captured output -- ");
      System.out.println(messageOut);
      System.out.println("--- End captured output --");

      System.out.println("FIXME [JBWS-1772]: WSConsume output is not correctly redirected");
      
      /*assertTrue("Tools output not correctly redirected",
        messageOut.indexOf("org/jboss/test/ws/tools/testMessageStream/EndpointInterface.java")!=-1
      );*/
   }

   /**
    * Sets the additional classpath to use if/when invoking the Java compiler.
    * Typically an implementation will use the system <code>java.class.path</code>
    * property. So for most normal applications this method is not needed. However,
    * if this API is being used from an isolated classloader, then it needs to
    * be called in order to reference all jars that are required by the
    * implementation.
    *
    */
   public void testAdditionalCompilerClassPath()
   {
      System.out.println("FIXME [JBWS-1773]: Verify isolated classloading with WSConsume");
   }

   /**
    * Set the target JAX-WS specification target. Defaults to <code>2.0</code>
    */
   public void testTarget() throws Exception
   {
      consumer.setTargetPackage("org.jboss.test.ws.tools.testTarget");      
      consumer.setGenerateSource(true);
      consumer.setTarget("2.1");
      
      consumeWSDL();

      URLClassLoader loader = new URLClassLoader(new URL[] { new URL("file:"+System.getProperty("user.dir")+"/wsconsume/java/") });
      String seiClassName = "org.jboss.test.ws.tools.testTarget.TestService";
      Class sei = loader.loadClass(seiClassName);


      boolean featureSig = false;
      for(Method m : sei.getDeclaredMethods())
      {
         if(m.getName().equals("getEndpointInterfacePort"))
         {
            for(Class c : m.getParameterTypes())
            {
               if(c.isArray() &&
                 c.getComponentType().equals(WebServiceFeature.class)) 
               {
                  featureSig = true;
                  break;
               }
            }
         }
      }

      assertTrue("JAX-WS 2.1 extensions not generated with 'target=2.1'", featureSig);
     
   }

   /**
    * Tests the SOAP 1.2 binding extension
    *
    */
   public void testSOAP12Extension() throws Exception
   {
      consumer.setOutputDirectory(outputDirectory);
      consumer.setTargetPackage("org.jboss.test.ws.tools.testSOAP12Extension");
      consumer.setGenerateSource(true);
      consumer.setExtension(true);
      consumer.consume("resources/jaxws/smoke/tools/wsdl/TestServiceSoap12.wsdl");
      File sei = new File("wsconsume/java/org/jboss/test/ws/tools/testSOAP12Extension/EndpointInterface.java");
      assertTrue("SEI not generated", sei.exists());
      File service = new File("wsconsume/java/org/jboss/test/ws/tools/testSOAP12Extension/TestService.java");
      assertTrue("Service not generated", service.exists());
   }

   private void consumeWSDL() throws Exception    
   {      
      consumer.setOutputDirectory(outputDirectory);
      consumer.consume("resources/jaxws/smoke/tools/wsdl/TestService.wsdl");
   }

}
