1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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.nio.charset.Charset;
32 import java.util.Map;
33 import java.util.concurrent.Executor;
34 import java.util.concurrent.atomic.AtomicLong;
35
36 import javax.management.MBeanNotificationInfo;
37 import javax.management.Notification;
38 import javax.management.NotificationBroadcasterSupport;
39 import javax.management.ObjectName;
40
41 import org.apache.logging.log4j.core.LoggerContext;
42 import org.apache.logging.log4j.core.config.Configuration;
43 import org.apache.logging.log4j.core.config.ConfigurationFactory;
44 import org.apache.logging.log4j.core.config.ConfigurationFactory.ConfigurationSource;
45 import org.apache.logging.log4j.core.helpers.Assert;
46 import org.apache.logging.log4j.core.helpers.Charsets;
47 import org.apache.logging.log4j.core.helpers.Closer;
48 import org.apache.logging.log4j.core.helpers.FileUtils;
49 import org.apache.logging.log4j.status.StatusLogger;
50
51
52
53
54 public class LoggerContextAdmin extends NotificationBroadcasterSupport
55 implements LoggerContextAdminMBean, PropertyChangeListener {
56 private static final int PAGE = 4 * 1024;
57 private static final int TEXT_BUFFER = 64 * 1024;
58 private static final int BUFFER_SIZE = 2048;
59 private static final StatusLogger LOGGER = StatusLogger.getLogger();
60
61 private final AtomicLong sequenceNo = new AtomicLong();
62 private final ObjectName objectName;
63 private final LoggerContext loggerContext;
64 private String customConfigText;
65
66
67
68
69
70
71
72
73 public LoggerContextAdmin(final LoggerContext loggerContext, final Executor executor) {
74 super(executor, createNotificationInfo());
75 this.loggerContext = Assert.isNotNull(loggerContext, "loggerContext");
76 try {
77 final String ctxName = Server.escape(loggerContext.getName());
78 final String name = String.format(PATTERN, ctxName);
79 objectName = new ObjectName(name);
80 } catch (final Exception e) {
81 throw new IllegalStateException(e);
82 }
83 loggerContext.addPropertyChangeListener(this);
84 }
85
86 private static MBeanNotificationInfo createNotificationInfo() {
87 final String[] notifTypes = new String[] {
88 NOTIF_TYPE_RECONFIGURED };
89 final String name = Notification.class.getName();
90 final String description = "Configuration reconfigured";
91 return new MBeanNotificationInfo(notifTypes, name, description);
92 }
93
94 @Override
95 public String getStatus() {
96 return loggerContext.getStatus().toString();
97 }
98
99 @Override
100 public String getName() {
101 return loggerContext.getName();
102 }
103
104 private Configuration getConfig() {
105 return loggerContext.getConfiguration();
106 }
107
108 @Override
109 public String getConfigLocationURI() {
110 if (loggerContext.getConfigLocation() != null) {
111 return String.valueOf(loggerContext.getConfigLocation());
112 }
113 if (getConfigName() != null) {
114 return String.valueOf(new File(getConfigName()).toURI());
115 }
116 return "";
117 }
118
119 @Override
120 public void setConfigLocationURI(final String configLocation)
121 throws URISyntaxException, IOException {
122 LOGGER.debug("---------");
123 LOGGER.debug("Remote request to reconfigure using location "
124 + configLocation);
125 final URI uri = FileUtils.getCorrectedFilePathUri(configLocation);
126
127
128
129 uri.toURL().openStream().close();
130
131 loggerContext.setConfigLocation(uri);
132 LOGGER.debug("Completed remote request to reconfigure.");
133 }
134
135 @Override
136 public void propertyChange(final PropertyChangeEvent evt) {
137 if (!LoggerContext.PROPERTY_CONFIG.equals(evt.getPropertyName())) {
138 return;
139 }
140
141 if (loggerContext.getConfiguration().getName() != null) {
142 customConfigText = null;
143 }
144 final Notification notif = new Notification(NOTIF_TYPE_RECONFIGURED,
145 getObjectName(), nextSeqNo(), now(), null);
146 sendNotification(notif);
147 }
148
149 @Override
150 public String getConfigText() throws IOException {
151 return getConfigText(Charsets.UTF_8.name());
152 }
153
154 @Override
155 public String getConfigText(final String charsetName) throws IOException {
156 if (customConfigText != null) {
157 return customConfigText;
158 }
159 try {
160 final Charset charset = Charset.forName(charsetName);
161 return readContents(FileUtils.getCorrectedFilePathUri(getConfigLocationURI()), charset);
162 } catch (final Exception ex) {
163 final StringWriter sw = new StringWriter(BUFFER_SIZE);
164 ex.printStackTrace(new PrintWriter(sw));
165 return sw.toString();
166 }
167 }
168
169 @Override
170 public void setConfigText(final String configText, final String charsetName) {
171 final String old = customConfigText;
172 customConfigText = Assert.isNotNull(configText, "configText");
173 LOGGER.debug("---------");
174 LOGGER.debug("Remote request to reconfigure from config text.");
175
176 try {
177 final InputStream in = new ByteArrayInputStream(
178 configText.getBytes(charsetName));
179 final ConfigurationSource source = new ConfigurationSource(in);
180 final Configuration updated = ConfigurationFactory.getInstance()
181 .getConfiguration(source);
182 loggerContext.start(updated);
183 LOGGER.debug("Completed remote request to reconfigure from config text.");
184 } catch (final Exception ex) {
185 customConfigText = old;
186 final String msg = "Could not reconfigure from config text";
187 LOGGER.error(msg, ex);
188 throw new IllegalArgumentException(msg, ex);
189 }
190 }
191
192
193
194
195
196
197
198
199 private String readContents(final URI uri, final Charset charset) throws IOException {
200 InputStream in = null;
201 Reader reader = null;
202 try {
203 in = uri.toURL().openStream();
204 reader = new InputStreamReader(in, charset);
205 final StringBuilder result = new StringBuilder(TEXT_BUFFER);
206 final char[] buff = new char[PAGE];
207 int count = -1;
208 while ((count = reader.read(buff)) >= 0) {
209 result.append(buff, 0, count);
210 }
211 return result.toString();
212 } finally {
213 Closer.closeSilent(in);
214 Closer.closeSilent(reader);
215 }
216 }
217
218 @Override
219 public String getConfigName() {
220 return getConfig().getName();
221 }
222
223 @Override
224 public String getConfigClassName() {
225 return getConfig().getClass().getName();
226 }
227
228 @Override
229 public String getConfigFilter() {
230 return String.valueOf(getConfig().getFilter());
231 }
232
233 @Override
234 public String getConfigMonitorClassName() {
235 return getConfig().getConfigurationMonitor().getClass().getName();
236 }
237
238 @Override
239 public Map<String, String> getConfigProperties() {
240 return getConfig().getProperties();
241 }
242
243
244
245
246
247
248
249 @Override
250 public ObjectName getObjectName() {
251 return objectName;
252 }
253
254 private long nextSeqNo() {
255 return sequenceNo.getAndIncrement();
256 }
257
258 private long now() {
259 return System.currentTimeMillis();
260 }
261 }