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.InputStream;
21  import java.lang.reflect.Method;
22  import java.util.Iterator;
23  import java.util.logging.Handler;
24  import java.util.logging.Level;
25  import java.util.logging.LogManager;
26  import java.util.logging.LogRecord;
27  import java.util.logging.Logger;
28  
29  import junit.framework.Test;
30  
31  import org.apache.commons.io.IOUtils;
32  import org.apache.commons.logging.DummyException;
33  import org.apache.commons.logging.PathableClassLoader;
34  import org.apache.commons.logging.PathableTestSuite;
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          try (InputStream is = srcCL.getResourceAsStream(resName)) {
89              System.err.println("Reading resource [" + resName + "]");
90              return IOUtils.toByteArray(is);
91          }
92      }
93  
94      /**
95       * Return the tests included in this test suite.
96       */
97      public static Test suite() throws Exception {
98          final PathableClassLoader cl = new PathableClassLoader(null);
99          cl.useExplicitLoader("junit.", Test.class.getClassLoader());
100 
101         // the TestHandler class must be accessible from the System class loader
102         // in order for java.util.logging.LogManager.readConfiguration to
103         // be able to instantiate it. And this test case must see the same
104         // class in order to be able to access its data. Yes this is ugly
105         // but the whole jdk14 API is a ******* mess anyway.
106         final ClassLoader scl = ClassLoader.getSystemClassLoader();
107         loadTestHandler(HANDLER_NAME, scl);
108         cl.useExplicitLoader(HANDLER_NAME, scl);
109         cl.addLogicalLib("commons-logging");
110         cl.addLogicalLib("testclasses");
111 
112         final Class<?> testClass = cl.loadClass(CustomConfigTestCase.class.getName());
113         return new PathableTestSuite(testClass, cl);
114     }
115 
116     /**
117      * <p>The customized {@code Handler} we will be using.</p>
118      */
119     protected TestHandler handler;
120 
121     /**
122      * <p>The underlying {@code Handler}s we will be using.</p>
123      */
124     protected Handler handlers[];
125 
126     /**
127      * <p>The underlying {@code Logger} we will be using.</p>
128      */
129     protected Logger logger;
130 
131     /**
132      * <p>The underlying {@code LogManager} we will be using.</p>
133      */
134     protected LogManager manager;
135 
136     /**
137      * <p>The message levels that should have been logged.</p>
138      */
139     protected Level[] testLevels =
140     { Level.FINE, Level.INFO, Level.WARNING, Level.SEVERE, Level.SEVERE };
141 
142     /**
143      * <p>The message strings that should have been logged.</p>
144      */
145     protected String[] testMessages =
146     { "debug", "info", "warn", "error", "fatal" };
147 
148     /**
149      * <p>Construct a new instance of this test case.</p>
150      *
151      * @param name Name of the test case
152      */
153     public CustomConfigTestCase(final String name) {
154         super(name);
155     }
156 
157     // Check the log instance
158     @Override
159     protected void checkLog() {
160 
161         assertNotNull("Log exists", log);
162         assertEquals("Log class",
163                      "org.apache.commons.logging.impl.Jdk14Logger",
164                      log.getClass().getName());
165 
166         // Assert which logging levels have been enabled
167         assertTrue(log.isFatalEnabled());
168         assertTrue(log.isErrorEnabled());
169         assertTrue(log.isWarnEnabled());
170         assertTrue(log.isInfoEnabled());
171         assertTrue(log.isDebugEnabled());
172         assertFalse(log.isTraceEnabled());
173 
174     }
175 
176     // Check the recorded messages
177     protected void checkLogRecords(final boolean thrown) {
178         final Iterator<LogRecord> records = handler.records();
179         for (int i = 0; i < testMessages.length; i++) {
180             assertTrue(records.hasNext());
181             final LogRecord record = records.next();
182             assertEquals("LogRecord level",
183                          testLevels[i], record.getLevel());
184             assertEquals("LogRecord message",
185                          testMessages[i], record.getMessage());
186             assertTrue("LogRecord class",
187                          record.getSourceClassName().startsWith(
188                                  "org.apache.commons.logging.jdk14.CustomConfig"));
189             if (thrown) {
190                 assertEquals("LogRecord method",
191                              "logExceptionMessages",
192                              record.getSourceMethodName());
193             } else {
194                 assertEquals("LogRecord method",
195                              "logPlainMessages",
196                              record.getSourceMethodName());
197             }
198             if (thrown) {
199                 assertNotNull("LogRecord thrown", record.getThrown());
200                 assertTrue("LogRecord thrown type",
201                            record.getThrown() instanceof DummyException);
202             } else {
203                 assertNull("LogRecord thrown",
204                            record.getThrown());
205             }
206         }
207         assertFalse(records.hasNext());
208         handler.flush();
209     }
210 
211     // Log the messages with exceptions
212     protected void logExceptionMessages() {
213         final Throwable t = new DummyException();
214         log.trace("trace", t); // Should not actually get logged
215         log.debug("debug", t);
216         log.info("info", t);
217         log.warn("warn", t);
218         log.error("error", t);
219         log.fatal("fatal", t);
220     }
221 
222     // Log the plain messages
223     protected void logPlainMessages() {
224         log.trace("trace"); // Should not actually get logged
225         log.debug("debug");
226         log.info("info");
227         log.warn("warn");
228         log.error("error");
229         log.fatal("fatal");
230     }
231 
232     /**
233      * Sets up instance variables required by this test case.
234      */
235     @Override
236     public void setUp() throws Exception {
237         setUpManager
238             ("org/apache/commons/logging/jdk14/CustomConfig.properties");
239         setUpLogger(this.getClass().getName());
240         setUpHandlers();
241         setUpFactory();
242         setUpLog(this.getClass().getName());
243     }
244 
245     // Set up handlers instance
246     protected void setUpHandlers() throws Exception {
247         Logger parent = logger;
248         while (parent.getParent() != null) {
249             parent = parent.getParent();
250         }
251         handlers = parent.getHandlers();
252 
253         // The CustomConfig.properties file explicitly defines one handler class
254         // to be attached to the root logger, so if it isn't there then
255         // something is badly wrong...
256         //
257         // Yes this testing is also done in testPristineHandlers but
258         // unfortunately:
259         //  * we need to set up the handlers variable here,
260         //  * we don't want that to be set up incorrectly, as that can
261         //    produce weird error messages in other tests, and
262         //  * we can't rely on testPristineHandlers being the first
263         //    test to run.
264         // so we need to test things here too.
265         assertNotNull("No Handlers defined for JDK14 logging", handlers);
266         assertEquals("Unexpected number of handlers for JDK14 logging", 1, handlers.length);
267         assertNotNull("Handler is null", handlers[0]);
268         assertTrue("Handler not of expected type", handlers[0] instanceof TestHandler);
269         handler = (TestHandler) handlers[0];
270     }
271 
272     // Set up logger instance
273     protected void setUpLogger(final String name) throws Exception {
274         logger = Logger.getLogger(name);
275     }
276 
277     // Set up LogManager instance
278     protected void setUpManager(final String config) throws Exception {
279         manager = LogManager.getLogManager();
280         try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(config)) {
281             manager.readConfiguration(is);
282         }
283     }
284 
285     /**
286      * Tear down instance variables required by this test case.
287      */
288     @Override
289     public void tearDown() {
290         super.tearDown();
291         handlers = null;
292         logger = null;
293         manager = null;
294     }
295 
296     // Test logging message strings with exceptions
297     public void testExceptionMessages() throws Exception {
298         logExceptionMessages();
299         checkLogRecords(true);
300     }
301 
302     // Test logging plain message strings
303     public void testPlainMessages() throws Exception {
304         logPlainMessages();
305         checkLogRecords(false);
306     }
307 
308     // Test pristine Handlers instances
309     public void testPristineHandlers() {
310         assertNotNull(handlers);
311         assertEquals(1, handlers.length);
312         assertTrue(handlers[0] instanceof TestHandler);
313         assertNotNull(handler);
314     }
315 
316     // Test pristine Logger instance
317     public void testPristineLogger() {
318         assertNotNull("Logger exists", logger);
319         assertEquals("Logger name", this.getClass().getName(), logger.getName());
320 
321         // Assert which logging levels have been enabled
322         assertTrue(logger.isLoggable(Level.SEVERE));
323         assertTrue(logger.isLoggable(Level.WARNING));
324         assertTrue(logger.isLoggable(Level.INFO));
325         assertTrue(logger.isLoggable(Level.CONFIG));
326         assertTrue(logger.isLoggable(Level.FINE));
327         assertFalse(logger.isLoggable(Level.FINER));
328         assertFalse(logger.isLoggable(Level.FINEST));
329     }
330 
331     // Test Serializability of Log instance
332     @Override
333     public void testSerializable() throws Exception {
334         super.testSerializable();
335         testExceptionMessages();
336     }
337 
338 }