    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17
    18  package org.apache.commons.logging.jdk14;
    19
    20
    21  import java.io.ByteArrayOutputStream;
    22  import java.io.InputStream;
    23  import java.lang.reflect.Method;
    24  import java.util.Iterator;
    25  import java.util.logging.Handler;
    26  import java.util.logging.Level;
    27  import java.util.logging.LogManager;
    28  import java.util.logging.LogRecord;
    29  import java.util.logging.Logger;
    30
    31  import junit.framework.Test;
    32
    33  import org.apache.commons.logging.DummyException;
    34  import org.apache.commons.logging.PathableClassLoader;
    35  import org.apache.commons.logging.PathableTestSuite;
    36
    37
    38  /**
    39   * <p>TestCase for JDK 1.4 logging when running on a JDK 1.4 system with
    40   * custom configuration, so that JDK 1.4 should be selected and an appropriate
    41   * logger configured per the configuration properties.</p>
    42   *
    43   * @author Craig R. McClanahan
    44   * @version $Revision: 568760 $ $Date: 2007-08-23 00:19:45 +0200 (to, 23 aug 2007) $
    45   */
    46
    47  public class CustomConfigTestCase extends DefaultConfigTestCase {
    48
    49      protected static final String HANDLER_NAME
    50          = "org.apache.commons.logging.jdk14.TestHandler";
    51
    52      // ----------------------------------------------------------- Constructors
    53
    54
    55      /**
    56       * <p>Construct a new instance of this test case.</p>
    57       *
    58       * @param name Name of the test case
    59       */
    60      public CustomConfigTestCase(String name) {
    61          super(name);
    62      }
    63
    64
    65      // ----------------------------------------------------- Instance Variables
    66
    67
    68      /**
    69       * <p>The customized <code>Handler</code> we will be using.</p>
    70       */
    71      protected TestHandler handler = null;
    72
    73
    74      /**
    75       * <p>The underlying <code>Handler</code>s we will be using.</p>
    76       */
    77      protected Handler handlers[] = null;
    78
    79
    80      /**
    81       * <p>The underlying <code>Logger</code> we will be using.</p>
    82       */
    83      protected Logger logger = null;
    84
    85
    86      /**
    87       * <p>The underlying <code>LogManager</code> we will be using.</p>
    88       */
    89      protected LogManager manager = null;
    90
    91
    92      /**
    93       * <p>The message levels that should have been logged.</p>
    94       */
    95      protected Level testLevels[] =
    96      { Level.FINE, Level.INFO, Level.WARNING, Level.SEVERE, Level.SEVERE };
    97
    98
    99      /**
    100      * <p>The message strings that should have been logged.</p>
    101      */
    102     protected String testMessages[] =
    103     { "debug", "info", "warn", "error", "fatal" };
    104
    105
    106     // ------------------------------------------- JUnit Infrastructure Methods
    107
    108
    109     /**
    110      * Given the name of a class that is somewhere in the classpath of the provided
    111      * classloader, return the contents of the corresponding .class file.
    112      */
    113     protected static byte[] readClass(String name, ClassLoader srcCL) throws Exception {
    114         String resName = name.replace('.', '/') + ".class";
    115         System.err.println("Trying to load resource [" + resName + "]");
    116         InputStream is = srcCL.getResourceAsStream(resName);
    117         ByteArrayOutputStream baos = new ByteArrayOutputStream();
    118         System.err.println("Reading resource [" + resName + "]");
    119         byte[] buf = new byte[1000];
    120         for(;;) {
    121             int read = is.read(buf);
    122             if (read <= 0) {
    123                 break;
    124             }
    125             baos.write(buf, 0, read);
    126         }
    127         is.close();
    128         return baos.toByteArray();
    129     }
    130
    131     /**
    132      * Make a class available in the system classloader even when its classfile is
    133      * not present in the classpath configured for that classloader. This only
    134      * works for classes for which all dependencies are already loaded in
    135      * that classloader.
    136      */
    137     protected static void loadTestHandler(String className, ClassLoader targetCL) {
    138         try {
    139             targetCL.loadClass(className);
    140             // fail("Class already in target classloader");
    141             return;
    142         } catch(ClassNotFoundException ex) {
    143             // ok, go ahead and load it
    144         }
    145
    146         try {
    147             ClassLoader srcCL = CustomConfigAPITestCase.class.getClassLoader();
    148             byte[] classData = readClass(className, srcCL);
    149
    150             Class[] params = new Class[] {
    151                 String.class, classData.getClass(),
    152                 Integer.TYPE, Integer.TYPE};
    153             Method m = ClassLoader.class.getDeclaredMethod("defineClass", params);
    154
    155             Object[] args = new Object[4];
    156             args[0] = className;
    157             args[1] = classData;
    158             args[2] = new Integer(0);
    159             args[3] = new Integer(classData.length);
    160             m.setAccessible(true);
    161             m.invoke(targetCL, args);
    162         } catch(Exception e) {
    163             e.printStackTrace();
    164             fail("Unable to load class " + className);
    165         }
    166     }
    167
    168     /**
    169      * Set up instance variables required by this test case.
    170      */
    171     public void setUp() throws Exception {
    172         setUpManager
    173             ("org/apache/commons/logging/jdk14/CustomConfig.properties");
    174         setUpLogger("TestLogger");
    175         setUpHandlers();
    176         setUpFactory();
    177         setUpLog("TestLogger");
    178     }
    179
    180
    181     /**
    182      * Return the tests included in this test suite.
    183      */
    184     public static Test suite() throws Exception {
    185         PathableClassLoader cl = new PathableClassLoader(null);
    186         cl.useExplicitLoader("junit.", Test.class.getClassLoader());
    187
    188         // the TestHandler class must be accessable from the System classloader
    189         // in order for java.util.logging.LogManager.readConfiguration to
    190         // be able to instantiate it. And this test case must see the same
    191         // class in order to be able to access its data. Yes this is ugly
    192         // but the whole jdk14 API is a ******* mess anyway.
    193         ClassLoader scl = ClassLoader.getSystemClassLoader();
    194         loadTestHandler(HANDLER_NAME, scl);
    195         cl.useExplicitLoader(HANDLER_NAME, scl);
    196         cl.addLogicalLib("commons-logging");
    197         cl.addLogicalLib("testclasses");
    198
    199         Class testClass = cl.loadClass(CustomConfigTestCase.class.getName());
    200         return new PathableTestSuite(testClass, cl);
    201     }
    202
    203     /**
    204      * Tear down instance variables required by this test case.
    205      */
    206     public void tearDown() {
    207         super.tearDown();
    208         handlers = null;
    209         logger = null;
    210         manager = null;
    211     }
    212
    213
    214     // ----------------------------------------------------------- Test Methods
    215
    216
    217     // Test logging message strings with exceptions
    218     public void testExceptionMessages() throws Exception {
    219
    220         logExceptionMessages();
    221         checkLogRecords(true);
    222
    223     }
    224
    225
    226     // Test logging plain message strings
    227     public void testPlainMessages() throws Exception {
    228
    229         logPlainMessages();
    230         checkLogRecords(false);
    231
    232     }
    233
    234
    235     // Test pristine Handlers instances
    236     public void testPristineHandlers() {
    237
    238         assertNotNull(handlers);
    239         assertEquals(1, handlers.length);
    240         assertTrue(handlers[0] instanceof TestHandler);
    241         assertNotNull(handler);
    242
    243     }
    244
    245
    246     // Test pristine Logger instance
    247     public void testPristineLogger() {
    248
    249         assertNotNull("Logger exists", logger);
    250         assertEquals("Logger name", "TestLogger", logger.getName());
    251
    252         // Assert which logging levels have been enabled
    253         assertTrue(logger.isLoggable(Level.SEVERE));
    254         assertTrue(logger.isLoggable(Level.WARNING));
    255         assertTrue(logger.isLoggable(Level.INFO));
    256         assertTrue(logger.isLoggable(Level.CONFIG));
    257         assertTrue(logger.isLoggable(Level.FINE));
    258         assertTrue(!logger.isLoggable(Level.FINER));
    259         assertTrue(!logger.isLoggable(Level.FINEST));
    260
    261     }
    262
    263
    264     // Test Serializability of Log instance
    265     public void testSerializable() throws Exception {
    266
    267         super.testSerializable();
    268         testExceptionMessages();
    269
    270     }
    271
    272
    273     // -------------------------------------------------------- Support Methods
    274
    275
    276     // Check the log instance
    277     protected void checkLog() {
    278
    279         assertNotNull("Log exists", log);
    280         assertEquals("Log class",
    281                      "org.apache.commons.logging.impl.Jdk14Logger",
    282                      log.getClass().getName());
    283
    284         // Assert which logging levels have been enabled
    285         assertTrue(log.isFatalEnabled());
    286         assertTrue(log.isErrorEnabled());
    287         assertTrue(log.isWarnEnabled());
    288         assertTrue(log.isInfoEnabled());
    289         assertTrue(log.isDebugEnabled());
    290         assertTrue(!log.isTraceEnabled());
    291
    292     }
    293
    294
    295     // Check the recorded messages
    296     protected void checkLogRecords(boolean thrown) {
    297         Iterator records = handler.records();
    298         for (int i = 0; i < testMessages.length; i++) {
    299             assertTrue(records.hasNext());
    300             LogRecord record = (LogRecord) records.next();
    301             assertEquals("LogRecord level",
    302                          testLevels[i], record.getLevel());
    303             assertEquals("LogRecord message",
    304                          testMessages[i], record.getMessage());
    305             assertTrue("LogRecord class",
    306                          record.getSourceClassName().startsWith(
    307                                  "org.apache.commons.logging.jdk14.CustomConfig"));
    308             if (thrown) {
    309                 assertEquals("LogRecord method",
    310                              "logExceptionMessages",
    311                              record.getSourceMethodName());
    312             } else {
    313                 assertEquals("LogRecord method",
    314                              "logPlainMessages",
    315                              record.getSourceMethodName());
    316             }
    317             if (thrown) {
    318                 assertNotNull("LogRecord thrown", record.getThrown());
    319                 assertTrue("LogRecord thrown type",
    320                            record.getThrown() instanceof DummyException);
    321             } else {
    322                 assertNull("LogRecord thrown",
    323                            record.getThrown());
    324             }
    325         }
    326         assertTrue(!records.hasNext());
    327         handler.flush();
    328     }
    329
    330
    331     // Log the messages with exceptions
    332     protected void logExceptionMessages() {
    333         Throwable t = new DummyException();
    334         log.trace("trace", t); // Should not actually get logged
    335         log.debug("debug", t);
    336         log.info("info", t);
    337         log.warn("warn", t);
    338         log.error("error", t);
    339         log.fatal("fatal", t);
    340     }
    341
    342
    343     // Log the plain messages
    344     protected void logPlainMessages() {
    345         log.trace("trace"); // Should not actually get logged
    346         log.debug("debug");
    347         log.info("info");
    348         log.warn("warn");
    349         log.error("error");
    350         log.fatal("fatal");
    351     }
    352
    353
    354     // Set up handlers instance
    355     protected void setUpHandlers() throws Exception {
    356         Logger parent = logger;
    357         while (parent.getParent() != null) {
    358             parent = parent.getParent();
    359         }
    360         handlers = parent.getHandlers();
    361
    362         // The CustomConfig.properties file explicitly defines one handler class
    363         // to be attached to the root logger, so if it isn't there then
    364         // something is badly wrong...
    365         //
    366         // Yes this testing is also done in testPristineHandlers but
    367         // unfortunately:
    368         //  * we need to set up the handlers variable here,
    369         //  * we don't want that to be set up incorrectly, as that can
    370         //    produce weird error messages in other tests, and
    371         //  * we can't rely on testPristineHandlers being the first
    372         //    test to run.
    373         // so we need to test things here too.
    374         assertNotNull("No Handlers defined for JDK14 logging", handlers);
    375         assertEquals("Unexpected number of handlers for JDK14 logging", 1, handlers.length);
    376         assertNotNull("Handler is null", handlers[0]);
    377         assertTrue("Handler not of expected type", handlers[0] instanceof TestHandler);
    378         handler = (TestHandler) handlers[0];
    379     }
    380
    381
    382     // Set up logger instance
    383     protected void setUpLogger(String name) throws Exception {
    384         logger = Logger.getLogger(name);
    385     }
    386
    387
    388     // Set up LogManager instance
    389     protected void setUpManager(String config) throws Exception {
    390         manager = LogManager.getLogManager();
    391         InputStream is =
    392             this.getClass().getClassLoader().getResourceAsStream(config);
    393         manager.readConfiguration(is);
    394         is.close();
    395     }
    396
    397
    398 }