001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     * 
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     * 
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */ 
017    
018    package org.apache.commons.logging.jdk14;
019    
020    
021    import java.io.ByteArrayOutputStream;
022    import java.io.InputStream;
023    import java.lang.reflect.Method;
024    import java.util.Iterator;
025    import java.util.logging.Handler;
026    import java.util.logging.Level;
027    import java.util.logging.LogManager;
028    import java.util.logging.LogRecord;
029    import java.util.logging.Logger;
030    
031    import junit.framework.Test;
032    
033    import org.apache.commons.logging.DummyException;
034    import org.apache.commons.logging.PathableClassLoader;
035    import org.apache.commons.logging.PathableTestSuite;
036    
037    
038    /**
039     * <p>TestCase for JDK 1.4 logging when running on a JDK 1.4 system with
040     * custom configuration, so that JDK 1.4 should be selected and an appropriate
041     * logger configured per the configuration properties.</p>
042     *
043     * @author Craig R. McClanahan
044     * @version $Revision: 568760 $ $Date: 2007-08-23 00:19:45 +0200 (to, 23 aug 2007) $
045     */
046    
047    public class CustomConfigTestCase extends DefaultConfigTestCase {
048    
049        protected static final String HANDLER_NAME 
050            = "org.apache.commons.logging.jdk14.TestHandler";
051    
052        // ----------------------------------------------------------- Constructors
053    
054    
055        /**
056         * <p>Construct a new instance of this test case.</p>
057         *
058         * @param name Name of the test case
059         */
060        public CustomConfigTestCase(String name) {
061            super(name);
062        }
063    
064    
065        // ----------------------------------------------------- Instance Variables
066    
067    
068        /**
069         * <p>The customized <code>Handler</code> we will be using.</p>
070         */
071        protected TestHandler handler = null;
072    
073    
074        /**
075         * <p>The underlying <code>Handler</code>s we will be using.</p>
076         */
077        protected Handler handlers[] = null;
078    
079    
080        /**
081         * <p>The underlying <code>Logger</code> we will be using.</p>
082         */
083        protected Logger logger = null;
084    
085    
086        /**
087         * <p>The underlying <code>LogManager</code> we will be using.</p>
088         */
089        protected LogManager manager = null;
090    
091    
092        /**
093         * <p>The message levels that should have been logged.</p>
094         */
095        protected Level testLevels[] =
096        { Level.FINE, Level.INFO, Level.WARNING, Level.SEVERE, Level.SEVERE };
097    
098    
099        /**
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    }