This is an automated email from the ASF dual-hosted git repository.

mattsicker pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git


The following commit(s) were added to refs/heads/main by this push:
     new d48acf3b6d Remove Jackson JSON configuration
d48acf3b6d is described below

commit d48acf3b6d8a3fc339fe20fc7aa0f466186062b0
Author: Matt Sicker <[email protected]>
AuthorDate: Fri Dec 1 15:49:36 2023 -0600

    Remove Jackson JSON configuration
    
    This merges the Jackson JSON and YAML configuration plugins into just a
    YAML plugin as JSON is already supported by a built-in parser. This also
    moves the built-in parser plugin to the package where the Jackson JSON
    plugins were.
---
 .../log4j/core/config/jason/JsonConfiguration.java | 238 -------------------
 .../config/jason/JsonConfigurationFactory.java     |  46 ----
 .../log4j/core/config/jason/package-info.java      |  22 --
 .../log4j/core/config/json/JsonConfiguration.java  | 251 +++++++++------------
 .../core/config/json/JsonConfigurationFactory.java |  40 +---
 .../log4j/core/config/json/package-info.java       |   4 +-
 .../log4j/core/config/yaml/YamlConfiguration.java  | 222 +++++++++++++++++-
 .../log4j/core/config/yaml/package-info.java       |   4 +-
 .../.3.x.x/remove_jackson_json_config.xml          |  27 +++
 9 files changed, 369 insertions(+), 485 deletions(-)

diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/jason/JsonConfiguration.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/jason/JsonConfiguration.java
deleted file mode 100644
index 54eb85bdb3..0000000000
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/jason/JsonConfiguration.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.logging.log4j.core.config.jason;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.atomic.AtomicInteger;
-import org.apache.logging.log4j.core.LoggerContext;
-import org.apache.logging.log4j.core.config.AbstractConfiguration;
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.core.config.ConfigurationSource;
-import org.apache.logging.log4j.core.config.LoggerConfig;
-import org.apache.logging.log4j.core.config.Reconfigurable;
-import org.apache.logging.log4j.core.config.status.StatusConfiguration;
-import org.apache.logging.log4j.plugins.Node;
-import org.apache.logging.log4j.plugins.model.PluginType;
-import org.apache.logging.log4j.util.Cast;
-import org.apache.logging.log4j.util.JsonReader;
-
-public class JsonConfiguration extends AbstractConfiguration implements 
Reconfigurable {
-
-    private final List<Status> statuses = new ArrayList<>();
-    private Map<String, Object> root;
-
-    public JsonConfiguration(final LoggerContext loggerContext, final 
ConfigurationSource configurationSource) {
-        super(loggerContext, configurationSource);
-        try {
-            final byte[] bytes;
-            try (final var configStream = 
configurationSource.getInputStream()) {
-                bytes = configStream.readAllBytes();
-                root = Cast.cast(JsonReader.read(new String(bytes, 
StandardCharsets.UTF_8)));
-            }
-            if (root.size() == 1) {
-                for (final Object value : root.values()) {
-                    root = Cast.cast(value);
-                }
-            }
-            processAttributes(rootNode, root);
-            final StatusConfiguration statusConfig = new 
StatusConfiguration().setStatus(getDefaultStatus());
-            final AtomicInteger monitorIntervalSeconds = new AtomicInteger();
-
-            rootNode.getAttributes().forEach((key, value) -> {
-                if ("status".equalsIgnoreCase(key)) {
-                    statusConfig.setStatus(value);
-                } else if ("dest".equalsIgnoreCase(key)) {
-                    statusConfig.setDestination(value);
-                } else if ("shutdownHook".equalsIgnoreCase(key)) {
-                    isShutdownHookEnabled = !"disable".equalsIgnoreCase(value);
-                } else if ("shutdownTimeout".equalsIgnoreCase(key)) {
-                    shutdownTimeoutMillis = Long.parseLong(value);
-                } else if ("verbose".equalsIgnoreCase(key)) {
-                    statusConfig.setVerbosity(value);
-                } else if ("packages".equalsIgnoreCase(key)) {
-                    LOGGER.warn("The packages attribute is no longer 
supported");
-                } else if ("name".equalsIgnoreCase(key)) {
-                    setName(value);
-                } else if ("monitorInterval".equalsIgnoreCase(key)) {
-                    monitorIntervalSeconds.setOpaque(Integer.parseInt(value));
-                } else if ("advertiser".equalsIgnoreCase(key)) {
-                    createAdvertiser(value, configurationSource, bytes, 
"application/json");
-                }
-            });
-            initializeWatchers(this, configurationSource, 
monitorIntervalSeconds.getOpaque());
-            statusConfig.initialize();
-            if (getName() == null) {
-                setName(configurationSource.getLocation());
-            }
-        } catch (final Exception e) {
-            LOGGER.error("Error parsing {}", 
configurationSource.getLocation(), e);
-        }
-    }
-
-    @Override
-    public void setup() {
-        final List<Node> children = rootNode.getChildren();
-        root.forEach((key, value) -> {
-            if (value instanceof Map) {
-                LOGGER.debug("Processing node for object {}", key);
-                children.add(constructNode(key, rootNode, Cast.cast(value)));
-            }
-        });
-        LOGGER.debug("Completed parsing configuration");
-        if (statuses.size() > 0) {
-            for (final var s : statuses) {
-                LOGGER.error("Error processing element {}: {}", s.name, 
s.errorType);
-            }
-        }
-    }
-
-    private Node constructNode(final String key, final Node parent, final 
Map<String, Object> value) {
-        final PluginType<?> pluginType = corePlugins.get(key);
-        final Node node = new Node(parent, key, pluginType);
-        processAttributes(node, value);
-        final int size = node.getChildren().size();
-        value.forEach((k, v) -> {
-            if (isValueType(v)) {
-                LOGGER.debug("Node {} is of type {}", k, v != null ? 
v.getClass() : null);
-                return;
-            }
-            if (pluginType == null) {
-                statuses.add(new Status(v, k, ErrorType.CLASS_NOT_FOUND));
-                return;
-            }
-            if (v instanceof List<?>) {
-                LOGGER.debug("Processing node for array {}", k);
-                ((List<?>) v).forEach(object -> {
-                    if (object instanceof Map<?, ?>) {
-                        final Map<String, Object> map = Cast.cast(object);
-                        final String type = getType(map).orElse(k);
-                        final PluginType<?> entryType = corePlugins.get(type);
-                        final Node child = new Node(node, k, entryType);
-                        processAttributes(child, map);
-                        if (type.equalsIgnoreCase(k)) {
-                            LOGGER.debug("Processing {}[{}]", k, size);
-                        } else {
-                            LOGGER.debug("Processing {} {}[{}]", type, k, 
size);
-                        }
-                        map.forEach((itemKey, itemValue) -> {
-                            if (itemValue instanceof Map<?, ?>) {
-                                LOGGER.debug("Processing node for object {}", 
itemKey);
-                                child.addChild(constructNode(itemKey, child, 
Cast.cast(itemValue)));
-                            } else if (itemValue instanceof List<?>) {
-                                final List<?> list = (List<?>) itemValue;
-                                LOGGER.debug("Processing array for object {}", 
itemKey);
-                                list.forEach(
-                                        subValue -> 
child.addChild(constructNode(itemKey, child, Cast.cast(subValue))));
-                            }
-                        });
-                        node.addChild(child);
-                    }
-                });
-            } else {
-                LOGGER.debug("Processing node for object {}", k);
-                node.addChild(constructNode(k, node, Cast.cast(v)));
-            }
-        });
-
-        final String t;
-        if (pluginType == null) {
-            t = "null";
-        } else {
-            t = pluginType.getElementType() + ':' + 
pluginType.getPluginClass();
-        }
-
-        final String p = node.getParent() == null
-                ? "null"
-                : node.getParent().getName() == null
-                        ? LoggerConfig.ROOT
-                        : node.getParent().getName();
-        LOGGER.debug("Returning {} with parent {} of type {}", node.getName(), 
p, t);
-        return node;
-    }
-
-    @Override
-    public Configuration reconfigure() {
-        try {
-            final ConfigurationSource configurationSource =
-                    getConfigurationSource().resetInputStream();
-            if (configurationSource == null) {
-                return null;
-            }
-            return new JsonConfiguration(getLoggerContext(), 
configurationSource);
-        } catch (final IOException e) {
-            LOGGER.error("Cannot locate file {}", getConfigurationSource(), e);
-        }
-        return null;
-    }
-
-    private static boolean isValueType(final Object value) {
-        return !(value instanceof Map<?, ?> || value instanceof List<?>);
-    }
-
-    private static void processAttributes(final Node parent, final Map<String, 
Object> node) {
-        final Map<String, String> attributes = parent.getAttributes();
-        node.forEach((key, value) -> {
-            if (!key.equalsIgnoreCase("type") && isValueType(value)) {
-                attributes.put(key, String.valueOf(value));
-            }
-        });
-    }
-
-    private static Optional<String> getType(final Map<String, Object> node) {
-        for (final Map.Entry<String, Object> entry : node.entrySet()) {
-            if (entry.getKey().equalsIgnoreCase("type")) {
-                final Object value = entry.getValue();
-                if (isValueType(value)) {
-                    return Optional.of(String.valueOf(value));
-                }
-            }
-        }
-        return Optional.empty();
-    }
-
-    /**
-     * The error that occurred.
-     */
-    private enum ErrorType {
-        CLASS_NOT_FOUND
-    }
-
-    /**
-     * Status for recording errors.
-     */
-    private static final class Status {
-        private final Object node;
-        private final String name;
-        private final ErrorType errorType;
-
-        private Status(final Object node, final String name, final ErrorType 
errorType) {
-            this.node = node;
-            this.name = name;
-            this.errorType = errorType;
-        }
-
-        @Override
-        public String toString() {
-            return "Status{" + "node=" + node + ", name='" + name + '\'' + ", 
errorType=" + errorType + '}';
-        }
-    }
-}
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/jason/JsonConfigurationFactory.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/jason/JsonConfigurationFactory.java
deleted file mode 100644
index c0b992d1e2..0000000000
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/jason/JsonConfigurationFactory.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.logging.log4j.core.config.jason;
-
-import org.apache.logging.log4j.core.LoggerContext;
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.core.config.ConfigurationFactory;
-import org.apache.logging.log4j.core.config.ConfigurationSource;
-import org.apache.logging.log4j.core.config.Order;
-import org.apache.logging.log4j.plugins.Namespace;
-import org.apache.logging.log4j.plugins.Plugin;
-
-@Namespace(ConfigurationFactory.NAMESPACE)
-@Plugin("JsonConfigurationFactory")
-@Order(6)
-public class JsonConfigurationFactory extends ConfigurationFactory {
-
-    /**
-     * The file extensions supported by this factory.
-     */
-    private static final String[] SUFFIXES = new String[] {".json", ".jsn"};
-
-    @Override
-    protected String[] getSupportedTypes() {
-        return SUFFIXES;
-    }
-
-    @Override
-    public Configuration getConfiguration(final LoggerContext loggerContext, 
final ConfigurationSource source) {
-        return new JsonConfiguration(loggerContext, source);
-    }
-}
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/jason/package-info.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/jason/package-info.java
deleted file mode 100644
index 64e0a011db..0000000000
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/jason/package-info.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-@Export
-@Version("1.0.0")
-package org.apache.logging.log4j.core.config.jason;
-
-import org.osgi.annotation.bundle.Export;
-import org.osgi.annotation.versioning.Version;
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfiguration.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfiguration.java
index bfdd3a8a88..0698962d20 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfiguration.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfiguration.java
@@ -16,16 +16,13 @@
  */
 package org.apache.logging.log4j.core.config.json;
 
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import java.io.ByteArrayInputStream;
 import java.io.IOException;
-import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicInteger;
 import org.apache.logging.log4j.core.LoggerContext;
 import org.apache.logging.log4j.core.config.AbstractConfiguration;
 import org.apache.logging.log4j.core.config.Configuration;
@@ -35,37 +32,32 @@ import org.apache.logging.log4j.core.config.Reconfigurable;
 import org.apache.logging.log4j.core.config.status.StatusConfiguration;
 import org.apache.logging.log4j.plugins.Node;
 import org.apache.logging.log4j.plugins.model.PluginType;
+import org.apache.logging.log4j.util.Cast;
+import org.apache.logging.log4j.util.JsonReader;
 
-/**
- * Creates a Node hierarchy from a JSON file.
- */
 public class JsonConfiguration extends AbstractConfiguration implements 
Reconfigurable {
 
-    private final List<Status> status = new ArrayList<>();
-    private JsonNode root;
+    private final List<Status> statuses = new ArrayList<>();
+    private Map<String, Object> root;
 
-    public JsonConfiguration(final LoggerContext loggerContext, final 
ConfigurationSource configSource) {
-        super(loggerContext, configSource);
-        final byte[] buffer;
+    public JsonConfiguration(final LoggerContext loggerContext, final 
ConfigurationSource configurationSource) {
+        super(loggerContext, configurationSource);
         try {
-            try (final InputStream configStream = 
configSource.getInputStream()) {
-                buffer = configStream.readAllBytes();
+            final byte[] bytes;
+            try (final var configStream = 
configurationSource.getInputStream()) {
+                bytes = configStream.readAllBytes();
+                root = Cast.cast(JsonReader.read(new String(bytes, 
StandardCharsets.UTF_8)));
             }
-            final InputStream is = new ByteArrayInputStream(buffer);
-            root = getObjectMapper().readTree(is);
             if (root.size() == 1) {
-                for (final JsonNode node : root) {
-                    root = node;
+                for (final Object value : root.values()) {
+                    root = Cast.cast(value);
                 }
             }
             processAttributes(rootNode, root);
             final StatusConfiguration statusConfig = new 
StatusConfiguration().setStatus(getDefaultStatus());
-            int monitorIntervalSeconds = 0;
-            for (final Map.Entry<String, String> entry :
-                    rootNode.getAttributes().entrySet()) {
-                final String key = entry.getKey();
-                final String value = 
getConfigurationStrSubstitutor().replace(entry.getValue());
-                // TODO: this duplicates a lot of the XmlConfiguration 
constructor
+            final AtomicInteger monitorIntervalSeconds = new AtomicInteger();
+
+            rootNode.getAttributes().forEach((key, value) -> {
                 if ("status".equalsIgnoreCase(key)) {
                     statusConfig.setStatus(value);
                 } else if ("dest".equalsIgnoreCase(key)) {
@@ -74,126 +66,98 @@ public class JsonConfiguration extends 
AbstractConfiguration implements Reconfig
                     isShutdownHookEnabled = !"disable".equalsIgnoreCase(value);
                 } else if ("shutdownTimeout".equalsIgnoreCase(key)) {
                     shutdownTimeoutMillis = Long.parseLong(value);
-                } else if ("verbose".equalsIgnoreCase(entry.getKey())) {
+                } else if ("verbose".equalsIgnoreCase(key)) {
                     statusConfig.setVerbosity(value);
                 } else if ("packages".equalsIgnoreCase(key)) {
                     LOGGER.warn("The packages attribute is no longer 
supported");
                 } else if ("name".equalsIgnoreCase(key)) {
                     setName(value);
                 } else if ("monitorInterval".equalsIgnoreCase(key)) {
-                    monitorIntervalSeconds = Integer.parseInt(value);
+                    monitorIntervalSeconds.setOpaque(Integer.parseInt(value));
                 } else if ("advertiser".equalsIgnoreCase(key)) {
-                    createAdvertiser(value, configSource, buffer, 
"application/json");
+                    createAdvertiser(value, configurationSource, bytes, 
"application/json");
                 }
-            }
-            initializeWatchers(this, configSource, monitorIntervalSeconds);
+            });
+            initializeWatchers(this, configurationSource, 
monitorIntervalSeconds.getOpaque());
             statusConfig.initialize();
             if (getName() == null) {
-                setName(configSource.getLocation());
+                setName(configurationSource.getLocation());
             }
-        } catch (final Exception ex) {
-            LOGGER.error("Error parsing " + configSource.getLocation(), ex);
+        } catch (final Exception e) {
+            LOGGER.error("Error parsing {}", 
configurationSource.getLocation(), e);
         }
     }
 
-    protected ObjectMapper getObjectMapper() {
-        return new ObjectMapper().configure(JsonParser.Feature.ALLOW_COMMENTS, 
true);
-    }
-
     @Override
     public void setup() {
-        final Iterator<Map.Entry<String, JsonNode>> iter = root.fields();
         final List<Node> children = rootNode.getChildren();
-        while (iter.hasNext()) {
-            final Map.Entry<String, JsonNode> entry = iter.next();
-            final JsonNode n = entry.getValue();
-            if (n.isObject()) {
-                LOGGER.debug("Processing node for object {}", entry.getKey());
-                children.add(constructNode(entry.getKey(), rootNode, n));
-            } else if (n.isArray()) {
-                LOGGER.error("Arrays are not supported at the root 
configuration.");
+        root.forEach((key, value) -> {
+            if (value instanceof Map) {
+                LOGGER.debug("Processing node for object {}", key);
+                children.add(constructNode(key, rootNode, Cast.cast(value)));
             }
-        }
+        });
         LOGGER.debug("Completed parsing configuration");
-        if (status.size() > 0) {
-            for (final Status s : status) {
+        if (statuses.size() > 0) {
+            for (final var s : statuses) {
                 LOGGER.error("Error processing element {}: {}", s.name, 
s.errorType);
             }
         }
     }
 
-    @Override
-    public Configuration reconfigure() {
-        try {
-            final ConfigurationSource source = 
getConfigurationSource().resetInputStream();
-            if (source == null) {
-                return null;
+    private Node constructNode(final String key, final Node parent, final 
Map<String, Object> value) {
+        final PluginType<?> pluginType = corePlugins.get(key);
+        final Node node = new Node(parent, key, pluginType);
+        processAttributes(node, value);
+        final int size = node.getChildren().size();
+        value.forEach((k, v) -> {
+            if (isValueType(v)) {
+                LOGGER.debug("Node {} is of type {}", k, v != null ? 
v.getClass() : null);
+                return;
             }
-            return new JsonConfiguration(getLoggerContext(), source);
-        } catch (final IOException ex) {
-            LOGGER.error("Cannot locate file {}", getConfigurationSource(), 
ex);
-        }
-        return null;
-    }
-
-    private Node constructNode(final String name, final Node parent, final 
JsonNode jsonNode) {
-        final PluginType<?> type = corePlugins.get(name);
-        final Node node = new Node(parent, name, type);
-        processAttributes(node, jsonNode);
-        final Iterator<Map.Entry<String, JsonNode>> iter = jsonNode.fields();
-        final List<Node> children = node.getChildren();
-        while (iter.hasNext()) {
-            final Map.Entry<String, JsonNode> entry = iter.next();
-            final JsonNode n = entry.getValue();
-            if (n.isArray() || n.isObject()) {
-                if (type == null) {
-                    status.add(new Status(name, n, ErrorType.CLASS_NOT_FOUND));
-                }
-                if (n.isArray()) {
-                    LOGGER.debug("Processing node for array {}", 
entry.getKey());
-                    for (int i = 0; i < n.size(); ++i) {
-                        final String pluginType = getType(n.get(i), 
entry.getKey());
-                        final PluginType<?> entryType = 
corePlugins.get(pluginType);
-                        final Node item = new Node(node, entry.getKey(), 
entryType);
-                        processAttributes(item, n.get(i));
-                        if (pluginType.equals(entry.getKey())) {
-                            LOGGER.debug("Processing {}[{}]", entry.getKey(), 
i);
+            if (pluginType == null) {
+                statuses.add(new Status(v, k, ErrorType.CLASS_NOT_FOUND));
+                return;
+            }
+            if (v instanceof List<?>) {
+                LOGGER.debug("Processing node for array {}", k);
+                ((List<?>) v).forEach(object -> {
+                    if (object instanceof Map<?, ?>) {
+                        final Map<String, Object> map = Cast.cast(object);
+                        final String type = getType(map).orElse(k);
+                        final PluginType<?> entryType = corePlugins.get(type);
+                        final Node child = new Node(node, k, entryType);
+                        processAttributes(child, map);
+                        if (type.equalsIgnoreCase(k)) {
+                            LOGGER.debug("Processing {}[{}]", k, size);
                         } else {
-                            LOGGER.debug("Processing {} {}[{}]", pluginType, 
entry.getKey(), i);
+                            LOGGER.debug("Processing {} {}[{}]", type, k, 
size);
                         }
-                        final Iterator<Map.Entry<String, JsonNode>> itemIter =
-                                n.get(i).fields();
-                        final List<Node> itemChildren = item.getChildren();
-                        while (itemIter.hasNext()) {
-                            final Map.Entry<String, JsonNode> itemEntry = 
itemIter.next();
-                            if (itemEntry.getValue().isObject()) {
-                                LOGGER.debug("Processing node for object {}", 
itemEntry.getKey());
-                                
itemChildren.add(constructNode(itemEntry.getKey(), item, itemEntry.getValue()));
-                            } else if (itemEntry.getValue().isArray()) {
-                                final JsonNode array = itemEntry.getValue();
-                                final String entryName = itemEntry.getKey();
-                                LOGGER.debug("Processing array for object {}", 
entryName);
-                                for (int j = 0; j < array.size(); ++j) {
-                                    itemChildren.add(constructNode(entryName, 
item, array.get(j)));
-                                }
+                        map.forEach((itemKey, itemValue) -> {
+                            if (itemValue instanceof Map<?, ?>) {
+                                LOGGER.debug("Processing node for object {}", 
itemKey);
+                                child.addChild(constructNode(itemKey, child, 
Cast.cast(itemValue)));
+                            } else if (itemValue instanceof List<?>) {
+                                final List<?> list = (List<?>) itemValue;
+                                LOGGER.debug("Processing array for object {}", 
itemKey);
+                                list.forEach(
+                                        subValue -> 
child.addChild(constructNode(itemKey, child, Cast.cast(subValue))));
                             }
-                        }
-                        children.add(item);
+                        });
+                        node.addChild(child);
                     }
-                } else {
-                    LOGGER.debug("Processing node for object {}", 
entry.getKey());
-                    children.add(constructNode(entry.getKey(), node, n));
-                }
+                });
             } else {
-                LOGGER.debug("Node {} is of type {}", entry.getKey(), 
n.getNodeType());
+                LOGGER.debug("Processing node for object {}", k);
+                node.addChild(constructNode(k, node, Cast.cast(v)));
             }
-        }
+        });
 
         final String t;
-        if (type == null) {
+        if (pluginType == null) {
             t = "null";
         } else {
-            t = type.getElementType() + ':' + type.getPluginClass();
+            t = pluginType.getElementType() + ':' + 
pluginType.getPluginClass();
         }
 
         final String p = node.getParent() == null
@@ -205,37 +169,44 @@ public class JsonConfiguration extends 
AbstractConfiguration implements Reconfig
         return node;
     }
 
-    private String getType(final JsonNode node, final String name) {
-        final Iterator<Map.Entry<String, JsonNode>> iter = node.fields();
-        while (iter.hasNext()) {
-            final Map.Entry<String, JsonNode> entry = iter.next();
-            if (entry.getKey().equalsIgnoreCase("type")) {
-                final JsonNode n = entry.getValue();
-                if (n.isValueNode()) {
-                    return n.asText();
-                }
+    @Override
+    public Configuration reconfigure() {
+        try {
+            final ConfigurationSource configurationSource =
+                    getConfigurationSource().resetInputStream();
+            if (configurationSource == null) {
+                return null;
             }
+            return new JsonConfiguration(getLoggerContext(), 
configurationSource);
+        } catch (final IOException e) {
+            LOGGER.error("Cannot locate file {}", getConfigurationSource(), e);
         }
-        return name;
+        return null;
     }
 
-    private void processAttributes(final Node parent, final JsonNode node) {
-        final Map<String, String> attrs = parent.getAttributes();
-        final Iterator<Map.Entry<String, JsonNode>> iter = node.fields();
-        while (iter.hasNext()) {
-            final Map.Entry<String, JsonNode> entry = iter.next();
-            if (!entry.getKey().equalsIgnoreCase("type")) {
-                final JsonNode n = entry.getValue();
-                if (n.isValueNode()) {
-                    attrs.put(entry.getKey(), n.asText());
-                }
+    private static boolean isValueType(final Object value) {
+        return !(value instanceof Map<?, ?> || value instanceof List<?>);
+    }
+
+    private static void processAttributes(final Node parent, final Map<String, 
Object> node) {
+        final Map<String, String> attributes = parent.getAttributes();
+        node.forEach((key, value) -> {
+            if (!key.equalsIgnoreCase("type") && isValueType(value)) {
+                attributes.put(key, String.valueOf(value));
             }
-        }
+        });
     }
 
-    @Override
-    public String toString() {
-        return getClass().getSimpleName() + "[location=" + 
getConfigurationSource() + "]";
+    private static Optional<String> getType(final Map<String, Object> node) {
+        for (final Map.Entry<String, Object> entry : node.entrySet()) {
+            if (entry.getKey().equalsIgnoreCase("type")) {
+                final Object value = entry.getValue();
+                if (isValueType(value)) {
+                    return Optional.of(String.valueOf(value));
+                }
+            }
+        }
+        return Optional.empty();
     }
 
     /**
@@ -248,20 +219,20 @@ public class JsonConfiguration extends 
AbstractConfiguration implements Reconfig
     /**
      * Status for recording errors.
      */
-    private static class Status {
-        private final JsonNode node;
+    private static final class Status {
+        private final Object node;
         private final String name;
         private final ErrorType errorType;
 
-        public Status(final String name, final JsonNode node, final ErrorType 
errorType) {
-            this.name = name;
+        private Status(final Object node, final String name, final ErrorType 
errorType) {
             this.node = node;
+            this.name = name;
             this.errorType = errorType;
         }
 
         @Override
         public String toString() {
-            return "Status [name=" + name + ", errorType=" + errorType + ", 
node=" + node + "]";
+            return "Status{" + "node=" + node + ", name='" + name + '\'' + ", 
errorType=" + errorType + '}';
         }
     }
 }
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfigurationFactory.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfigurationFactory.java
index 9ac8bb8607..048b0a9f62 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfigurationFactory.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfigurationFactory.java
@@ -20,8 +20,13 @@ import org.apache.logging.log4j.core.LoggerContext;
 import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.config.ConfigurationFactory;
 import org.apache.logging.log4j.core.config.ConfigurationSource;
-import org.apache.logging.log4j.core.util.Loader;
+import org.apache.logging.log4j.core.config.Order;
+import org.apache.logging.log4j.plugins.Namespace;
+import org.apache.logging.log4j.plugins.Plugin;
 
+@Namespace(ConfigurationFactory.NAMESPACE)
+@Plugin("JsonConfigurationFactory")
+@Order(6)
 public class JsonConfigurationFactory extends ConfigurationFactory {
 
     /**
@@ -29,42 +34,13 @@ public class JsonConfigurationFactory extends 
ConfigurationFactory {
      */
     private static final String[] SUFFIXES = new String[] {".json", ".jsn"};
 
-    private static final String[] dependencies = new String[] {
-        "com.fasterxml.jackson.databind.ObjectMapper",
-        "com.fasterxml.jackson.databind.JsonNode",
-        "com.fasterxml.jackson.core.JsonParser"
-    };
-
-    private final boolean isActive;
-
-    public JsonConfigurationFactory() {
-        for (final String dependency : dependencies) {
-            if (!Loader.isClassAvailable(dependency)) {
-                LOGGER.debug(
-                        "Missing dependencies for Json support, 
ConfigurationFactory {} is inactive",
-                        getClass().getName());
-                isActive = false;
-                return;
-            }
-        }
-        isActive = true;
-    }
-
     @Override
-    protected boolean isActive() {
-        return isActive;
+    protected String[] getSupportedTypes() {
+        return SUFFIXES;
     }
 
     @Override
     public Configuration getConfiguration(final LoggerContext loggerContext, 
final ConfigurationSource source) {
-        if (!isActive) {
-            return null;
-        }
         return new JsonConfiguration(loggerContext, source);
     }
-
-    @Override
-    public String[] getSupportedTypes() {
-        return SUFFIXES;
-    }
 }
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/package-info.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/package-info.java
index 9d9e5d60eb..22142cfdaa 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/package-info.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/package-info.java
@@ -15,10 +15,10 @@
  * limitations under the license.
  */
 /**
- * Classes and interfaces supporting configuration of Log4j 2 with JSON.
+ * Classes supporting configuration of Log4j with JSON.
  */
 @Export
-@Version("2.20.1")
+@Version("3.0.0")
 package org.apache.logging.log4j.core.config.json;
 
 import org.osgi.annotation.bundle.Export;
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/YamlConfiguration.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/YamlConfiguration.java
index b147eed4c5..8260d48461 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/YamlConfiguration.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/YamlConfiguration.java
@@ -17,25 +17,112 @@
 package org.apache.logging.log4j.core.config.yaml;
 
 import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
 import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.AbstractConfiguration;
 import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.config.ConfigurationSource;
-import org.apache.logging.log4j.core.config.json.JsonConfiguration;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.config.Reconfigurable;
+import org.apache.logging.log4j.core.config.status.StatusConfiguration;
+import org.apache.logging.log4j.plugins.Node;
+import org.apache.logging.log4j.plugins.model.PluginType;
 
-public class YamlConfiguration extends JsonConfiguration {
+/**
+ * Creates a Node hierarchy from a YAML file.
+ */
+public class YamlConfiguration extends AbstractConfiguration implements 
Reconfigurable {
+
+    private final List<Status> status = new ArrayList<>();
+    private JsonNode root;
 
     public YamlConfiguration(final LoggerContext loggerContext, final 
ConfigurationSource configSource) {
         super(loggerContext, configSource);
+        final byte[] buffer;
+        try {
+            try (final InputStream configStream = 
configSource.getInputStream()) {
+                buffer = configStream.readAllBytes();
+            }
+            final InputStream is = new ByteArrayInputStream(buffer);
+            root = getObjectMapper().readTree(is);
+            if (root.size() == 1) {
+                for (final JsonNode node : root) {
+                    root = node;
+                }
+            }
+            processAttributes(rootNode, root);
+            final StatusConfiguration statusConfig = new 
StatusConfiguration().setStatus(getDefaultStatus());
+            int monitorIntervalSeconds = 0;
+            for (final Map.Entry<String, String> entry :
+                    rootNode.getAttributes().entrySet()) {
+                final String key = entry.getKey();
+                final String value = 
getConfigurationStrSubstitutor().replace(entry.getValue());
+                // TODO: this duplicates a lot of the XmlConfiguration 
constructor
+                if ("status".equalsIgnoreCase(key)) {
+                    statusConfig.setStatus(value);
+                } else if ("dest".equalsIgnoreCase(key)) {
+                    statusConfig.setDestination(value);
+                } else if ("shutdownHook".equalsIgnoreCase(key)) {
+                    isShutdownHookEnabled = !"disable".equalsIgnoreCase(value);
+                } else if ("shutdownTimeout".equalsIgnoreCase(key)) {
+                    shutdownTimeoutMillis = Long.parseLong(value);
+                } else if ("verbose".equalsIgnoreCase(entry.getKey())) {
+                    statusConfig.setVerbosity(value);
+                } else if ("packages".equalsIgnoreCase(key)) {
+                    LOGGER.warn("The packages attribute is no longer 
supported");
+                } else if ("name".equalsIgnoreCase(key)) {
+                    setName(value);
+                } else if ("monitorInterval".equalsIgnoreCase(key)) {
+                    monitorIntervalSeconds = Integer.parseInt(value);
+                } else if ("advertiser".equalsIgnoreCase(key)) {
+                    createAdvertiser(value, configSource, buffer, 
"application/json");
+                }
+            }
+            initializeWatchers(this, configSource, monitorIntervalSeconds);
+            statusConfig.initialize();
+            if (getName() == null) {
+                setName(configSource.getLocation());
+            }
+        } catch (final Exception ex) {
+            LOGGER.error("Error parsing " + configSource.getLocation(), ex);
+        }
     }
 
-    @Override
     protected ObjectMapper getObjectMapper() {
         return new ObjectMapper(new 
YAMLFactory()).configure(JsonParser.Feature.ALLOW_COMMENTS, true);
     }
 
+    @Override
+    public void setup() {
+        final Iterator<Map.Entry<String, JsonNode>> iter = root.fields();
+        final List<Node> children = rootNode.getChildren();
+        while (iter.hasNext()) {
+            final Map.Entry<String, JsonNode> entry = iter.next();
+            final JsonNode n = entry.getValue();
+            if (n.isObject()) {
+                LOGGER.debug("Processing node for object {}", entry.getKey());
+                children.add(constructNode(entry.getKey(), rootNode, n));
+            } else if (n.isArray()) {
+                LOGGER.error("Arrays are not supported at the root 
configuration.");
+            }
+        }
+        LOGGER.debug("Completed parsing configuration");
+        if (status.size() > 0) {
+            for (final Status s : status) {
+                LOGGER.error("Error processing element {}: {}", s.name, 
s.errorType);
+            }
+        }
+    }
+
     @Override
     public Configuration reconfigure() {
         try {
@@ -49,4 +136,133 @@ public class YamlConfiguration extends JsonConfiguration {
         }
         return null;
     }
+
+    private Node constructNode(final String name, final Node parent, final 
JsonNode jsonNode) {
+        final PluginType<?> type = corePlugins.get(name);
+        final Node node = new Node(parent, name, type);
+        processAttributes(node, jsonNode);
+        final Iterator<Map.Entry<String, JsonNode>> iter = jsonNode.fields();
+        final List<Node> children = node.getChildren();
+        while (iter.hasNext()) {
+            final Map.Entry<String, JsonNode> entry = iter.next();
+            final JsonNode n = entry.getValue();
+            if (n.isArray() || n.isObject()) {
+                if (type == null) {
+                    status.add(new Status(name, n, ErrorType.CLASS_NOT_FOUND));
+                }
+                if (n.isArray()) {
+                    LOGGER.debug("Processing node for array {}", 
entry.getKey());
+                    for (int i = 0; i < n.size(); ++i) {
+                        final String pluginType = getType(n.get(i), 
entry.getKey());
+                        final PluginType<?> entryType = 
corePlugins.get(pluginType);
+                        final Node item = new Node(node, entry.getKey(), 
entryType);
+                        processAttributes(item, n.get(i));
+                        if (pluginType.equals(entry.getKey())) {
+                            LOGGER.debug("Processing {}[{}]", entry.getKey(), 
i);
+                        } else {
+                            LOGGER.debug("Processing {} {}[{}]", pluginType, 
entry.getKey(), i);
+                        }
+                        final Iterator<Map.Entry<String, JsonNode>> itemIter =
+                                n.get(i).fields();
+                        final List<Node> itemChildren = item.getChildren();
+                        while (itemIter.hasNext()) {
+                            final Map.Entry<String, JsonNode> itemEntry = 
itemIter.next();
+                            if (itemEntry.getValue().isObject()) {
+                                LOGGER.debug("Processing node for object {}", 
itemEntry.getKey());
+                                
itemChildren.add(constructNode(itemEntry.getKey(), item, itemEntry.getValue()));
+                            } else if (itemEntry.getValue().isArray()) {
+                                final JsonNode array = itemEntry.getValue();
+                                final String entryName = itemEntry.getKey();
+                                LOGGER.debug("Processing array for object {}", 
entryName);
+                                for (int j = 0; j < array.size(); ++j) {
+                                    itemChildren.add(constructNode(entryName, 
item, array.get(j)));
+                                }
+                            }
+                        }
+                        children.add(item);
+                    }
+                } else {
+                    LOGGER.debug("Processing node for object {}", 
entry.getKey());
+                    children.add(constructNode(entry.getKey(), node, n));
+                }
+            } else {
+                LOGGER.debug("Node {} is of type {}", entry.getKey(), 
n.getNodeType());
+            }
+        }
+
+        final String t;
+        if (type == null) {
+            t = "null";
+        } else {
+            t = type.getElementType() + ':' + type.getPluginClass();
+        }
+
+        final String p = node.getParent() == null
+                ? "null"
+                : node.getParent().getName() == null
+                        ? LoggerConfig.ROOT
+                        : node.getParent().getName();
+        LOGGER.debug("Returning {} with parent {} of type {}", node.getName(), 
p, t);
+        return node;
+    }
+
+    private String getType(final JsonNode node, final String name) {
+        final Iterator<Map.Entry<String, JsonNode>> iter = node.fields();
+        while (iter.hasNext()) {
+            final Map.Entry<String, JsonNode> entry = iter.next();
+            if (entry.getKey().equalsIgnoreCase("type")) {
+                final JsonNode n = entry.getValue();
+                if (n.isValueNode()) {
+                    return n.asText();
+                }
+            }
+        }
+        return name;
+    }
+
+    private void processAttributes(final Node parent, final JsonNode node) {
+        final Map<String, String> attrs = parent.getAttributes();
+        final Iterator<Map.Entry<String, JsonNode>> iter = node.fields();
+        while (iter.hasNext()) {
+            final Map.Entry<String, JsonNode> entry = iter.next();
+            if (!entry.getKey().equalsIgnoreCase("type")) {
+                final JsonNode n = entry.getValue();
+                if (n.isValueNode()) {
+                    attrs.put(entry.getKey(), n.asText());
+                }
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + "[location=" + 
getConfigurationSource() + "]";
+    }
+
+    /**
+     * The error that occurred.
+     */
+    private enum ErrorType {
+        CLASS_NOT_FOUND
+    }
+
+    /**
+     * Status for recording errors.
+     */
+    private static class Status {
+        private final JsonNode node;
+        private final String name;
+        private final ErrorType errorType;
+
+        public Status(final String name, final JsonNode node, final ErrorType 
errorType) {
+            this.name = name;
+            this.node = node;
+            this.errorType = errorType;
+        }
+
+        @Override
+        public String toString() {
+            return "Status [name=" + name + ", errorType=" + errorType + ", 
node=" + node + "]";
+        }
+    }
 }
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/package-info.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/package-info.java
index c476f1d82c..09276828bf 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/package-info.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/package-info.java
@@ -15,10 +15,10 @@
  * limitations under the license.
  */
 /**
- * Classes and interfaces supporting configuration of Log4j 2 with YAML.
+ * Classes supporting configuration of Log4j with YAML.
  */
 @Export
-@Version("2.20.1")
+@Version("3.0.0")
 package org.apache.logging.log4j.core.config.yaml;
 
 import org.osgi.annotation.bundle.Export;
diff --git a/src/changelog/.3.x.x/remove_jackson_json_config.xml 
b/src/changelog/.3.x.x/remove_jackson_json_config.xml
new file mode 100644
index 0000000000..d0a25102b3
--- /dev/null
+++ b/src/changelog/.3.x.x/remove_jackson_json_config.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to you under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xmlns="http://logging.apache.org/log4j/changelog";
+       xsi:schemaLocation="http://logging.apache.org/log4j/changelog 
https://logging.apache.org/log4j/changelog-0.1.2.xsd";
+       type="removed">
+  <author id="mattsicker"/>
+  <description format="asciidoc">
+    Remove Jackson-based JSON configuration support. JSON configuration files 
are now handled through
+    a built-in JSON parser.
+  </description>
+</entry>

Reply via email to