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.logging.log4j.core.jmx;
18  
19  import java.beans.PropertyChangeEvent;
20  import java.beans.PropertyChangeListener;
21  import java.io.ByteArrayInputStream;
22  import java.io.File;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.InputStreamReader;
26  import java.io.PrintWriter;
27  import java.io.Reader;
28  import java.io.StringWriter;
29  import java.net.URI;
30  import java.net.URISyntaxException;
31  import java.util.Map;
32  import java.util.concurrent.Executor;
33  import java.util.concurrent.atomic.AtomicLong;
34  
35  import javax.management.MBeanNotificationInfo;
36  import javax.management.Notification;
37  import javax.management.NotificationBroadcasterSupport;
38  import javax.management.ObjectName;
39  
40  import org.apache.logging.log4j.core.LoggerContext;
41  import org.apache.logging.log4j.core.config.Configuration;
42  import org.apache.logging.log4j.core.config.ConfigurationFactory;
43  import org.apache.logging.log4j.core.config.ConfigurationFactory.ConfigurationSource;
44  import org.apache.logging.log4j.status.StatusLogger;
45  
46  /**
47   * Implementation of the {@code LoggerContextAdminMBean} interface.
48   */
49  public class LoggerContextAdmin extends NotificationBroadcasterSupport
50          implements LoggerContextAdminMBean, PropertyChangeListener {
51      private static final int PAGE = 4 * 1024;
52      private static final int TEXT_BUFFER = 64 * 1024;
53      private static final int BUFFER_SIZE = 2048;
54      private static final StatusLogger LOGGER = StatusLogger.getLogger();
55  
56      private final AtomicLong sequenceNo = new AtomicLong();
57      private final ObjectName objectName;
58      private final LoggerContext loggerContext;
59      private String customConfigText;
60  
61      /**
62       * Constructs a new {@code LoggerContextAdmin} with the {@code Executor} to
63       * be used for sending {@code Notification}s asynchronously to listeners.
64       * 
65       * @param executor
66       */
67      public LoggerContextAdmin(LoggerContext loggerContext, Executor executor) {
68          super(executor, createNotificationInfo());
69          this.loggerContext = Assert.isNotNull(loggerContext, "loggerContext");
70          try {
71              String ctxName = Server.escape(loggerContext.getName());
72              String name = String.format(PATTERN, ctxName);
73              objectName = new ObjectName(name);
74          } catch (Exception e) {
75              throw new IllegalStateException(e);
76          }
77          loggerContext.addPropertyChangeListener(this);
78      }
79  
80      private static MBeanNotificationInfo createNotificationInfo() {
81          String[] notifTypes = new String[] { NOTIF_TYPE_RECONFIGURED };
82          String name = Notification.class.getName();
83          String description = "Configuration reconfigured";
84          return new MBeanNotificationInfo(notifTypes, name, description);
85      }
86  
87      @Override
88      public String getStatus() {
89          return loggerContext.getStatus().toString();
90      }
91  
92      @Override
93      public String getName() {
94          return loggerContext.getName();
95      }
96  
97      private Configuration getConfig() {
98          return loggerContext.getConfiguration();
99      }
100 
101     @Override
102     public String getConfigLocationURI() {
103         if (loggerContext.getConfigLocation() != null) {
104             return String.valueOf(loggerContext.getConfigLocation());
105         }
106         if (getConfigName() != null) {
107             return String.valueOf(new File(getConfigName()).toURI());
108         }
109         return "";
110     }
111 
112     @Override
113     public void setConfigLocationURI(String configLocation)
114             throws URISyntaxException, IOException {
115         LOGGER.debug("---------");
116         LOGGER.debug("Remote request to reconfigure using location "
117                 + configLocation);
118         URI uri = new URI(configLocation);
119 
120         // validate the location first: invalid location will result in
121         // default configuration being configured, try to avoid that...
122         uri.toURL().openStream().close();
123 
124         loggerContext.setConfigLocation(uri);
125         LOGGER.debug("Completed remote request to reconfigure.");
126     }
127 
128     @Override
129     public void propertyChange(PropertyChangeEvent evt) {
130         if (!LoggerContext.PROPERTY_CONFIG.equals(evt.getPropertyName())) {
131             return;
132         }
133         // erase custom text if new configuration was read from a location
134         if (loggerContext.getConfiguration().getName() != null) {
135             customConfigText = null;
136         }
137         Notification notif = new Notification(NOTIF_TYPE_RECONFIGURED,
138                 getObjectName(), nextSeqNo(), now(), null);
139         sendNotification(notif);
140     }
141 
142     @Override
143     public String getConfigText() throws IOException {
144         if (customConfigText != null) {
145             return customConfigText;
146         }
147         try {
148             return readContents(new URI(getConfigLocationURI()));
149         } catch (Exception ex) {
150             StringWriter sw = new StringWriter(BUFFER_SIZE);
151             ex.printStackTrace(new PrintWriter(sw));
152             return sw.toString();
153         }
154     }
155 
156     @Override
157     public void setConfigText(String configText, String charsetName) {
158         String old = customConfigText;
159         customConfigText = Assert.isNotNull(configText, "configText");
160         LOGGER.debug("---------");
161         LOGGER.debug("Remote request to reconfigure from config text.");
162 
163         try {
164             InputStream in = new ByteArrayInputStream(
165                     configText.getBytes(charsetName));
166             ConfigurationSource source = new ConfigurationSource(in);
167             Configuration updated = ConfigurationFactory.getInstance()
168                     .getConfiguration(source);
169             loggerContext.setConfiguration(updated);
170             LOGGER.debug("Completed remote request to reconfigure from config text.");
171         } catch (Exception ex) {
172             customConfigText = old;
173             String msg = "Could not reconfigure from config text";
174             LOGGER.error(msg, ex);
175             throw new IllegalArgumentException(msg, ex);
176         }
177     }
178 
179     private String readContents(URI uri) throws IOException {
180         InputStream in = null;
181         try {
182             in = uri.toURL().openStream();
183             Reader reader = new InputStreamReader(in);
184             StringBuilder result = new StringBuilder(TEXT_BUFFER);
185             char[] buff = new char[PAGE];
186             int count = -1;
187             while ((count = reader.read(buff)) >= 0) {
188                 result.append(buff, 0, count);
189             }
190             return result.toString();
191         } finally {
192             try {
193                 in.close();
194             } catch (Exception ignored) {
195             }
196         }
197     }
198 
199     @Override
200     public String getConfigName() {
201         return getConfig().getName();
202     }
203 
204     @Override
205     public String getConfigClassName() {
206         return getConfig().getClass().getName();
207     }
208 
209     @Override
210     public String getConfigFilter() {
211         return String.valueOf(getConfig().getFilter());
212     }
213 
214     @Override
215     public String getConfigMonitorClassName() {
216         return getConfig().getConfigurationMonitor().getClass().getName();
217     }
218 
219     @Override
220     public Map<String, String> getConfigProperties() {
221         return getConfig().getProperties();
222     }
223 
224     /** @see LoggerContextAdminMBean#PATTERN */
225     public ObjectName getObjectName() {
226         return objectName;
227     }
228 
229     private long nextSeqNo() {
230         return sequenceNo.getAndIncrement();
231     }
232 
233     private long now() {
234         return System.currentTimeMillis();
235     }
236 }