1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.config.json;
18
19 import com.fasterxml.jackson.core.JsonParser;
20 import com.fasterxml.jackson.databind.JsonNode;
21 import com.fasterxml.jackson.databind.ObjectMapper;
22 import org.apache.logging.log4j.core.config.AbstractConfiguration;
23 import org.apache.logging.log4j.core.config.Configuration;
24 import org.apache.logging.log4j.core.config.ConfigurationSource;
25 import org.apache.logging.log4j.core.config.ConfiguratonFileWatcher;
26 import org.apache.logging.log4j.core.config.LoggerConfig;
27 import org.apache.logging.log4j.core.config.Node;
28 import org.apache.logging.log4j.core.config.Reconfigurable;
29 import org.apache.logging.log4j.core.config.plugins.util.PluginType;
30 import org.apache.logging.log4j.core.config.plugins.util.ResolverUtil;
31 import org.apache.logging.log4j.core.config.status.StatusConfiguration;
32 import org.apache.logging.log4j.core.util.FileWatcher;
33 import org.apache.logging.log4j.core.util.Patterns;
34
35 import java.io.ByteArrayInputStream;
36 import java.io.File;
37 import java.io.IOException;
38 import java.io.InputStream;
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.Iterator;
42 import java.util.List;
43 import java.util.Map;
44
45
46
47
48 public class JsonConfiguration extends AbstractConfiguration implements Reconfigurable {
49
50 private static final long serialVersionUID = 1L;
51 private static final String[] VERBOSE_CLASSES = new String[] { ResolverUtil.class.getName() };
52 private final List<Status> status = new ArrayList<>();
53 private JsonNode root;
54
55 public JsonConfiguration(final ConfigurationSource configSource) {
56 super(configSource);
57 final File configFile = configSource.getFile();
58 byte[] buffer;
59 try {
60 try (final InputStream configStream = configSource.getInputStream()) {
61 buffer = toByteArray(configStream);
62 }
63 final InputStream is = new ByteArrayInputStream(buffer);
64 root = getObjectMapper().readTree(is);
65 if (root.size() == 1) {
66 for (final JsonNode node : root) {
67 root = node;
68 }
69 }
70 processAttributes(rootNode, root);
71 final StatusConfiguration statusConfig = new StatusConfiguration().withVerboseClasses(VERBOSE_CLASSES)
72 .withStatus(getDefaultStatus());
73 for (final Map.Entry<String, String> entry : rootNode.getAttributes().entrySet()) {
74 final String key = entry.getKey();
75 final String value = getStrSubstitutor().replace(entry.getValue());
76
77 if ("status".equalsIgnoreCase(key)) {
78 statusConfig.withStatus(value);
79 } else if ("dest".equalsIgnoreCase(key)) {
80 statusConfig.withDestination(value);
81 } else if ("shutdownHook".equalsIgnoreCase(key)) {
82 isShutdownHookEnabled = !"disable".equalsIgnoreCase(value);
83 } else if ("verbose".equalsIgnoreCase(entry.getKey())) {
84 statusConfig.withVerbosity(value);
85 } else if ("packages".equalsIgnoreCase(key)) {
86 pluginPackages.addAll(Arrays.asList(value.split(Patterns.COMMA_SEPARATOR)));
87 } else if ("name".equalsIgnoreCase(key)) {
88 setName(value);
89 } else if ("monitorInterval".equalsIgnoreCase(key)) {
90 final int intervalSeconds = Integer.parseInt(value);
91 if (intervalSeconds > 0) {
92 getWatchManager().setIntervalSeconds(intervalSeconds);
93 if (configFile != null) {
94 FileWatcher watcher = new ConfiguratonFileWatcher(this, listeners);
95 getWatchManager().watchFile(configFile, watcher);
96 }
97 }
98 } else if ("advertiser".equalsIgnoreCase(key)) {
99 createAdvertiser(value, configSource, buffer, "application/json");
100 }
101 }
102 statusConfig.initialize();
103 if (getName() == null) {
104 setName(configSource.getLocation());
105 }
106 } catch (final Exception ex) {
107 LOGGER.error("Error parsing {}", configSource.getLocation(), ex);
108 }
109 }
110
111 protected ObjectMapper getObjectMapper() {
112 return new ObjectMapper().configure(JsonParser.Feature.ALLOW_COMMENTS, true);
113 }
114
115 @Override
116 public void setup() {
117 final Iterator<Map.Entry<String, JsonNode>> iter = root.fields();
118 final List<Node> children = rootNode.getChildren();
119 while (iter.hasNext()) {
120 final Map.Entry<String, JsonNode> entry = iter.next();
121 final JsonNode n = entry.getValue();
122 if (n.isObject()) {
123 LOGGER.debug("Processing node for object {}", entry.getKey());
124 children.add(constructNode(entry.getKey(), rootNode, n));
125 } else if (n.isArray()) {
126 LOGGER.error("Arrays are not supported at the root configuration.");
127 }
128 }
129 LOGGER.debug("Completed parsing configuration");
130 if (status.size() > 0) {
131 for (final Status s : status) {
132 LOGGER.error("Error processing element " + s.name + ": " + s.errorType);
133 }
134 }
135 }
136
137 @Override
138 public Configuration reconfigure() {
139 try {
140 final ConfigurationSource source = getConfigurationSource().resetInputStream();
141 if (source == null) {
142 return null;
143 }
144 return new JsonConfiguration(source);
145 } catch (final IOException ex) {
146 LOGGER.error("Cannot locate file {}", getConfigurationSource(), ex);
147 }
148 return null;
149 }
150
151 private Node constructNode(final String name, final Node parent, final JsonNode jsonNode) {
152 final PluginType<?> type = pluginManager.getPluginType(name);
153 final Node node = new Node(parent, name, type);
154 processAttributes(node, jsonNode);
155 final Iterator<Map.Entry<String, JsonNode>> iter = jsonNode.fields();
156 final List<Node> children = node.getChildren();
157 while (iter.hasNext()) {
158 final Map.Entry<String, JsonNode> entry = iter.next();
159 final JsonNode n = entry.getValue();
160 if (n.isArray() || n.isObject()) {
161 if (type == null) {
162 status.add(new Status(name, n, ErrorType.CLASS_NOT_FOUND));
163 }
164 if (n.isArray()) {
165 LOGGER.debug("Processing node for array {}", entry.getKey());
166 for (int i = 0; i < n.size(); ++i) {
167 final String pluginType = getType(n.get(i), entry.getKey());
168 final PluginType<?> entryType = pluginManager.getPluginType(pluginType);
169 final Node item = new Node(node, entry.getKey(), entryType);
170 processAttributes(item, n.get(i));
171 if (pluginType.equals(entry.getKey())) {
172 LOGGER.debug("Processing {}[{}]", entry.getKey(), i);
173 } else {
174 LOGGER.debug("Processing {} {}[{}]", pluginType, entry.getKey(), i);
175 }
176 final Iterator<Map.Entry<String, JsonNode>> itemIter = n.get(i).fields();
177 final List<Node> itemChildren = item.getChildren();
178 while (itemIter.hasNext()) {
179 final Map.Entry<String, JsonNode> itemEntry = itemIter.next();
180 if (itemEntry.getValue().isObject()) {
181 LOGGER.debug("Processing node for object {}", itemEntry.getKey());
182 itemChildren.add(constructNode(itemEntry.getKey(), item, itemEntry.getValue()));
183 } else if (itemEntry.getValue().isArray()) {
184 final JsonNode array = itemEntry.getValue();
185 final String entryName = itemEntry.getKey();
186 LOGGER.debug("Processing array for object {}", entryName);
187 for (int j = 0; j < array.size(); ++j) {
188 itemChildren.add(constructNode(entryName, item, array.get(j)));
189 }
190 }
191
192 }
193 children.add(item);
194 }
195 } else {
196 LOGGER.debug("Processing node for object {}", entry.getKey());
197 children.add(constructNode(entry.getKey(), node, n));
198 }
199 } else {
200 LOGGER.debug("Node {} is of type {}", entry.getKey(), n.getNodeType());
201 }
202 }
203
204 String t;
205 if (type == null) {
206 t = "null";
207 } else {
208 t = type.getElementName() + ':' + type.getPluginClass();
209 }
210
211 final String p = node.getParent() == null ? "null"
212 : node.getParent().getName() == null ? LoggerConfig.ROOT : node.getParent().getName();
213 LOGGER.debug("Returning {} with parent {} of type {}", node.getName(), p, t);
214 return node;
215 }
216
217 private String getType(final JsonNode node, final String name) {
218 final Iterator<Map.Entry<String, JsonNode>> iter = node.fields();
219 while (iter.hasNext()) {
220 final Map.Entry<String, JsonNode> entry = iter.next();
221 if (entry.getKey().equalsIgnoreCase("type")) {
222 final JsonNode n = entry.getValue();
223 if (n.isValueNode()) {
224 return n.asText();
225 }
226 }
227 }
228 return name;
229 }
230
231 private void processAttributes(final Node parent, final JsonNode node) {
232 final Map<String, String> attrs = parent.getAttributes();
233 final Iterator<Map.Entry<String, JsonNode>> iter = node.fields();
234 while (iter.hasNext()) {
235 final Map.Entry<String, JsonNode> entry = iter.next();
236 if (!entry.getKey().equalsIgnoreCase("type")) {
237 final JsonNode n = entry.getValue();
238 if (n.isValueNode()) {
239 attrs.put(entry.getKey(), n.asText());
240 }
241 }
242 }
243 }
244
245 @Override
246 public String toString() {
247 return getClass().getSimpleName() + "[location=" + getConfigurationSource() + "]";
248 }
249
250
251
252
253 private enum ErrorType {
254 CLASS_NOT_FOUND
255 }
256
257
258
259
260 private static class Status {
261 private final JsonNode node;
262 private final String name;
263 private final ErrorType errorType;
264
265 public Status(final String name, final JsonNode node, final ErrorType errorType) {
266 this.name = name;
267 this.node = node;
268 this.errorType = errorType;
269 }
270
271 @Override
272 public String toString() {
273 return "Status [name=" + name + ", errorType=" + errorType + ", node=" + node + "]";
274 }
275 }
276 }