Package org.jboss.test.cluster.defaultcfg.test

Source Code of org.jboss.test.cluster.defaultcfg.test.JChannelFactoryClassLoaderLeakTestCase$ClassLoaderLeakHandler

/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, 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.cluster.defaultcfg.test;

import java.lang.reflect.Method;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

import junit.framework.TestCase;

import org.jboss.ha.framework.server.JChannelFactory;
import org.jboss.logging.Logger;
import org.jgroups.Address;
import org.jgroups.Channel;
import org.jgroups.MembershipListener;
import org.jgroups.Message;
import org.jgroups.MessageListener;
import org.jgroups.View;
import org.jgroups.blocks.GroupRequest;
import org.jgroups.blocks.MessageDispatcher;
import org.jgroups.blocks.RequestHandler;

/**
* Tests classloader leak handling of {@link JChannelFactory}.
*
* @author Brian Stansberry
*/
public class JChannelFactoryClassLoaderLeakTestCase extends TestCase
{
   private static final Logger log = Logger.getLogger(JChannelFactoryClassLoaderLeakTestCase.class);
  
   private static Method OBJECT_ARG = null;
   private static Method STRING_ARG = null;
   private static Method SIMPLE_MUX = null;
   private static Method COMPLEX_MUX = null;
  
   static
   {
      Class<?> clazz = JChannelFactory.class;
      Class<?> parentClazz = clazz.getSuperclass();
      try
      {
         OBJECT_ARG = clazz.getDeclaredMethod("createChannel", new Class[] { Object.class });
      }
      catch (NoSuchMethodException nsme)
      {
         try
         {
            OBJECT_ARG = parentClazz.getDeclaredMethod("createChannel", new Class[] { Object.class });
         }
         catch (NoSuchMethodException nsme1)
         {
            log.error("Reflection failure", nsme);
         }
      }
      try
      {
         STRING_ARG = clazz.getDeclaredMethod("createChannel", new Class[] { String.class });
      }
      catch (NoSuchMethodException nsme)
      {
         try
         {
            STRING_ARG = parentClazz.getDeclaredMethod("createChannel", new Class[] { String.class });
         }
         catch (NoSuchMethodException nsme1)
         {
            log.error("Reflection failure", nsme);
         }
      }
      try
      {
         SIMPLE_MUX = clazz.getDeclaredMethod("createMultiplexerChannel", new Class[] { String.class, String.class });
      }
      catch (NoSuchMethodException nsme)
      {
         try
         {
            SIMPLE_MUX = parentClazz.getDeclaredMethod("createMultiplexerChannel", new Class[] { String.class, String.class });
         }
         catch (NoSuchMethodException nsme1)
         {
            log.error("Reflection failure", nsme);
         }
      }
      try
      {
         COMPLEX_MUX = clazz.getDeclaredMethod("createMultiplexerChannel", new Class[] { String.class, String.class, boolean.class, String.class });
      }
      catch (NoSuchMethodException nsme)
      {
         try
         {
            COMPLEX_MUX = parentClazz.getDeclaredMethod("createMultiplexerChannel", new Class[] { String.class, String.class, boolean.class, String.class });
         }
         catch (NoSuchMethodException nsme1)
         {
            log.error("Reflection failure", nsme);
         }
      }
   }
  
   private JChannelFactory factory1;
   private JChannelFactory factory2;
   private Channel channel1;
   private Channel channel2;
   private String jgroups_bind_addr;
   private ClassLoader testLoader;
  
   /**
    * Create a new JChannelFactoryUnitTestCase.
    *
    * @param name
    */
   public JChannelFactoryClassLoaderLeakTestCase(String name)
   {
      super(name);
   }

   protected void setUp() throws Exception
   {
      if (COMPLEX_MUX == null)
         throw new IllegalStateException("Reflection failed in class init; see logs");
     
      super.setUp();
     
      testLoader = new ClassLoader(Thread.currentThread().getContextClassLoader()){};
     
      // set the jgroups.bind_addr to the testsuite bind_addr
      String jgroups_bind_addr = System.getProperty("jgroups.bind_addr");
      if (jgroups_bind_addr == null)
      {
         System.setProperty("jgroups.bind_addr", System.getProperty("jbosstest.cluster.node0", "localhost"));
      }

      // JBAS-8540 need to use a stack specific default for JGroups mcast address
      String jgroups_udp_mcast_addr = System.getProperty("jgroups.udp.mcast_addr");
      if (jgroups_udp_mcast_addr == null)
      {
         System.setProperty("jgroups.udp.mcast_addr", System.getProperty("jbosstest.jgroups.mcast_addr", "230.111.111.111"));
      }
     
     
      factory1 = new TestClassLoaderJChannelFactory();
      factory1.setMultiplexerConfig("cluster/channelfactory/stacks.xml");
      factory1.setAssignLogicalAddresses(false);
      factory1.setExposeChannels(false);
      factory1.setManageReleasedThreadClassLoader(true);
      factory1.create();
      factory1.start();
      factory2 = new TestClassLoaderJChannelFactory();
      factory2.setMultiplexerConfig("cluster/channelfactory/stacks.xml");
      factory2.setAssignLogicalAddresses(false);
      factory2.setExposeChannels(false);
      factory2.setManageReleasedThreadClassLoader(true);
      factory2.create();
      factory2.start();
   }

   protected void tearDown() throws Exception
   {
      super.tearDown();
     
      testLoader = null;
     
      if (jgroups_bind_addr == null)
         System.clearProperty("jgroups.bind_addr");
     
      if (channel1 != null && channel1.isOpen())
         channel1.close();
     
      if (channel2 != null && channel2.isOpen())
         channel2.close();
     
      if (factory1 != null)
      {
         factory1.stop();
         factory1.destroy();
      }
      if (factory2 != null)
      {
         factory2.stop();
         factory2.destroy();
      }
   }
  
   public void testClassLoaderLeakObjectShared() throws Exception
  
      Object[] args1 = { factory1.getConfig("shared1") };
      Object[] args2 = { factory2.getConfig("shared2") };
      classloaderLeakTest(OBJECT_ARG, args1, args2);
   }
  
   public void testClassLoaderLeakObjectUnshared() throws Exception
  
      Object[] args1 = { factory1.getConfig("unshared1") };
      Object[] args2 = { factory2.getConfig("unshared2") };
      classloaderLeakTest(OBJECT_ARG, args1, args2);
   }
  
   public void testClassLoaderLeakStringShared() throws Exception
  
      Object[] args1 = { "shared1" };
      Object[] args2 = { "shared2" };
      classloaderLeakTest(STRING_ARG, args1, args2);
   }
  
   public void testClassLoaderLeakStringUnshared() throws Exception
  
      Object[] args1 = { "unshared1" };
      Object[] args2 = { "unshared2" };
      classloaderLeakTest(STRING_ARG, args1, args2);
   }
  
   public void testClassLoaderLeakSimpleMuxShared() throws Exception
  
      Object[] args1 = { "shared1", "leaktest" };
      Object[] args2 = { "shared2", "leaktest" };
      classloaderLeakTest(SIMPLE_MUX, args1, args2);
   }
  
   public void testClassLoaderLeakSimpleMuxUnshared() throws Exception
  
      Object[] args1 = { "unshared1", "leaktest" };
      Object[] args2 = { "unshared2", "leaktest" };
      classloaderLeakTest(SIMPLE_MUX, args1, args2);
   }
  
   public void testClassLoaderLeakComplexMuxShared() throws Exception
  
      Object[] args1 = { "shared1", "leaktest", Boolean.FALSE, null };
      Object[] args2 = { "shared2", "leaktest", Boolean.FALSE, null };
      classloaderLeakTest(COMPLEX_MUX, args1, args2);
   }
  
   public void testClassLoaderLeakComplexMuxUnshared() throws Exception
  
      Object[] args1 = { "unshared1", "leaktest", Boolean.FALSE, null };
      Object[] args2 = { "unshared2", "leaktest", Boolean.FALSE, null };
      classloaderLeakTest(COMPLEX_MUX, args1, args2);
   }
  
   public void testClassLoaderLeakNonConcurrent() throws Exception
  
      Object[] args1 = { "nonconcurrent1" };
      Object[] args2 = { "nonconcurrent2" };
      classloaderLeakTest(STRING_ARG, args1, args2);
   }
  
   private void classloaderLeakTest(Method factoryMeth, Object[] factory1Args,
                                    Object[] factory2Args)
      throws Exception
   {
      int numThreads = 8;
      int numLoops = 100;
     
      Semaphore semaphore = new Semaphore(numThreads);
     
      ThreadGroup runnerGroup = new ThreadGroup("TestRunners");
     
      ClassLoader ours = Thread.currentThread().getContextClassLoader();
     
      // The classloader we want channel threads to use
      ClassLoaderLeakHandler handler = new ClassLoaderLeakHandler(testLoader, semaphore, runnerGroup);
     
      MessageDispatcher[] dispatchers = new MessageDispatcher[2];
     
//      Thread.currentThread().setContextClassLoader(testLoader);   
//      try
//      {
         channel1 = (Channel) factoryMeth.invoke(factory1, factory1Args);
         dispatchers[0] = new MessageDispatcher(channel1, handler, handler, handler);
        
         channel1.connect("leaktest");
         assertEquals("No classloader leak on channel1 connect", null, handler.getLeakedClassLoader());
        
         channel2 = (Channel) factoryMeth.invoke(factory2, factory2Args);
         dispatchers[1] = new MessageDispatcher(channel2, handler, handler, handler);
        
         channel2.connect("leaktest");
         assertEquals("No classloader leak on channel2 connect", null, handler.getLeakedClassLoader());
//      }
//      finally
//      {
//         Thread.currentThread().setContextClassLoader(ours);
//      }
     
      log.info("Channels connected");
     
      ClassLoaderLeakRunner[] runners = new ClassLoaderLeakRunner[numThreads];     
     
      for (int i = 0; i < runners.length; i++)
      {
         MessageDispatcher disp = dispatchers[i % 2];
           
         runners[i] = new ClassLoaderLeakRunner(disp, numLoops, runnerGroup, semaphore);        
      }

      semaphore.acquire(numThreads);
     
      for (int i = 0; i < runners.length; i++)
      {
         runners[i].start();
      }
     
      semaphore.release(numThreads);
     
      try
      {
         assertTrue("messages received within 15 seconds", semaphore.tryAcquire(numThreads, 15, TimeUnit.SECONDS));
        
         log.info("Messages received");
      }
      finally
      {
         for (int i = 0; i < runners.length; i++)
         {
            runners[i].stop();
         }
      }
     
      log.info("Sender threads stopped");
     
      assertEquals("No classloader leak", null, handler.getLeakedClassLoader());
   }

   private class ClassLoaderLeakRunner implements Runnable
   {
      private Thread thread;
      private final MessageDispatcher dispatcher;
      private final int numMsgs;
      private final ThreadGroup threadGroup;
      private final Semaphore semaphore;
      private boolean stopped;
      private Exception exception;
     
      ClassLoaderLeakRunner(MessageDispatcher dispatcher, int numMsgs, ThreadGroup group, Semaphore semaphore)
      {
         this.dispatcher = dispatcher;
         this.numMsgs = numMsgs;
         this.threadGroup = group;
         this.semaphore = semaphore;
      }
     
      public void run()
      {
         boolean acquired = false;
         ClassLoader cl = new ClassLoader(Thread.currentThread().getContextClassLoader()){};
         try
         {
            semaphore.acquire();        
            acquired = true;
           
            log.info(Thread.currentThread().getName() + " starting");       
           
            Thread.currentThread().setContextClassLoader(cl);
            for (int i = 0; i < numMsgs && !stopped && !Thread.interrupted(); i++)
            {
               Message msg = new Message(null, null, String.valueOf(i));
               // sending this way calls receive()
               dispatcher.send(msg);
               // sending this way calls handle()
               dispatcher.castMessage(null, msg, GroupRequest.GET_ALL, 0, false);
            }
           
            log.info(Thread.currentThread().getName() + " done");
         }
         catch (Exception e)
         {
            this.exception = e;
         }
         finally
         {
            if (acquired)
               semaphore.release();
           
            Thread.currentThread().setContextClassLoader(cl.getParent());
         }
      }

      public Exception getException()
      {
         return exception;
      }

      public void start()
      {
         thread = new Thread(this.threadGroup, this);
         thread.setDaemon(true);
         thread.start();
      }
     
      public void stop()
      {
         stopped = true;
         if (thread != null && thread.isAlive())
         {
            try
            {
               thread.join(100);
            }
            catch (InterruptedException e)
            {
            }
            if (thread.isAlive())
               thread.interrupt();
         }
      }
   }
  
   private class ClassLoaderLeakHandler
      implements MembershipListener, MessageListener, RequestHandler
   {
      private final Semaphore semaphore;
      private final ClassLoader expected;
      private final ThreadGroup runnerGroup;
      private final Thread main;
      private ClassLoader leakedClassLoader;
      private final int numPermits;
     
      ClassLoaderLeakHandler(ClassLoader expected, Semaphore semaphore, ThreadGroup runnerGroup)
      {
         this.expected = expected;
         this.semaphore = semaphore;
         this.runnerGroup = runnerGroup;
         this.numPermits = this.semaphore.availablePermits();
         this.main = Thread.currentThread();
      }

      public Object handle(Message msg)
      {
         log.debug("handled(): " + msg.getObject());
         checkClassLoader(true, "handle()");
         return null;
      }

      public void block()
      {
         checkClassLoader(false, "block()");
      }

      public void suspect(Address suspected_mbr)
      {
         checkClassLoader(false, "suspect()");        
      }

      public void viewAccepted(View new_view)
      {
         checkClassLoader(false, "viewAccepted()");    
         log.info("viewAccepted(): " + new_view);
      }

      public byte[] getState()
      {
         checkClassLoader(false, "getState()")
         return new byte[1];
      }

      public void receive(Message msg)
      {
         checkClassLoader(false, "receive()")
      }

      public void setState(byte[] state)
      {
         checkClassLoader(false, "setState()");  
      }

      public ClassLoader getLeakedClassLoader()
      {
         return leakedClassLoader;
      }
     
      private void checkClassLoader(boolean fromHandle, String method)
      {        
         if (leakedClassLoader == null) // ignore msgs once we found a leak
         {
            // ignore runner threads that loop all the way back up
            Thread current = Thread.currentThread();
            if (current == main || current.getThreadGroup().equals(runnerGroup))
            {              
               return;
            }
           
            ClassLoader tccl = Thread.currentThread().getContextClassLoader();
            if (!expected.equals(tccl))              
            {
               leakedClassLoader = tccl;
               semaphore.release(numPermits);
               log.info("ClassLoader leaked in " + method + ": " + tccl + " leaked to " + Thread.currentThread().getName());
            }
         }        
      }
     
   }
  
   private class TestClassLoaderJChannelFactory
   extends JChannelFactory
   {

      @Override
      protected ClassLoader getDefaultChannelThreadContextClassLoader()
      {
         return testLoader;
      }
     
   }
}
TOP

Related Classes of org.jboss.test.cluster.defaultcfg.test.JChannelFactoryClassLoaderLeakTestCase$ClassLoaderLeakHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.