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 }