1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.config;
18
19 import org.apache.logging.log4j.Level;
20 import org.apache.logging.log4j.core.config.plugins.PluginManager;
21 import org.apache.logging.log4j.core.config.plugins.PluginType;
22 import org.apache.logging.log4j.core.config.plugins.ResolverUtil;
23 import org.apache.logging.log4j.core.helpers.FileUtils;
24 import org.apache.logging.log4j.core.net.Advertiser;
25 import org.apache.logging.log4j.status.StatusConsoleListener;
26 import org.apache.logging.log4j.status.StatusListener;
27 import org.apache.logging.log4j.status.StatusLogger;
28 import org.codehaus.jackson.JsonNode;
29 import org.codehaus.jackson.JsonParser;
30 import org.codehaus.jackson.map.ObjectMapper;
31
32 import java.io.ByteArrayInputStream;
33 import java.io.ByteArrayOutputStream;
34 import java.io.File;
35 import java.io.FileInputStream;
36 import java.io.FileNotFoundException;
37 import java.io.FileOutputStream;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.io.PrintStream;
41 import java.net.URI;
42 import java.net.URISyntaxException;
43 import java.util.ArrayList;
44 import java.util.Iterator;
45 import java.util.List;
46 import java.util.Map;
47
48
49
50
51 public class JSONConfiguration extends BaseConfiguration implements Reconfigurable {
52
53 private static final String[] VERBOSE_CLASSES = new String[] {ResolverUtil.class.getName()};
54
55 private static final int BUF_SIZE = 16384;
56
57 private final List<Status> status = new ArrayList<Status>();
58
59 private JsonNode root;
60
61 private final List<String> messages = new ArrayList<String>();
62
63 private final File configFile;
64
65 public JSONConfiguration(final ConfigurationFactory.ConfigurationSource configSource) {
66 this.configFile = configSource.getFile();
67 byte[] buffer;
68
69 try {
70 final InputStream configStream = configSource.getInputStream();
71 buffer = toByteArray(configStream);
72 configStream.close();
73 final InputStream is = new ByteArrayInputStream(buffer);
74 final ObjectMapper mapper = new ObjectMapper().configure(JsonParser.Feature.ALLOW_COMMENTS, true);
75 root = mapper.readTree(is);
76 if (root.size() == 1) {
77 final Iterator<JsonNode> i = root.getElements();
78 root = i.next();
79 }
80 processAttributes(rootNode, root);
81 Level status = Level.OFF;
82 boolean verbose = false;
83 PrintStream stream = System.out;
84 for (final Map.Entry<String, String> entry : rootNode.getAttributes().entrySet()) {
85 if ("status".equalsIgnoreCase(entry.getKey())) {
86 status = Level.toLevel(getSubst().replace(entry.getValue()), null);
87 if (status == null) {
88 status = Level.ERROR;
89 messages.add("Invalid status specified: " + entry.getValue() + ". Defaulting to ERROR");
90 }
91 } else if ("dest".equalsIgnoreCase(entry.getKey())) {
92 final String dest = entry.getValue();
93 if (dest != null) {
94 if (dest.equalsIgnoreCase("err")) {
95 stream = System.err;
96 } else {
97 try {
98 final File destFile = FileUtils.fileFromURI(new URI(dest));
99 stream = new PrintStream(new FileOutputStream(destFile));
100 } catch (final URISyntaxException use) {
101 System.err.println("Unable to write to " + dest + ". Writing to stdout");
102 }
103 }
104 }
105 } else if ("verbose".equalsIgnoreCase(entry.getKey())) {
106 verbose = Boolean.parseBoolean(getSubst().replace(entry.getValue()));
107 } else if ("packages".equalsIgnoreCase(entry.getKey())) {
108 final String[] packages = getSubst().replace(entry.getValue()).split(",");
109 for (final String p : packages) {
110 PluginManager.addPackage(p);
111 }
112 } else if ("name".equalsIgnoreCase(entry.getKey())) {
113 setName(getSubst().replace(entry.getValue()));
114 } else if ("monitorInterval".equalsIgnoreCase(entry.getKey())) {
115 final int interval = Integer.parseInt(getSubst().replace(entry.getValue()));
116 if (interval > 0 && configFile != null) {
117 monitor = new FileConfigurationMonitor(this, configFile, listeners, interval);
118 }
119 } else if ("advertiser".equalsIgnoreCase(entry.getKey())) {
120 final String advertiserString = getSubst().replace(entry.getValue());
121 if (advertiserString != null)
122 {
123 final PluginType type = getPluginManager().getPluginType(advertiserString);
124 if (type != null)
125 {
126 final Class<Advertiser> clazz = type.getPluginClass();
127 advertiser = clazz.newInstance();
128 }
129 }
130 }
131 }
132
133 final Iterator<StatusListener> statusIter = ((StatusLogger) LOGGER).getListeners();
134 boolean found = false;
135 while (statusIter.hasNext()) {
136 final StatusListener listener = statusIter.next();
137 if (listener instanceof StatusConsoleListener) {
138 found = true;
139 ((StatusConsoleListener) listener).setLevel(status);
140 if (!verbose) {
141 ((StatusConsoleListener) listener).setFilters(VERBOSE_CLASSES);
142 }
143 }
144 }
145 if (!found && status != Level.OFF) {
146 final StatusConsoleListener listener = new StatusConsoleListener(status, stream);
147 if (!verbose) {
148 listener.setFilters(VERBOSE_CLASSES);
149 }
150 ((StatusLogger) LOGGER).registerListener(listener);
151 for (final String msg : messages) {
152 LOGGER.error(msg);
153 }
154 }
155 if (getName() == null) {
156 setName(configSource.getLocation());
157 }
158 } catch (final Exception ex) {
159 LOGGER.error("Error parsing " + configSource.getLocation(), ex);
160 ex.printStackTrace();
161 }
162 }
163
164 @Override
165 public void setup() {
166 final Iterator<Map.Entry<String, JsonNode>> iter = root.getFields();
167 final List<Node> children = rootNode.getChildren();
168 while (iter.hasNext()) {
169 final Map.Entry<String, JsonNode> entry = iter.next();
170 final JsonNode n = entry.getValue();
171 if (n.isObject()) {
172 LOGGER.debug("Processing node for object " + entry.getKey());
173 children.add(constructNode(entry.getKey(), rootNode, n));
174 } else if (n.isArray()) {
175 LOGGER.error("Arrays are not supported at the root configuration.");
176 }
177 }
178 LOGGER.debug("Completed parsing configuration");
179 if (status.size() > 0) {
180 for (final Status s : status) {
181 LOGGER.error("Error processing element " + s.name + ": " + s.errorType);
182 }
183 return;
184 }
185 }
186
187 public Configuration reconfigure() {
188 if (configFile != null) {
189 try {
190 final ConfigurationFactory.ConfigurationSource source =
191 new ConfigurationFactory.ConfigurationSource(new FileInputStream(configFile), configFile);
192 return new JSONConfiguration(source);
193 } catch (final FileNotFoundException ex) {
194 LOGGER.error("Cannot locate file " + configFile, ex);
195 }
196 }
197 return null;
198 }
199
200 private Node constructNode(final String name, final Node parent, final JsonNode jsonNode) {
201 final PluginType type = getPluginManager().getPluginType(name);
202 final Node node = new Node(parent, name, type);
203 processAttributes(node, jsonNode);
204 final Iterator<Map.Entry<String, JsonNode>> iter = jsonNode.getFields();
205 final List<Node> children = node.getChildren();
206 while (iter.hasNext()) {
207 final Map.Entry<String, JsonNode> entry = iter.next();
208 final JsonNode n = entry.getValue();
209 if (n.isArray() || n.isObject()) {
210 if (type == null) {
211 status.add(new Status(name, n, ErrorType.CLASS_NOT_FOUND));
212 }
213 if (n.isArray()) {
214 LOGGER.debug("Processing node for array " + entry.getKey());
215 for (int i = 0; i < n.size(); ++i) {
216 final String pluginType = getType(n.get(i), entry.getKey());
217 final PluginType entryType = getPluginManager().getPluginType(pluginType);
218 final Node item = new Node(node, entry.getKey(), entryType);
219 processAttributes(item, n.get(i));
220 if (pluginType.equals(entry.getKey())) {
221 LOGGER.debug("Processing " + entry.getKey() + "[" + i + "]");
222 } else {
223 LOGGER.debug("Processing " + pluginType + " " + entry.getKey() + "[" + i + "]");
224 }
225 final Iterator<Map.Entry<String, JsonNode>> itemIter = n.get(i).getFields();
226 final List<Node> itemChildren = item.getChildren();
227 while (itemIter.hasNext()) {
228 final Map.Entry<String, JsonNode> itemEntry = itemIter.next();
229 if (itemEntry.getValue().isObject()) {
230 LOGGER.debug("Processing node for object " + itemEntry.getKey());
231 itemChildren.add(constructNode(itemEntry.getKey(), item, itemEntry.getValue()));
232 }
233 }
234 children.add(item);
235 }
236 } else {
237 LOGGER.debug("Processing node for object " + entry.getKey());
238 children.add(constructNode(entry.getKey(), node, n));
239 }
240 }
241 }
242
243 String t;
244 if (type == null) {
245 t = "null";
246 } else {
247 t = type.getElementName() + ":" + type.getPluginClass();
248 }
249
250 final String p = node.getParent() == null ? "null" : node.getParent().getName() == null ?
251 "root" : node.getParent().getName();
252 LOGGER.debug("Returning " + node.getName() + " with parent " + p + " of type " + t);
253 return node;
254 }
255
256 private String getType(final JsonNode node, final String name) {
257 final Iterator<Map.Entry<String, JsonNode>> iter = node.getFields();
258 while (iter.hasNext()) {
259 final Map.Entry<String, JsonNode> entry = iter.next();
260 if (entry.getKey().equalsIgnoreCase("type")) {
261 final JsonNode n = entry.getValue();
262 if (n.isValueNode()) {
263 return n.asText();
264 }
265 }
266 }
267 return name;
268 }
269
270 private void processAttributes(final Node parent, final JsonNode node) {
271 final Map<String, String> attrs = parent.getAttributes();
272 final Iterator<Map.Entry<String, JsonNode>> iter = node.getFields();
273 while (iter.hasNext()) {
274 final Map.Entry<String, JsonNode> entry = iter.next();
275 if (!entry.getKey().equalsIgnoreCase("type")) {
276 final JsonNode n = entry.getValue();
277 if (n.isValueNode()) {
278 attrs.put(entry.getKey(), n.asText());
279 }
280 }
281 }
282 }
283
284 protected byte[] toByteArray(final InputStream is) throws IOException {
285 final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
286
287 int nRead;
288 final byte[] data = new byte[BUF_SIZE];
289
290 while ((nRead = is.read(data, 0, data.length)) != -1) {
291 buffer.write(data, 0, nRead);
292 }
293
294 return buffer.toByteArray();
295 }
296
297
298
299
300 private enum ErrorType {
301 CLASS_NOT_FOUND
302 }
303
304
305
306
307 private class Status {
308 private final JsonNode node;
309 private final String name;
310 private final ErrorType errorType;
311
312 public Status(final String name, final JsonNode node, final ErrorType errorType) {
313 this.name = name;
314 this.node = node;
315 this.errorType = errorType;
316 }
317 }
318 }