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  package org.apache.log4j.chainsaw.receivers;
18  
19  
20  import org.apache.log4j.LogManager;
21  import org.apache.log4j.Logger;
22  import org.apache.log4j.chainsaw.plugins.PluginClassLoaderFactory;
23  import org.apache.log4j.plugins.Plugin;
24  import org.apache.log4j.plugins.PluginRegistry;
25  import org.apache.log4j.plugins.Receiver;
26  import org.apache.log4j.spi.LoggerRepository;
27  import org.apache.log4j.spi.LoggerRepositoryEx;
28  import org.w3c.dom.Document;
29  import org.w3c.dom.Element;
30  
31  import javax.xml.parsers.DocumentBuilder;
32  import javax.xml.parsers.DocumentBuilderFactory;
33  import javax.xml.transform.OutputKeys;
34  import javax.xml.transform.Transformer;
35  import javax.xml.transform.TransformerFactory;
36  import javax.xml.transform.dom.DOMSource;
37  import javax.xml.transform.stream.StreamResult;
38  import java.beans.BeanInfo;
39  import java.beans.Introspector;
40  import java.beans.PropertyDescriptor;
41  import java.io.*;
42  import java.net.URL;
43  import java.util.ArrayList;
44  import java.util.Arrays;
45  import java.util.Collections;
46  import java.util.List;
47  
48  
49  /**
50   * Helper class to assisit with all the known Receivers.
51   * <p>
52   * A local resource 'known.receivers' is read in on initialization
53   * with each line representing the FQN of the Class that is a recognised Receiver.
54   *
55   * @author Paul Smith &lt;psmith@apache.org&gt;
56   */
57  public class ReceiversHelper {
58  
59      private static final ReceiversHelper instance = new ReceiversHelper();
60  
61      private final Logger logger = LogManager.getLogger(ReceiversHelper.class);
62      private List<Class> receiverClassList = new ArrayList<>();
63  
64      /**
65       *
66       */
67      private ReceiversHelper() {
68  
69          URL url = this.getClass().getClassLoader().getResource(
70              this.getClass().getPackage().getName().replace('.', '/') + "/known.receivers");
71          if (url == null) {
72              logger.warn("Failed to locate known.receivers file");
73              return;
74          }
75          LineNumberReader stream = null;
76          try {
77  
78              stream = new LineNumberReader(new InputStreamReader(url.openStream()));
79              String line;
80              // we need the special Classloader, because under Web start, optional jars might be local
81              // to this workstation
82              ClassLoader classLoader = PluginClassLoaderFactory.getInstance().getClassLoader();
83  
84              while ((line = stream.readLine()) != null) {
85  
86                  try {
87                      if (line.startsWith("#") || (line.length() == 0)) {
88                          continue;
89                      }
90                      Class receiverClass = classLoader.loadClass(line);
91                      receiverClassList.add(receiverClass);
92                      logger.debug("Located known Receiver class " + receiverClass.getName());
93                  } catch (ClassNotFoundException e) {
94                      logger.warn("Failed to locate Receiver class:" + line);
95                  } catch (NoClassDefFoundError e) {
96                      logger.error("Failed to locate Receiver class:" + line + ", looks like a dependent class is missing from the classpath", e);
97                  }
98              }
99          } catch (Exception e) {
100             e.printStackTrace();
101         } finally {
102             if (stream != null) {
103                 try {
104                     stream.close();
105                 } catch (IOException ioe) {
106                     ioe.printStackTrace();
107                 }
108             }
109         }
110     }
111 
112 
113     public static ReceiversHelper getInstance() {
114         return instance;
115     }
116 
117 
118     /**
119      * Returns an unmodifiable list of Class objects which represent all the 'known'
120      * Receiver classes.
121      *
122      * @return known receiver classes
123      */
124     public List getKnownReceiverClasses() {
125         return Collections.unmodifiableList(receiverClassList);
126     }
127 
128 
129     public void saveReceiverConfiguration(File file) {
130         LoggerRepository repo = LogManager.getLoggerRepository();
131         PluginRegistry pluginRegistry = ((LoggerRepositoryEx) repo).getPluginRegistry();
132         List<Plugin> fullPluginList = pluginRegistry.getPlugins();
133         List<Plugin> pluginList = new ArrayList<>();
134         for (Object aFullPluginList : fullPluginList) {
135             Plugin thisPlugin = (Plugin) aFullPluginList;
136             if (thisPlugin instanceof Receiver) {
137                 pluginList.add(thisPlugin);
138             }
139         }
140         //remove everything that isn't a receiver..otherwise, we'd create an empty config file
141         try {
142             if (pluginList.size() > 0) {
143                 //we programmatically register the ZeroConf plugin in the plugin registry
144                 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
145                 factory.setNamespaceAware(true);
146                 DocumentBuilder builder = factory.newDocumentBuilder();
147                 Document document = builder.newDocument();
148                 Element rootElement = document.createElementNS("http://jakarta.apache.org/log4j/", "configuration");
149                 rootElement.setPrefix("log4j");
150                 rootElement.setAttribute("xmlns:log4j", "http://jakarta.apache.org/log4j/");
151                 rootElement.setAttribute("debug", "true");
152 
153                 for (Object aPluginList : pluginList) {
154                     Receiver receiver;
155 
156                     if (aPluginList instanceof Receiver) {
157                         receiver = (Receiver) aPluginList;
158                     } else {
159                         continue;
160                     }
161 
162                     Element pluginElement = document.createElement("plugin");
163                     pluginElement.setAttribute("name", receiver.getName());
164                     pluginElement.setAttribute("class", receiver.getClass().getName());
165 
166                     BeanInfo beanInfo = Introspector.getBeanInfo(receiver.getClass());
167                     List<PropertyDescriptor> list = new ArrayList<>(Arrays.asList(beanInfo.getPropertyDescriptors()));
168 
169                     for (Object aList : list) {
170                         PropertyDescriptor d = (PropertyDescriptor) aList;
171                         //don't serialize the loggerRepository property for subclasses of componentbase..
172                         //easier to change this than tweak componentbase right now..
173                         if (d.getReadMethod().getName().equals("getLoggerRepository")) {
174                             continue;
175                         }
176                         Object o = d.getReadMethod().invoke(receiver);
177                         if (o != null) {
178                             Element paramElement = document.createElement("param");
179                             paramElement.setAttribute("name", d.getName());
180                             paramElement.setAttribute("value", o.toString());
181                             pluginElement.appendChild(paramElement);
182                         }
183                     }
184 
185                     rootElement.appendChild(pluginElement);
186 
187                 }
188 
189                 TransformerFactory transformerFactory = TransformerFactory.newInstance();
190                 Transformer transformer = transformerFactory.newTransformer();
191                 transformer.setOutputProperty(OutputKeys.INDENT, "yes");
192                 transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
193                 DOMSource source = new DOMSource(rootElement);
194                 FileOutputStream stream = new FileOutputStream(file);
195                 StreamResult result = new StreamResult(stream);
196                 transformer.transform(source, result);
197                 stream.close();
198             }
199 
200         } catch (Exception e) {
201             e.printStackTrace();
202         }
203     }
204 }