/*
 * JBoss, Home of Professional Open Source
 * Copyright 2006, 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.remoting.transport.multiplex.config;

import java.io.ByteArrayOutputStream;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import org.jboss.remoting.transport.Connector;
import org.jboss.remoting.transport.multiplex.InputMultiplexor;
import org.jboss.remoting.transport.multiplex.Multiplex;
import org.jboss.remoting.transport.multiplex.MultiplexServerInvoker;
import org.jboss.remoting.transport.multiplex.MultiplexingManager;
import org.jboss.remoting.transport.multiplex.OutputMultiplexor;
import org.jboss.remoting.transport.multiplex.VirtualServerSocket;

/**
 * 
 * @author <a href="mailto:ron.sigal@jboss.com">Ron Sigal</a>
 * @version $Revision$
 * <p>
 * Copyright (c) May 19, 2006
 * </p>
 */
public class ConfigurationByMapDiffStaticObjectsTestClient extends ConfigurationTestClient
{

   /**
    *  In this test a Connector configured with a configuration Map
    *  is started and the parameters of the MultiplexingManager,
    *  InputMultiplexor, and OutputMultiplexor are tested.  Then another
    *  Connector with different parameters is started in the same JVM 
    *  and the parameters are tested again.  Since the InputMultiplexor and
    *  OutputMultiplexor are new, their parameters should have update values..
    */
   public void testConfigurationByMapDifferentStaticObjects()
   {
      log.info("entering " + getName());
      
      try
      {
         // Create HashMap with parameters.
         HashMap conf = new HashMap();
         
         // MultiplexingManager parameters
         conf.put(Multiplex.STATIC_THREADS_MONITOR_PERIOD,
                  new Integer(2 + Multiplex.STATIC_THREADS_MONITOR_PERIOD_DEFAULT));
         conf.put(Multiplex.SHUTDOWN_REQUEST_TIMEOUT,
                  new Integer(3 + Multiplex.SHUTDOWN_REQUEST_TIMEOUT_DEFAULT));
         conf.put(Multiplex.SHUTDOWN_REFUSALS_MAXIMUM,
                  new Integer(4 + Multiplex.SHUTDOWN_REFUSALS_MAXIMUM_DEFAULT));
         conf.put(Multiplex.SHUTDOWN_MONITOR_PERIOD,
                  new Integer(5 + Multiplex.SHUTDOWN_MONITOR_PERIOD_DEFAULT));
         
         // InputMultiplexor parameters
         conf.put(Multiplex.INPUT_BUFFER_SIZE,
                  new Integer(2 + Multiplex.INPUT_BUFFER_SIZE_DEFAULT));
         conf.put(Multiplex.INPUT_MAX_ERRORS,
                  new Integer(3 + Multiplex.INPUT_MAX_ERRORS_DEFAULT));
         
         // OutputMultiplexor parameters
         conf.put(Multiplex.OUTPUT_MESSAGE_POOL_SIZE,
                  new Integer(2 + Multiplex.OUTPUT_MESSAGE_POOL_SIZE_DEFAULT));
         conf.put(Multiplex.OUTPUT_MESSAGE_SIZE,
                  new Integer(3 + Multiplex.OUTPUT_MESSAGE_SIZE_DEFAULT));
         conf.put(Multiplex.OUTPUT_MAX_CHUNK_SIZE,
                  new Integer(4 + Multiplex.OUTPUT_MAX_CHUNK_SIZE_DEFAULT));
         conf.put(Multiplex.OUTPUT_MAX_TIME_SLICE,
                  new Integer(5 + Multiplex.OUTPUT_MAX_TIME_SLICE_DEFAULT));
         conf.put(Multiplex.OUTPUT_MAX_DATA_SLICE,
                  new Integer(6 + Multiplex.OUTPUT_MAX_DATA_SLICE_DEFAULT));
         
         // MultiplexServerInvoker
         conf.put(Multiplex.MAX_ACCEPT_ERRORS,
                  new Integer(2 + Multiplex.MAX_ACCEPT_ERRORS_DEFAULT));
         
         log.info("configuration: " + conf);
         
         // Create Connector.
         Connector connector1 = new Connector(ConfigurationTestServer.connectorURI3, conf);
         connector1.create();
         connector1.start();
         log.info("started Connector on: " + connector1.getInvokerLocator());
         
         // Tell test server to connect.
         os.write(13);
         
         /////////////////////////////////////////////////////////////////////////////////////
         // Get MultiplexingManager.
         MultiplexServerInvoker masterServerInvoker = (MultiplexServerInvoker) connector1.getServerInvoker();        
         while (masterServerInvoker.getServerInvokers().isEmpty())
         {
            try
            {
               Thread.sleep(1000);
            }
            catch (Exception e) {}
         }
         
         Collection virtualServerInvokers = masterServerInvoker.getServerInvokers();
         MultiplexServerInvoker virtualServerInvoker = (MultiplexServerInvoker) virtualServerInvokers.iterator().next();
         VirtualServerSocket vss = (VirtualServerSocket) virtualServerInvoker.getServerSocket();
         MultiplexingManager manager = vss.getMultiplexingManager();

         // Test MultiplexingManager parameters.
         Field field = MultiplexingManager.class.getDeclaredField("staticThreadsMonitorPeriod");
         field.setAccessible(true);
         assertEquals(field.getInt(manager), 2 + Multiplex.STATIC_THREADS_MONITOR_PERIOD_DEFAULT);
         
         field = MultiplexingManager.class.getDeclaredField("shutdownRequestTimeout");
         field.setAccessible(true);
         assertEquals(field.getInt(manager), 3 + Multiplex.SHUTDOWN_REQUEST_TIMEOUT_DEFAULT);
         
         field = MultiplexingManager.class.getDeclaredField("shutdownRefusalsMaximum");
         field.setAccessible(true);
         assertEquals(field.getInt(manager), 4 + Multiplex.SHUTDOWN_REFUSALS_MAXIMUM_DEFAULT);
         
         field = MultiplexingManager.class.getDeclaredField("shutdownMonitorPeriod");
         field.setAccessible(true);
         assertEquals(field.getInt(manager), 5 + Multiplex.SHUTDOWN_MONITOR_PERIOD_DEFAULT);
         
         /////////////////////////////////////////////////////////////////////////////////////
         // Get InputMultiplexor.
         field =  MultiplexingManager.class.getDeclaredField("inputMultiplexor");
         field.setAccessible(true);
         InputMultiplexor inputMultiplexor = (InputMultiplexor) field.get(manager);
         log.info("inputMultiplexor: " + inputMultiplexor);
         
         // Test InputMultiplexor parameters.
         field = InputMultiplexor.class.getDeclaredField("bufferSize");
         field.setAccessible(true);
         assertEquals(field.getInt(inputMultiplexor), 2 + Multiplex.INPUT_BUFFER_SIZE_DEFAULT);
         
         field = InputMultiplexor.class.getDeclaredField("maxErrors");
         field.setAccessible(true);
         assertEquals(field.getInt(inputMultiplexor), 3 + Multiplex.INPUT_MAX_ERRORS_DEFAULT);
         
         /////////////////////////////////////////////////////////////////////////////////////
         // Get OutputMultiplexor.
         field =  MultiplexingManager.class.getDeclaredField("outputMultiplexor");
         field.setAccessible(true);
         OutputMultiplexor outputMultiplexor = (OutputMultiplexor) field.get(manager);
         log.info("outputMultiplexor: " + outputMultiplexor);
         
         // Test OutputMultiplexor parameters.
         field = OutputMultiplexor.class.getDeclaredField("messagePoolSize");
         field.setAccessible(true);
         assertEquals(field.getInt(outputMultiplexor), 2 + Multiplex.OUTPUT_MESSAGE_POOL_SIZE_DEFAULT);
         
         field = OutputMultiplexor.class.getDeclaredField("messagePool");
         field.setAccessible(true);
         List messagePool = (List) field.get(outputMultiplexor);
         assertTrue(messagePool.size() <=  2 + Multiplex.OUTPUT_MESSAGE_POOL_SIZE_DEFAULT);
          
         field = OutputMultiplexor.class.getDeclaredField("messageSize");
         field.setAccessible(true);
         assertEquals(field.getInt(outputMultiplexor), 3 + Multiplex.OUTPUT_MESSAGE_SIZE_DEFAULT);
         
         Object message = messagePool.get(0);
         field = message.getClass().getDeclaredField("baos");
         field.setAccessible(true);
         ByteArrayOutputStream baos = (ByteArrayOutputStream) field.get(message);
         field = ByteArrayOutputStream.class.getDeclaredField("buf");
         field.setAccessible(true);
         byte[] buf = (byte[]) field.get(baos);
         assertEquals(buf.length, 3 + Multiplex.OUTPUT_MESSAGE_SIZE_DEFAULT);
        
         field = OutputMultiplexor.class.getDeclaredField("maxChunkSize");
         field.setAccessible(true);
         assertEquals(field.getInt(outputMultiplexor), 4 + Multiplex.OUTPUT_MAX_CHUNK_SIZE_DEFAULT);
         
         field = OutputMultiplexor.class.getDeclaredField("buffer");
         field.setAccessible(true);
         ByteBuffer bb = (ByteBuffer) field.get(outputMultiplexor);
         field = OutputMultiplexor.class.getDeclaredField("HEADER_SIZE");
         field.setAccessible(true);
         int headerSize = ((Integer) field.get(outputMultiplexor)).intValue();
         assertEquals(bb.capacity(), 4 + Multiplex.OUTPUT_MAX_CHUNK_SIZE_DEFAULT + headerSize);
         
         field = OutputMultiplexor.class.getDeclaredField("maxTimeSlice");
         field.setAccessible(true);
         assertEquals(field.getInt(outputMultiplexor), 5 + Multiplex.OUTPUT_MAX_TIME_SLICE_DEFAULT);
         
         field = OutputMultiplexor.class.getDeclaredField("maxDataSlice");
         field.setAccessible(true);
         assertEquals(field.getInt(outputMultiplexor), 6 + Multiplex.OUTPUT_MAX_DATA_SLICE_DEFAULT);
      
         /////////////////////////////////////////////////////////////////////////////////////
         // Test MultiplexServerInvoker parameter.
         field = MultiplexServerInvoker.class.getDeclaredField("maxAcceptErrors");
         field.setAccessible(true);
         assertEquals(field.getInt(masterServerInvoker), 2 + Multiplex.MAX_ACCEPT_ERRORS_DEFAULT);
         assertEquals(field.getInt(virtualServerInvoker), 2 + Multiplex.MAX_ACCEPT_ERRORS_DEFAULT);
         
         /////////////////////////////////////////////////////////////////////////////////////
         // Now, create another Connector and test parameters in new objects.  First, wait
         // until the static threads have stopped.
         connector1.stop();
         os.write(17);
         
         field = MultiplexingManager.class.getDeclaredField("allManagers");
         field.setAccessible(true);
         Collection allManagers = (Collection) field.get(manager);
         
         while (!allManagers.isEmpty())
         {
            try
            {
               Thread.sleep(1000);
            }
            catch (InterruptedException ignored) {}
         }
         
         Thread.sleep(2 * 2 * Multiplex.SHUTDOWN_MONITOR_PERIOD_DEFAULT + 2000);
         field = MultiplexingManager.class.getDeclaredField("staticThreadsRunning");
         field.setAccessible(true);
         
         // Wait until static threads are stopped.
         for (int i = 0; i < 5; i++)
         {
            if (!field.getBoolean(manager))
               break;
            Thread.sleep(2000);
         }
         
         assertFalse(field.getBoolean(manager));
         
         // Increase parameter values.
         Iterator it = conf.keySet().iterator();
         while (it.hasNext())
         {
            Object key = it.next();
            conf.put(key, new Integer(((Integer)conf.get(key)).intValue() + 2));
         }
         
         log.info("configuration: " + conf);
         
         // Create Connector.
         Connector connector2 = new Connector(ConfigurationTestServer.connectorURI4, conf);
         connector2.create();
         connector2.start();
         log.info("started Connector on: " + connector2.getInvokerLocator());
         
         // Tell test server to connect.
         os.write(19);
         
         /////////////////////////////////////////////////////////////////////////////////////
         // Get MultiplexingManager.
         masterServerInvoker = (MultiplexServerInvoker) connector2.getServerInvoker();        
         while (masterServerInvoker.getServerInvokers().isEmpty())
         {
            try
            {
               Thread.sleep(1000);
            }
            catch (Exception e) {}
         }
         
         virtualServerInvokers = masterServerInvoker.getServerInvokers();
         virtualServerInvoker = (MultiplexServerInvoker) virtualServerInvokers.iterator().next();
         vss = (VirtualServerSocket) virtualServerInvoker.getServerSocket();
         manager = vss.getMultiplexingManager();

         // Test MultiplexingManager parameters.
         field = MultiplexingManager.class.getDeclaredField("staticThreadsMonitorPeriod");
         field.setAccessible(true);
         assertEquals(field.getInt(manager), 4 + Multiplex.STATIC_THREADS_MONITOR_PERIOD_DEFAULT);
         
         field = MultiplexingManager.class.getDeclaredField("shutdownRequestTimeout");
         field.setAccessible(true);
         assertEquals(field.getInt(manager), 5 + Multiplex.SHUTDOWN_REQUEST_TIMEOUT_DEFAULT);
         
         field = MultiplexingManager.class.getDeclaredField("shutdownRefusalsMaximum");
         field.setAccessible(true);
         assertEquals(field.getInt(manager), 6 + Multiplex.SHUTDOWN_REFUSALS_MAXIMUM_DEFAULT);
         
         field = MultiplexingManager.class.getDeclaredField("shutdownMonitorPeriod");
         field.setAccessible(true);
         assertEquals(field.getInt(manager), 7 + Multiplex.SHUTDOWN_MONITOR_PERIOD_DEFAULT);
         
         /////////////////////////////////////////////////////////////////////////////////////
         // Get InputMultiplexor.
         field =  MultiplexingManager.class.getDeclaredField("inputMultiplexor");
         field.setAccessible(true);
         inputMultiplexor = (InputMultiplexor) field.get(manager);
         log.info("inputMultiplexor: " + inputMultiplexor);
         
         // Test InputMultiplexor parameters.  They should be increased since we have
         // a new InputMultiplexor.
         field = InputMultiplexor.class.getDeclaredField("bufferSize");
         field.setAccessible(true);
         assertEquals(field.getInt(inputMultiplexor), 4 + Multiplex.INPUT_BUFFER_SIZE_DEFAULT);
         
         field = InputMultiplexor.class.getDeclaredField("maxErrors");
         field.setAccessible(true);
         assertEquals(field.getInt(inputMultiplexor), 5 + Multiplex.INPUT_MAX_ERRORS_DEFAULT);
         
         /////////////////////////////////////////////////////////////////////////////////////
         // Get OutputMultiplexor.
         field =  MultiplexingManager.class.getDeclaredField("outputMultiplexor");
         field.setAccessible(true);
         outputMultiplexor = (OutputMultiplexor) field.get(manager);
         log.info("outputMultiplexor: " + outputMultiplexor);
        
         // Test OutputMultiplexor parameters.  They should be increased since we have
         // a new OutputMultiplexor.
         field = OutputMultiplexor.class.getDeclaredField("messagePoolSize");
         field.setAccessible(true);
         assertEquals(field.getInt(outputMultiplexor), 4 + Multiplex.OUTPUT_MESSAGE_POOL_SIZE_DEFAULT);

         field = OutputMultiplexor.class.getDeclaredField("messagePool");
         field.setAccessible(true);
         messagePool = (List) field.get(outputMultiplexor);
         assertTrue(messagePool.size() <=  4 + Multiplex.OUTPUT_MESSAGE_POOL_SIZE_DEFAULT);
         
         field = OutputMultiplexor.class.getDeclaredField("messageSize");
         field.setAccessible(true);
         assertEquals(field.getInt(outputMultiplexor), 5 + Multiplex.OUTPUT_MESSAGE_SIZE_DEFAULT);
         
         message = messagePool.get(0);
         field = message.getClass().getDeclaredField("baos");
         field.setAccessible(true);
         baos = (ByteArrayOutputStream) field.get(message);
         field = ByteArrayOutputStream.class.getDeclaredField("buf");
         field.setAccessible(true);
         buf = (byte[]) field.get(baos);
         assertEquals(buf.length, 5 + Multiplex.OUTPUT_MESSAGE_SIZE_DEFAULT);
        
         field = OutputMultiplexor.class.getDeclaredField("maxChunkSize");
         field.setAccessible(true);
         assertEquals(field.getInt(outputMultiplexor), 6 + Multiplex.OUTPUT_MAX_CHUNK_SIZE_DEFAULT);
         
         field = OutputMultiplexor.class.getDeclaredField("buffer");
         field.setAccessible(true);
         bb = (ByteBuffer) field.get(outputMultiplexor);
         field = OutputMultiplexor.class.getDeclaredField("HEADER_SIZE");
         field.setAccessible(true);
         headerSize = ((Integer) field.get(outputMultiplexor)).intValue();
         assertEquals(bb.capacity(), 6 + Multiplex.OUTPUT_MAX_CHUNK_SIZE_DEFAULT + headerSize);
         
         field = OutputMultiplexor.class.getDeclaredField("maxTimeSlice");
         field.setAccessible(true);
         assertEquals(field.getInt(outputMultiplexor), 7 + Multiplex.OUTPUT_MAX_TIME_SLICE_DEFAULT);
         
         field = OutputMultiplexor.class.getDeclaredField("maxDataSlice");
         field.setAccessible(true);
         assertEquals(field.getInt(outputMultiplexor), 8 + Multiplex.OUTPUT_MAX_DATA_SLICE_DEFAULT);

         /////////////////////////////////////////////////////////////////////////////////////
         // Test MultiplexServerInvoker parameter.
         field = MultiplexServerInvoker.class.getDeclaredField("maxAcceptErrors");
         field.setAccessible(true);
         assertEquals(field.getInt(masterServerInvoker), 4 + Multiplex.MAX_ACCEPT_ERRORS_DEFAULT);
         assertEquals(field.getInt(virtualServerInvoker), 4 + Multiplex.MAX_ACCEPT_ERRORS_DEFAULT);
         
         connector2.stop();
         log.info(getName() + " PASSES");
      }
      catch (Exception e)
      {
         log.error(e);
         e.printStackTrace();
         fail();
         log.info(getName() + " FAILS");
      }
   }
}
