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 }