/*
* JBoss, Home of Professional Open Source
*
* 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.
*/

/*
* Created on Sep 18, 2005
*/

package org.jboss.test.remoting.transport.multiplex;

import java.io.File;
import java.io.IOException;
import java.net.ConnectException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.management.MBeanServer;

import org.apache.log4j.Category;
import org.apache.log4j.FileAppender;
import org.apache.log4j.PatternLayout;
import org.jboss.jrunit.extensions.ServerTestCase;
import org.jboss.logging.Logger;
import org.jboss.remoting.InvocationRequest;
import org.jboss.remoting.InvokerLocator;
import org.jboss.remoting.ServerInvocationHandler;
import org.jboss.remoting.ServerInvoker;
import org.jboss.remoting.callback.Callback;
import org.jboss.remoting.callback.InvokerCallbackHandler;
import org.jboss.remoting.transport.Connector;

/**
 * Simple remoting server.  Uses inner class SampleInvocationHandler
 * as the invocation target handler class, which will generate
 * callback messages upon callback listeners being added.
 *
 * @author <a href="mailto:telrod@e2technologies.net">Tom Elrod</a>
 */
public class MultiplexInvokerTestServer extends ServerTestCase
{
   private static final Logger log = Logger.getLogger(MultiplexInvokerTestServer.class);
   
   // Default locator values
   private static String transport = "multiplex";
   private static String host = "localhost";
   private static int port;
   private static int virtualServerSocketTestConnectPort;

   // String to be returned from invocation handler upon client invocation calls.
   private static final String RESPONSE_VALUE = "This is the return to SampleInvocationHandler invocation";

   // counter indicating the number of callbacks generated
   private static int callbackCounter = 1;
   
   private Map configuration = new HashMap();
   
   
   public void setUp()
   {
      String pattern = "%5p [%t] (%F:%L) <%d{ABSOLUTE}> - %m%n";
      PatternLayout layout = new PatternLayout(pattern);
      
      try
      {
         File logFile = new File("test_logs");
         logFile.mkdir();
         FileAppender fileAppender = new FileAppender(layout, "test_logs" + File.separator + "invoker.server.log");
         fileAppender.setAppend(false);
         //fileAppender.setThreshold(Level.toLevel(testLogLevel));
         Category.getRoot().addAppender(fileAppender);
      }
      catch(IOException e)
      {
         e.printStackTrace();
      }
      
      setUpTests();
   }
   

   public void tearDown() throws Exception
   {
   }
   
   protected int getPort()
   {
	   return 7070;
   }
   
   protected int getVirtualServerSocketTestConnectPort()
   {
	   return 9090;
   }
   
   protected String getTransport()
   {
      return transport;
   }
   
   protected Map getConfiguration()
   {
      return configuration;
   }
   
   
   protected void setUpTests()
   {
      port = getPort();
      virtualServerSocketTestConnectPort = getVirtualServerSocketTestConnectPort();
	   
      // timeout - 7070
      String locatorURI = getTransport() + "://" + host + ":" + port + "/?timeout=1200000";
      TestThread t1 = new TestThread(locatorURI, true);
      t1.start();
      
      // case 1a - 7071
      locatorURI = getTransport() + "://" + host + ":" + ++port + "/?timeout=1200000";
      new TestThread(locatorURI, false).start();
      
      // case 1b - 7072
      locatorURI = getTransport() + "://" + host + ":" + ++port + "/?timeout=1200000";
      new TestThread(locatorURI, false).start();
      
      // case 2a - 7073
      locatorURI = getTransport() + "://" + host + ":" + ++port + "/?timeout=1200000";
      new TestThread(locatorURI, false).start();
      
      // case 2b - 7074
      locatorURI = getTransport() + "://" + host + ":" + ++port + "/?timeout=1200000";
      new TestThread(locatorURI, false).start();
      
      // case 3a - 7075
      locatorURI = getTransport() + "://" + host + ":" + ++port + "/?timeout=1200000";
      new TestThread(locatorURI, false).start();
      
      // case 3b - 7076
      locatorURI = getTransport() + "://" + host + ":" + ++port + "/?timeout=1200000";
      new TestThread(locatorURI, false).start();
      
      // case 4a - 7077
      locatorURI = getTransport() + "://" + host + ":" + ++port + "/?timeout=1200000";
      TestThread t8 = new TestThread(locatorURI, false);
      t8.start();
      
      // case 4b - 7078
      locatorURI = getTransport() + "://" + host + ":" + ++port + "/?timeout=1200000";
      TestThread t9 = new TestThread(locatorURI, false);
      t9.start();
      
      
      // testInconsistentClientParameters() - 7079
      locatorURI = getTransport() + "://" + host + ":" + ++port + "/?timeout=1200000";
      TestThread t10 = new TestThread(locatorURI, false);
      t10.start();
      
      
      // testInconsistentServerParameters() - 7080
      locatorURI = getTransport() + "://" + host + ":" + ++port + "/?timeout=1200000";
      TestThread t11 = new TestThread(locatorURI, false);
      t11.start();
      
      // case 1a - VSS - 7081
      locatorURI = getTransport() + "://" + host + ":" + ++port + 
                               "/?multiplexConnectHost=localhost&multiplexConnectPort=" + virtualServerSocketTestConnectPort +
                               "&timeout=1200000";
      new VirtualServerSocketTestThread(locatorURI, false).start();
   }
  

   /**
    * Can pass transport and port to be used as parameters.
    * Valid transports are 'rmi' and 'socket'.
    *
    * @param args
    */
   public static void main(String[] args)
   {
      if(args != null && args.length == 2)
      {
         transport = args[0];
         port = Integer.parseInt(args[1]);
      }
      
      new MultiplexInvokerTestServer().setUpTests();
   }

   
   protected Connector getConnector(InvokerLocator locator, ServerInvocationHandler handler)
   throws Exception
   {
      Connector connector = new Connector(getConfiguration());
      connector.setInvokerLocator(locator.getLocatorURI());
      connector.create();
      connector.addInvocationHandler("sample", handler);
      return connector;
   }
   
   
   protected class TestThread extends Thread
   {
      private String locatorURI;
      private boolean doTimeoutTest;

      // remoting server connector
      private Connector connector = null;

      private SampleInvocationHandler invocationHandler;
      
      
      public TestThread(String locatorURI, boolean doTimeoutTest)
      {
         this.locatorURI = locatorURI;
         this.doTimeoutTest = doTimeoutTest;
      }
      
      
      public void run()
      {
         try
         {
            setupServer(locatorURI, doTimeoutTest);

            for (int i = 0; i < 240; i++)
            {
               Thread.sleep(500);

               if(invocationHandler.getInvocationCount() == 5)
               {
                  break;
               }
            }

            shutdownServer();

         }
         catch(Exception e)
         {
            e.printStackTrace();
         }
      }

      
      /**
       * Sets up target remoting server.
       * @param locatorURI
       * @param doTimeoutTest TODO
       *
       * @throws Exception
       */
      public void setupServer(String locatorURI, boolean doTimeoutTest) throws Exception
      {
         InvokerLocator locator = new InvokerLocator(locatorURI);
         System.out.println("Starting remoting server with locator uri of: " + locatorURI);
         log.info("Starting remoting server with locator uri of: " + locatorURI);
         invocationHandler = new SampleInvocationHandler(doTimeoutTest);
         connector = getConnector(locator, invocationHandler);
         
         for (int i = 0; i < 10; i++)
         {
            try
            {
               connector.start();
               break;
            }
            catch (ConnectException e)
            {
               log.info("got ConnectException: trying again");
               
               try
               {
                  Thread.sleep(5000);
               }
               catch (InterruptedException ignored)
               {
               }
            }
         }



         System.out.println("Started remoting server with locator uri of: " + locatorURI);
         log.info("Started remoting server with locator uri of: " + locatorURI);
      }


      /**
       * Shuts down the server
       */
      public void shutdownServer()
      {
         connector.stop();
         connector.destroy();
         System.out.println("shutting down thread: " + locatorURI);
      }
   }
   
   
   protected class VirtualServerSocketTestThread extends TestThread
   {
      public VirtualServerSocketTestThread(String locatorURI, boolean doTimeoutTest)
      {
         super(locatorURI, doTimeoutTest);
      }
      
      public void run()
      {
         try
         {
            // synchronize with client
            ServerSocket ss = new ServerSocket(virtualServerSocketTestConnectPort + 1);
            Socket s = ss.accept();
            s.close();
            ss.close();
         }
         catch (IOException e)
         {
            e.printStackTrace();
            fail();
         }

         super.run();
      }
   }

   /**
    * Simple invocation handler implementation.  When callback client's are registered, will
    * generate callbacks periodically.
    */
   public static class SampleInvocationHandler implements ServerInvocationHandler
   {
      // list of callback listeners registered
      private List listeners = new ArrayList();

      // flag to indicate when should generate callback messages
      private boolean shouldGenerateCallbacks = false;

      int invocations = 0;
      boolean doTimeoutTest;

      public SampleInvocationHandler(boolean doTimeoutTest)
      {
         this.doTimeoutTest = doTimeoutTest;
      }

      public int getInvocationCount()
      {
         return invocations;
      }

      /**
       * called by the remoting server to handle the invocation from client.
       *
       * @param invocation
       * @return
       * @throws Throwable
       */
      public Object invoke(InvocationRequest invocation) throws Throwable
      {
         // Print out the invocation request
         System.out.println("Invocation request is: " + invocation.getParameter());

         if(invocations++ > 0)
         {
            return new Integer(0);
         }
         else
         {
            if (doTimeoutTest)
            {
               // test socket timeout on client side
               try
               {
                  Thread.sleep(10000);
               }
               catch(Exception e)
               {
               }
            }

            return new Integer(1);
         }
      }

      /**
       * Adds a callback handler that will listen for callbacks from
       * the server invoker handler.
       *
       * @param callbackHandler
       */
      public void addListener(InvokerCallbackHandler callbackHandler)
      {
         System.out.println("entering addListener()");

         try
         {
            Callback callback = new Callback(new Integer(1));
            callbackHandler.handleCallback(callback);
            System.out.println("sent first callback");
            callback = new Callback(new Integer(2));
            callbackHandler.handleCallback(callback);
            System.out.println("sent second callback");
         }
         catch(Exception e)
         {
            e.printStackTrace();
         }
      }

      /**
       * Removes the callback handler that was listening for callbacks
       * from the server invoker handler.
       *
       * @param callbackHandler
       */
      public void removeListener(InvokerCallbackHandler callbackHandler)
      {
         invocations++;
      }


      /**
       * set the mbean server that the handler can reference
       *
       * @param server
       */
      public void setMBeanServer(MBeanServer server)
      {
         // NO OP as do not need reference to MBeanServer for this handler
      }

      /**
       * set the invoker that owns this handler
       *
       * @param invoker
       */
      public void setInvoker(ServerInvoker invoker)
      {
         // NO OP as do not need reference back to the server invoker
      }
   }
}