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