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