commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rdon...@apache.org
Subject cvs commit: jakarta-commons/logging/optional/src/test/org/apache/commons/logging LogFactoryTest.java
Date Mon, 22 Nov 2004 22:50:08 GMT
rdonkin     2004/11/22 14:50:07

  Modified:    logging/optional/src/test/org/apache/commons/logging
                        LogFactoryTest.java
  Log:
  Improved test cases for WeakHashMap classloading. Contributed by Brian Stansberry.
  
  Revision  Changes    Path
  1.3       +146 -96   jakarta-commons/logging/optional/src/test/org/apache/commons/logging/LogFactoryTest.java
  
  Index: LogFactoryTest.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/logging/optional/src/test/org/apache/commons/logging/LogFactoryTest.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- LogFactoryTest.java	17 Nov 2004 23:23:21 -0000	1.2
  +++ LogFactoryTest.java	22 Nov 2004 22:50:07 -0000	1.3
  @@ -17,8 +17,11 @@
   
   package org.apache.commons.logging;
   
  +import java.io.ByteArrayOutputStream;
  +import java.io.FileNotFoundException;
  +import java.io.IOException;
  +import java.io.InputStream;
   import java.lang.ref.WeakReference;
  -import java.util.Hashtable;
   
   import junit.framework.TestCase;
   
  @@ -46,119 +49,97 @@
        * Tests that LogFactories are not removed from the map
        * if their creating ClassLoader is still alive.
        */ 
  -    public void testHoldFactories()
  -    {     
  -        // Get a factory and create a WeakReference to it that
  -        // we can check to see if the factory has been removed
  -        // from LogFactory.properties
  -        LogFactory factory = LogFactory.getFactory();
  -        WeakReference weakFactory = new WeakReference(factory);
  -        
  -        // Remove any hard reference to the factory
  -        factory = null; 
  +    public void testHoldFactories() throws Exception
  +    {
  +        // 1) Basic test
           
  -        // Run the gc, confirming that the original factory
  +        // Get a weak reference to the factory using the classloader.
  +        // When this reference is cleared we know the factory has been 
  +        // cleared from LogFactory.factories as well
  +        WeakReference weakFactory = loadFactoryFromContextClassLoader();
  +        // Run the gc, confirming that the factory
           // is not dropped from the map even though there are 
  -        // no other references to it
  -        int iterations = 0;
  -        int bytz = 2;
  -        while(iterations++ < MAX_GC_ITERATIONS) {
  -            System.gc();
  -            
  -            assertNotNull("LogFactory released while ClassLoader still active.",
  -                          weakFactory.get());                
  -            
  -            // create garbage:
  -            byte[] b;
  -            try {
  -              b  =  new byte[bytz];            
  -              bytz = bytz * 2;
  -            }
  -            catch (OutOfMemoryError oom) {
  -                // This error means the test passed, as it means the LogFactory
  -                // was never released.  So, we have to catch and deal with it
  -                
  -                // Doing this is probably a no-no, but it seems to work ;-)
  -                b = null;
  -                System.gc();
  -                break;
  -            }
  -        }
  +        // no other references to it        
  +        checkRelease(weakFactory, true);
  +        
  +        // 2) Test using an isolated classloader a la a web app
  +        
  +        // Create a classloader that isolates commons-logging
  +        ClassLoader childLoader = new IsolatedClassLoader(origLoader);
  +        Thread.currentThread().setContextClassLoader(childLoader);
  +        weakFactory = loadFactoryFromContextClassLoader();
  +        Thread.currentThread().setContextClassLoader(origLoader);  
  +        // At this point we still have a reference to childLoader,
  +        // so the factory should not be cleared
  +        
  +        checkRelease(weakFactory, true);
       }
  -    
  +
       /**
  -     * Tests that a LogFactory is eventually removed from the map
  -     * after its creating ClassLoader is garbage collected. 
  +     * Tests that a ClassLoader is eventually removed from the map
  +     * after all hard references to it are removed. 
        */
  -    public void testReleaseFactories()
  +    public void testReleaseClassLoader() throws Exception
       {
  -        // Create a temporary classloader        
  -        ClassLoader childLoader = new ClassLoader() {};
  +        // 1) Test of a child classloader that follows the Java2
  +        //    delegation model (e.g. an EJB module classloader)
  +                
  +        // Create a classloader that delegates to its parent        
  +        ClassLoader childLoader = new ClassLoader() {};   
  +        // Get a weak reference to the factory using the classloader.
  +        // When this reference is cleared we know the factory has been 
  +        // cleared from LogFactory.factories as well
           Thread.currentThread().setContextClassLoader(childLoader);
  -        
  -        // Get a factory using the child loader.
  -        LogFactory factory        = LogFactory.getFactory();
  -        // Hold a WeakReference to the factory. When this reference
  -        // is cleared we know the factory has been cleared from
  -        // LogFactory.factories as well
  -        WeakReference weakFactory = new WeakReference(factory);
  +        loadFactoryFromContextClassLoader();
  +        Thread.currentThread().setContextClassLoader(origLoader);    
           
           // Get a WeakReference to the child loader so we know when it
           // has been gc'ed
  -        WeakReference weakLoader = new WeakReference(childLoader);
  +        WeakReference weakLoader = new WeakReference(childLoader);     
  +        // Remove any hard reference to the childLoader or the factory creator
  +        childLoader   = null;
  +
  +        // Run the gc, confirming that childLoader is dropped from the map
  +        checkRelease(weakLoader, false);
  +        
  +        // 2) Test using an isolated classloader a la a web app
           
  -        // Remove any hard reference to the childLoader and the factory
  +        childLoader = new IsolatedClassLoader(origLoader);
  +        Thread.currentThread().setContextClassLoader(childLoader);
  +        loadFactoryFromContextClassLoader();
           Thread.currentThread().setContextClassLoader(origLoader);
  -        childLoader = null;
  -        factory     = null;
  +        weakLoader  = new WeakReference(childLoader);
  +        childLoader = null;  // somewhat equivalent to undeploying a webapp  
  +        
  +        checkRelease(weakLoader, false);
  +        
  +    }
  +    
  +    /**
  +     * Repeatedly run the gc, checking whether the given WeakReference 
  +     * is not cleared and failing or succeeding based on
  +     * parameter <code>failOnRelease</code>.
  +     */
  +    private void checkRelease(WeakReference reference, boolean failOnRelease) {
           
  -        // Run the gc, confirming that the original childLoader
  -        // is dropped from the map
           int iterations = 0;
           int bytz = 2;
           while(true) {
               System.gc();
               if(iterations++ > MAX_GC_ITERATIONS){
  -                fail("Max iterations reached before childLoader released.");
  +                if (failOnRelease) {
  +                    break;
  +                }
  +                fail("Max iterations reached before reference released.");
               }
               
  -            if(weakLoader.get() == null) {
  -                break;                
  -            } else {
  -                // create garbage:
  -                byte[] b;
  -                try {
  -                    b =  new byte[bytz];
  -                    bytz = bytz * 2;
  +            if(reference.get() == null) {
  +                if (failOnRelease) {
  +                    fail("reference released");
                   }
  -                catch (OutOfMemoryError oom) {
  -                    // Doing this is probably a no-no, but it seems to work ;-)
  -                    b = null;
  -                    System.gc();
  -                    fail("OutOfMemory before childLoader released.");
  +                else {
  +                    break;
                   }
  -            }
  -        }
  -        
  -        // Confirm that the original factory is removed from the map
  -        // within the maximum allowed number of calls to put() +
  -        // the maximum number of subsequent gc iterations
  -        iterations = 0;
  -        while(true) {
  -            System.gc();
  -            if(iterations++ > WeakHashtable.MAX_PUTS_BEFORE_PURGE + MAX_GC_ITERATIONS){
  -                Hashtable table = LogFactory.factories;
  -                fail("Max iterations reached before factory released.");
  -            }           
  -            
  -            // Create a new child loader and use it to add to the map.
  -            ClassLoader newChildLoader  = new ClassLoader() {};
  -            Thread.currentThread().setContextClassLoader(newChildLoader);
  -            LogFactory.getFactory();
  -            Thread.currentThread().setContextClassLoader(origLoader);
  -            
  -            if(weakFactory.get() == null) {
  -                break;                
               } else {
                   // create garbage:
                   byte[] b;
  @@ -169,14 +150,16 @@
                   catch (OutOfMemoryError oom) {
                       // Doing this is probably a no-no, but it seems to work ;-)
                       b = null;
  -                    bytz = 2; // start over
                       System.gc();
  +                    if (failOnRelease) {
  +                        break;
  +                    }
  +                    fail("OutOfMemory before reference released.");
                   }
               }
           }
  -        
  -    }    
  -    
  +    }
  +
       protected void setUp() throws Exception {
           // Preserve the original classloader and factory implementation
           // class so we can restore them when we are done
  @@ -205,6 +188,73 @@
           }
           
           super.tearDown();
  +    }
  +    
  +    private static WeakReference loadFactoryFromContextClassLoader() 
  +            throws Exception {
  +        ClassLoader loader = Thread.currentThread().getContextClassLoader();
  +        Class clazz = loader.loadClass(SubDeploymentClass.class.getName());
  +        IFactoryCreator creator = (IFactoryCreator) clazz.newInstance();
  +        return creator.getWeakFactory();
  +    }
  +    
  +    /**
  +     * A ClassLoader that mimics the operation of a web app classloader
  +     * by not delegating some calls to its parent.  
  +     * 
  +     * In this case it does not delegate loading commons-logging classes, 
  +     * acting as if commons-logging were in WEB-INF/lib.  However, it does 
  +     * delegate loading of IFactoryCreator, thus allowing this class to 
  +     * interact with SubDeploymentClass via IFactoryCreator.
  +     */
  +    private static final class IsolatedClassLoader extends ClassLoader {
  +        
  +        private IsolatedClassLoader(ClassLoader parent) {
  +            super(parent);
  +        }
  +        
  +        protected synchronized Class loadClass(String name, boolean resolve)
  +        throws ClassNotFoundException
  +        {
  +            if (name != null && name.startsWith("org.apache.commons.logging")
  +                    && "org.apache.commons.logging.IFactoryCreator".equals(name)
== false) {
  +                // First, check if the class has already been loaded
  +                Class c = findClass(name);
  +            
  +                if (resolve) {
  +                    resolveClass(c);
  +                }
  +                return c;
  +            }
  +            else {
  +                return super.loadClass(name, resolve);
  +            }
  +        }
  +        
  +        protected Class findClass(String name) throws ClassNotFoundException {
  +            if (name != null && name.startsWith("org.apache.commons.logging")
  +                    && "org.apache.commons.logging.IFactoryCreator".equals(name)
== false) {
  +                try {
  +                    InputStream is = getResourceAsStream( name.replace('.','/').concat(".class"));
  +                    byte[] bytes = new byte[1024];                
  +                    ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
  +                    int read;
  +                    while ((read = is.read(bytes)) > -1) {
  +                        baos.write(bytes, 0, read);
  +                    }
  +                    bytes = baos.toByteArray();
  +                    return this.defineClass(name, bytes, 0, bytes.length);
  +                } catch (FileNotFoundException e) {
  +                    throw new ClassNotFoundException("cannot find " + name, e);
  +                } catch (IOException e) {
  +                    throw new ClassNotFoundException("cannot read " + name, e);
  +                }
  +            }
  +            else {
  +                return super.findClass(name);
  +            }
  +        }
  +        
       }
       
   }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Mime
View raw message