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 java.io.ByteArrayInputStream;
20 import java.io.ByteArrayOutputStream;
21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.io.FileNotFoundException;
24 import java.io.FileOutputStream;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.PrintStream;
28 import java.net.URISyntaxException;
29 import java.nio.charset.Charset;
30 import java.util.ArrayList;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.Map;
34
35 import org.apache.logging.log4j.Level;
36 import org.apache.logging.log4j.core.config.plugins.PluginManager;
37 import org.apache.logging.log4j.core.config.plugins.PluginType;
38 import org.apache.logging.log4j.core.config.plugins.ResolverUtil;
39 import org.apache.logging.log4j.core.helpers.FileUtils;
40 import org.apache.logging.log4j.status.StatusConsoleListener;
41 import org.apache.logging.log4j.status.StatusListener;
42 import org.apache.logging.log4j.status.StatusLogger;
43
44 import com.fasterxml.jackson.core.JsonParser;
45 import com.fasterxml.jackson.databind.JsonNode;
46 import com.fasterxml.jackson.databind.ObjectMapper;
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 File configFile;
62
63 public JSONConfiguration(final ConfigurationFactory.ConfigurationSource configSource) {
64 final List<String> messages = new ArrayList<String>();
65
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.elements();
78 root = i.next();
79 }
80 processAttributes(rootNode, root);
81 Level status = getDefaultStatus();
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(getStrSubstitutor().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(FileUtils.getCorrectedFilePathUri(dest));
99 final String enc = Charset.defaultCharset().name();
100 stream = new PrintStream(new FileOutputStream(destFile), true, enc);
101 } catch (final URISyntaxException use) {
102 System.err.println("Unable to write to " + dest + ". Writing to stdout");
103 }
104 }
105 }
106 } else if ("shutdownHook".equalsIgnoreCase(entry.getKey())) {
107 String hook = getStrSubstitutor().replace(entry.getValue());
108 isShutdownHookEnabled = !hook.equalsIgnoreCase("disable");
109 } else if ("verbose".equalsIgnoreCase(entry.getKey())) {
110 verbose = Boolean.parseBoolean(getStrSubstitutor().replace(entry.getValue()));
111 } else if ("packages".equalsIgnoreCase(entry.getKey())) {
112 final String[] packages = getStrSubstitutor().replace(entry.getValue()).split(",");
113 for (final String p : packages) {
114 PluginManager.addPackage(p);
115 }
116 } else if ("name".equalsIgnoreCase(entry.getKey())) {
117 setName(getStrSubstitutor().replace(entry.getValue()));
118 } else if ("monitorInterval".equalsIgnoreCase(entry.getKey())) {
119 final int interval = Integer.parseInt(getStrSubstitutor().replace(entry.getValue()));
120 if (interval > 0 && configFile != null) {
121 monitor = new FileConfigurationMonitor(this, configFile, listeners, interval);
122 }
123 } else if ("advertiser".equalsIgnoreCase(entry.getKey())) {
124 createAdvertiser(getStrSubstitutor().replace(entry.getValue()), configSource, buffer,
125 "application/json");
126 }
127 }
128
129 final Iterator<StatusListener> statusIter = ((StatusLogger) LOGGER).getListeners();
130 boolean found = false;
131 while (statusIter.hasNext()) {
132 final StatusListener listener = statusIter.next();
133 if (listener instanceof StatusConsoleListener) {
134 found = true;
135 ((StatusConsoleListener) listener).setLevel(status);
136 if (!verbose) {
137 ((StatusConsoleListener) listener).setFilters(VERBOSE_CLASSES);
138 }
139 }
140 }
141 if (!found && status != Level.OFF) {
142 final StatusConsoleListener listener = new StatusConsoleListener(status, stream);
143 if (!verbose) {
144 listener.setFilters(VERBOSE_CLASSES);
145 }
146 ((StatusLogger) LOGGER).registerListener(listener);
147 for (final String msg : messages) {
148 LOGGER.error(msg);
149 }
150 }
151 if (getName() == null) {
152 setName(configSource.getLocation());
153 }
154 } catch (final Exception ex) {
155 LOGGER.error("Error parsing " + configSource.getLocation(), ex);
156 ex.printStackTrace();
157 }
158 }
159
160 @Override
161 public void stop() {
162 super.stop();
163 }
164
165 @Override
166 public void setup() {
167 final Iterator<Map.Entry<String, JsonNode>> iter = root.fields();
168 final List<Node> children = rootNode.getChildren();
169 while (iter.hasNext()) {
170 final Map.Entry<String, JsonNode> entry = iter.next();
171 final JsonNode n = entry.getValue();
172 if (n.isObject()) {
173 LOGGER.debug("Processing node for object " + entry.getKey());
174 children.add(constructNode(entry.getKey(), rootNode, n));
175 } else if (n.isArray()) {
176 LOGGER.error("Arrays are not supported at the root configuration.");
177 }
178 }
179 LOGGER.debug("Completed parsing configuration");
180 if (status.size() > 0) {
181 for (final Status s : status) {
182 LOGGER.error("Error processing element " + s.name + ": " + s.errorType);
183 }
184 }
185 }
186
187 @Override
188 public Configuration reconfigure() {
189 if (configFile != null) {
190 try {
191 final ConfigurationFactory.ConfigurationSource source =
192 new ConfigurationFactory.ConfigurationSource(new FileInputStream(configFile), configFile);
193 return new JSONConfiguration(source);
194 } catch (final FileNotFoundException ex) {
195 LOGGER.error("Cannot locate file " + configFile, ex);
196 }
197 }
198 return null;
199 }
200
201 private Node constructNode(final String name, final Node parent, final JsonNode jsonNode) {
202 final PluginType<?> type = pluginManager.getPluginType(name);
203 final Node node = new Node(parent, name, type);
204 processAttributes(node, jsonNode);
205 final Iterator<Map.Entry<String, JsonNode>> iter = jsonNode.fields();
206 final List<Node> children = node.getChildren();
207 while (iter.hasNext()) {
208 final Map.Entry<String, JsonNode> entry = iter.next();
209 final JsonNode n = entry.getValue();
210 if (n.isArray() || n.isObject()) {
211 if (type == null) {
212 status.add(new Status(name, n, ErrorType.CLASS_NOT_FOUND));
213 }
214 if (n.isArray()) {
215 LOGGER.debug("Processing node for array " + entry.getKey());
216 for (int i = 0; i < n.size(); ++i) {
217 final String pluginType = getType(n.get(i), entry.getKey());
218 final PluginType<?> entryType = pluginManager.getPluginType(pluginType);
219 final Node item = new Node(node, entry.getKey(), entryType);
220 processAttributes(item, n.get(i));
221 if (pluginType.equals(entry.getKey())) {
222 LOGGER.debug("Processing " + entry.getKey() + "[" + i + "]");
223 } else {
224 LOGGER.debug("Processing " + pluginType + " " + entry.getKey() + "[" + i + "]");
225 }
226 final Iterator<Map.Entry<String, JsonNode>> itemIter = n.get(i).fields();
227 final List<Node> itemChildren = item.getChildren();
228 while (itemIter.hasNext()) {
229 final Map.Entry<String, JsonNode> itemEntry = itemIter.next();
230 if (itemEntry.getValue().isObject()) {
231 LOGGER.debug("Processing node for object " + itemEntry.getKey());
232 itemChildren.add(constructNode(itemEntry.getKey(), item, itemEntry.getValue()));
233 } else if (itemEntry.getValue().isArray()) {
234 JsonNode array = itemEntry.getValue();
235 String entryName = itemEntry.getKey();
236 LOGGER.debug("Processing array for object " + entryName);
237 for (int j = 0; j < array.size(); ++j) {
238 itemChildren.add(constructNode(entryName, item, array.get(j)));
239 }
240 }
241
242 }
243 children.add(item);
244 }
245 } else {
246 LOGGER.debug("Processing node for object " + entry.getKey());
247 children.add(constructNode(entry.getKey(), node, n));
248 }
249 } else {
250 LOGGER.debug("Node {} is of type {}", entry.getKey(), n.getNodeType());
251 }
252 }
253
254 String t;
255 if (type == null) {
256 t = "null";
257 } else {
258 t = type.getElementName() + ":" + type.getPluginClass();
259 }
260
261 final String p = node.getParent() == null ? "null" : node.getParent().getName() == null ?
262 "root" : node.getParent().getName();
263 LOGGER.debug("Returning " + node.getName() + " with parent " + p + " of type " + t);
264 return node;
265 }
266
267 private String getType(final JsonNode node, final String name) {
268 final Iterator<Map.Entry<String, JsonNode>> iter = node.fields();
269 while (iter.hasNext()) {
270 final Map.Entry<String, JsonNode> entry = iter.next();
271 if (entry.getKey().equalsIgnoreCase("type")) {
272 final JsonNode n = entry.getValue();
273 if (n.isValueNode()) {
274 return n.asText();
275 }
276 }
277 }
278 return name;
279 }
280
281 private void processAttributes(final Node parent, final JsonNode node) {
282 final Map<String, String> attrs = parent.getAttributes();
283 final Iterator<Map.Entry<String, JsonNode>> iter = node.fields();
284 while (iter.hasNext()) {
285 final Map.Entry<String, JsonNode> entry = iter.next();
286 if (!entry.getKey().equalsIgnoreCase("type")) {
287 final JsonNode n = entry.getValue();
288 if (n.isValueNode()) {
289 attrs.put(entry.getKey(), n.asText());
290 }
291 }
292 }
293 }
294
295 protected byte[] toByteArray(final InputStream is) throws IOException {
296 final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
297
298 int nRead;
299 final byte[] data = new byte[BUF_SIZE];
300
301 while ((nRead = is.read(data, 0, data.length)) != -1) {
302 buffer.write(data, 0, nRead);
303 }
304
305 return buffer.toByteArray();
306 }
307
308
309
310
311 private enum ErrorType {
312 CLASS_NOT_FOUND
313 }
314
315
316
317
318 private class Status {
319 private final JsonNode node;
320 private final String name;
321 private final ErrorType errorType;
322
323 public Status(final String name, final JsonNode node, final ErrorType errorType) {
324 this.name = name;
325 this.node = node;
326 this.errorType = errorType;
327 }
328 }
329 }