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