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.log4j.xml;
19  
20  import junit.framework.TestCase;
21  import org.apache.log4j.Appender;
22  import org.apache.log4j.FileAppender;
23  import org.apache.log4j.Level;
24  import org.apache.log4j.LogManager;
25  import org.apache.log4j.Logger;
26  import org.apache.log4j.VectorAppender;
27  import org.apache.log4j.spi.ErrorHandler;
28  import org.apache.log4j.spi.LoggerFactory;
29  import org.apache.log4j.spi.LoggingEvent;
30  import org.apache.log4j.spi.ThrowableRenderer;
31  import org.apache.log4j.spi.OptionHandler;
32  import org.apache.log4j.spi.ThrowableRendererSupport;
33  import org.apache.log4j.util.Compare;
34  import org.apache.log4j.util.ControlFilter;
35  import org.apache.log4j.util.Filter;
36  import org.apache.log4j.util.ISO8601Filter;
37  import org.apache.log4j.util.JunitTestRunnerFilter;
38  import org.apache.log4j.util.LineNumberFilter;
39  import org.apache.log4j.util.SunReflectFilter;
40  import org.apache.log4j.util.Transformer;
41  
42  import java.io.File;
43  import java.io.FileInputStream;
44  import java.io.FileOutputStream;
45  import java.io.IOException;
46  import java.io.InputStream;
47  import java.net.URL;
48  import java.util.zip.ZipEntry;
49  import java.util.zip.ZipOutputStream;
50  
51  public class DOMTestCase extends TestCase {
52  
53    static String TEMP_A1 = "output/temp.A1";
54    static String TEMP_A2 = "output/temp.A2";
55    static String FILTERED_A1 = "output/filtered.A1";
56    static String FILTERED_A2 = "output/filtered.A2";
57  
58  
59    static String EXCEPTION1 = "java.lang.Exception: Just testing";
60    static String EXCEPTION2 = "\\s*at .*\\(.*\\)";
61    static String EXCEPTION3 = "\\s*at .*\\(Native Method\\)";
62    static String EXCEPTION4 = "\\s*at .*\\(.*Compiled Code\\)";
63    static String EXCEPTION5 = "\\s*at .*\\(.*libgcj.*\\)";
64  
65  
66    static String TEST1_1A_PAT = 
67                         "(TRACE|DEBUG|INFO |WARN |ERROR|FATAL) \\w*\\.\\w* - Message \\d";
68  
69    static String TEST1_1B_PAT = "(TRACE|DEBUG|INFO |WARN |ERROR|FATAL) root - Message \\d";
70  
71    static String TEST1_2_PAT = "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2},\\d{3} "+
72                          "\\[main]\\ (TRACE|DEBUG|INFO|WARN|ERROR|FATAL) .* - Message \\d";
73  
74  
75  
76    Logger root; 
77    Logger logger;
78  
79    public DOMTestCase(String name) {
80      super(name);
81    }
82  
83    public void setUp() {
84      root = Logger.getRootLogger();
85      logger = Logger.getLogger(DOMTestCase.class);
86    }
87  
88    public void tearDown() {  
89      root.getLoggerRepository().resetConfiguration();
90    }
91  
92    public void test1() throws Exception {
93      DOMConfigurator.configure("input/xml/DOMTestCase1.xml");
94      common();
95  
96      ControlFilter cf1 = new ControlFilter(new String[]{TEST1_1A_PAT, TEST1_1B_PAT, 
97  					       EXCEPTION1, EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
98  
99      ControlFilter cf2 = new ControlFilter(new String[]{TEST1_2_PAT, 
100 					       EXCEPTION1, EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
101 
102     Transformer.transform(
103       TEMP_A1, FILTERED_A1,
104       new Filter[] {
105         cf1, new LineNumberFilter(), new SunReflectFilter(),
106         new JunitTestRunnerFilter()
107       });
108 
109     Transformer.transform(
110       TEMP_A2, FILTERED_A2,
111       new Filter[] {
112         cf2, new LineNumberFilter(), new ISO8601Filter(),
113         new SunReflectFilter(), new JunitTestRunnerFilter()
114       });
115 
116     assertTrue(Compare.compare(FILTERED_A1, "witness/dom.A1.1"));
117     assertTrue(Compare.compare(FILTERED_A2, "witness/dom.A2.1"));
118   }
119   
120   /**
121    *   Tests processing of external entities in XML file.
122    */
123   public void test4() throws Exception {
124     DOMConfigurator.configure("input/xml/DOMTest4.xml");
125     common();
126 
127     ControlFilter cf1 = new ControlFilter(new String[]{TEST1_1A_PAT, TEST1_1B_PAT, 
128 					       EXCEPTION1, EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
129 
130     ControlFilter cf2 = new ControlFilter(new String[]{TEST1_2_PAT, 
131 					       EXCEPTION1, EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
132 
133     Transformer.transform(
134       TEMP_A1 + ".4", FILTERED_A1 + ".4",
135       new Filter[] {
136         cf1, new LineNumberFilter(), new SunReflectFilter(),
137         new JunitTestRunnerFilter()
138       });
139 
140     Transformer.transform(
141       TEMP_A2 + ".4", FILTERED_A2 + ".4",
142       new Filter[] {
143         cf2, new LineNumberFilter(), new ISO8601Filter(),
144         new SunReflectFilter(), new JunitTestRunnerFilter()
145       });
146 
147     assertTrue(Compare.compare(FILTERED_A1 + ".4", "witness/dom.A1.4"));
148     assertTrue(Compare.compare(FILTERED_A2 + ".4", "witness/dom.A2.4"));
149   }
150 
151   void common() {
152     String oldThreadName = Thread.currentThread().getName();
153     Thread.currentThread().setName("main");
154 
155     int i = -1;
156  
157     logger.trace("Message " + ++i);
158     root.trace("Message " + i);  
159  
160     logger.debug("Message " + ++i);
161     root.debug("Message " + i);        
162 
163     logger.info ("Message " + ++i);
164     root.info("Message " + i);        
165 
166     logger.warn ("Message " + ++i);
167     root.warn("Message " + i);        
168 
169     logger.error("Message " + ++i);
170     root.error("Message " + i);
171     
172     logger.log(Level.FATAL, "Message " + ++i);
173     root.log(Level.FATAL, "Message " + i);    
174     
175     Exception e = new Exception("Just testing");
176     logger.debug("Message " + ++i, e);
177     root.debug("Message " + i, e);
178     
179     logger.error("Message " + ++i, e);
180     root.error("Message " + i, e);    
181 
182     Thread.currentThread().setName(oldThreadName);
183   }
184 
185 
186     /**
187      * CustomLogger implementation for testCategoryFactory1 and 2.
188      */
189   private static class CustomLogger extends Logger {
190         /**
191          * Creates new instance.
192          * @param name logger name.
193          */
194       public CustomLogger(final String name) {
195           super(name);
196       }
197   }
198 
199     /**
200      * Creates new instances of CustomLogger.
201      */
202   public static class CustomLoggerFactory implements LoggerFactory {
203         /**
204          * Addivity, expected to be set false in configuration file.
205          */
206       private boolean additivity;
207 
208         /**
209          * Create new instance of factory.
210          */
211       public CustomLoggerFactory() {
212           additivity = true;
213       }
214 
215         /**
216          * Create new logger.
217          * @param name logger name.
218          * @return new logger.
219          */
220       public Logger makeNewLoggerInstance(final String name) {
221           Logger logger = new CustomLogger(name);
222           assertFalse(additivity);
223           return logger;
224       }
225 
226         /**
227          * Set additivity.
228          * @param newVal new value of additivity.
229          */
230       public void setAdditivity(final boolean newVal) {
231           additivity = newVal;
232       }
233   }
234 
235     /**
236      * CustomErrorHandler for testCategoryFactory2.
237      */
238   public static class CustomErrorHandler implements ErrorHandler {
239       public CustomErrorHandler() {}
240       public void activateOptions() {}
241       public void setLogger(final Logger logger) {}
242       public void error(String message, Exception e, int errorCode) {}
243       public void error(String message) {}
244       public void error(String message, Exception e, int errorCode, LoggingEvent event) {}
245       public void setAppender(Appender appender) {}
246       public void setBackupAppender(Appender appender) {}
247   }
248 
249     /**
250      * Tests that loggers mentioned in logger elements
251      *    use the specified categoryFactory.  See bug 33708.
252      */
253   public void testCategoryFactory1() {
254       DOMConfigurator.configure("input/xml/categoryfactory1.xml");
255       //
256       //   logger explicitly mentioned in configuration,
257       //         should be a CustomLogger
258       Logger logger1 = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testCategoryFactory1.1");
259       assertTrue(logger1 instanceof CustomLogger);
260       //
261       //   logger not explicitly mentioned in configuration,
262       //         should use default factory
263       Logger logger2 = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testCategoryFactory1.2");
264       assertFalse(logger2 instanceof CustomLogger);
265   }
266 
267     /**
268      * Tests that loggers mentioned in logger-ref elements
269      *    use the specified categoryFactory.  See bug 33708.
270      */
271     public void testCategoryFactory2() {
272         DOMConfigurator.configure("input/xml/categoryfactory2.xml");
273         //
274         //   logger explicitly mentioned in configuration,
275         //         should be a CustomLogger
276         Logger logger1 = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testCategoryFactory2.1");
277         assertTrue(logger1 instanceof CustomLogger);
278         //
279         //   logger not explicitly mentioned in configuration,
280         //         should use default factory
281         Logger logger2 = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testCategoryFactory2.2");
282         assertFalse(logger2 instanceof CustomLogger);
283     }
284 
285     /**
286      * Tests that loggers mentioned in logger elements
287      *    use the specified loggerFactory.  See bug 33708.
288      */
289   public void testLoggerFactory1() {
290       DOMConfigurator.configure("input/xml/loggerfactory1.xml");
291       //
292       //   logger explicitly mentioned in configuration,
293       //         should be a CustomLogger
294       Logger logger1 = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testLoggerFactory1.1");
295       assertTrue(logger1 instanceof CustomLogger);
296       //
297       //   logger not explicitly mentioned in configuration,
298       //         should use default factory
299       Logger logger2 = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testLoggerFactory1.2");
300       assertFalse(logger2 instanceof CustomLogger);
301   }
302 
303     /**
304      * Tests that reset="true" on log4j:configuration element resets
305      * repository before configuration.
306      * @throws Exception thrown on error.
307      */
308   public void testReset() throws Exception {
309       VectorAppender appender = new VectorAppender();
310       appender.setName("V1");
311       Logger.getRootLogger().addAppender(appender);
312       DOMConfigurator.configure("input/xml/testReset.xml");
313       assertNull(Logger.getRootLogger().getAppender("V1"));
314   }
315 
316 
317     /**
318      * Test checks that configureAndWatch does initial configuration, see bug 33502.
319       * @throws Exception if IO error.
320      */
321   public void testConfigureAndWatch() throws Exception {
322     DOMConfigurator.configureAndWatch("input/xml/DOMTestCase1.xml");
323     assertNotNull(Logger.getRootLogger().getAppender("A1"));
324   }
325 
326 
327     /**
328      * This test checks that the subst method of an extending class
329      * is checked when evaluating parameters.  See bug 43325.
330      *
331      */
332   public void testOverrideSubst() {
333       DOMConfigurator configurator = new DOMConfigurator() {
334           protected String subst(final String value) {
335               if ("output/temp.A1".equals(value)) {
336                   return "output/subst-test.A1";
337               }
338               return value;
339           }
340       };
341       configurator.doConfigure("input/xml/DOMTestCase1.xml", LogManager.getLoggerRepository());
342       FileAppender a1 = (FileAppender) Logger.getRootLogger().getAppender("A1");
343       String file = a1.getFile();
344       assertEquals("output/subst-test.A1", file);
345   }
346 
347     /**
348      * Mock ThrowableRenderer for testThrowableRenderer.  See bug 45721.
349      */
350     public static class MockThrowableRenderer implements ThrowableRenderer, OptionHandler {
351         private boolean activated = false;
352         private boolean showVersion = true;
353 
354         public MockThrowableRenderer() {
355         }
356 
357         public void activateOptions() {
358             activated = true;
359         }
360 
361         public boolean isActivated() {
362             return activated;
363         }
364 
365         public String[] doRender(final Throwable t) {
366             return new String[0];
367         }
368 
369         public void setShowVersion(boolean v) {
370             showVersion = v;
371         }
372 
373         public boolean getShowVersion() {
374             return showVersion;
375         }
376     }
377 
378     /**
379      * Test of log4j.throwableRenderer support.  See bug 45721.
380      */
381     public void testThrowableRenderer1() {
382         DOMConfigurator.configure("input/xml/throwableRenderer1.xml");
383         ThrowableRendererSupport repo = (ThrowableRendererSupport) LogManager.getLoggerRepository();
384         MockThrowableRenderer renderer = (MockThrowableRenderer) repo.getThrowableRenderer();
385         LogManager.resetConfiguration();
386         assertNotNull(renderer);
387         assertEquals(true, renderer.isActivated());
388         assertEquals(false, renderer.getShowVersion());
389     }
390 
391     /**
392      * Test for bug 47465.
393      * configure(URL) did not close opened JarURLConnection.
394      * @throws IOException if IOException creating properties jar.
395      */
396     public void testJarURL() throws IOException {
397         File input = new File("input/xml/defaultInit.xml");
398         System.out.println(input.getAbsolutePath());
399         InputStream is = new FileInputStream(input);
400         File dir = new File("output");
401         dir.mkdirs();
402         File file = new File("output/xml.jar");
403         ZipOutputStream zos =
404             new ZipOutputStream(new FileOutputStream(file));
405         zos.putNextEntry(new ZipEntry("log4j.xml"));
406         int len;
407         byte[] buf = new byte[1024];
408         while ((len = is.read(buf)) > 0) {
409             zos.write(buf, 0, len);
410         }
411         zos.closeEntry();
412         zos.close();
413         URL url = new URL("jar:" + file.toURL() + "!/log4j.xml");
414         DOMConfigurator.configure(url);
415         assertTrue(file.delete());
416         assertFalse(file.exists());
417     }
418 
419 }