You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ma...@apache.org on 2014/04/10 22:06:15 UTC

svn commit: r1586456 - in /logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config: AbstractConfiguration.java plugins/PluginBuilder.java

Author: mattsicker
Date: Thu Apr 10 20:06:14 2014
New Revision: 1586456

URL: http://svn.apache.org/r1586456
Log:
Create PluginBuilder for Configuration objects.

Added:
    logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilder.java   (with props)
Modified:
    logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java

Modified: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java?rev=1586456&r1=1586455&r2=1586456&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java (original)
+++ logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java Thu Apr 10 20:06:14 2014
@@ -20,11 +20,6 @@ import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Serializable;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Array;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
@@ -34,7 +29,6 @@ import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.CopyOnWriteArrayList;
-
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
@@ -46,15 +40,10 @@ import org.apache.logging.log4j.core.app
 import org.apache.logging.log4j.core.appender.ConsoleAppender;
 import org.apache.logging.log4j.core.async.AsyncLoggerConfig;
 import org.apache.logging.log4j.core.async.AsyncLoggerContextSelector;
-import org.apache.logging.log4j.core.config.plugins.PluginAliases;
-import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
-import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
-import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilder;
 import org.apache.logging.log4j.core.config.plugins.PluginFactory;
 import org.apache.logging.log4j.core.config.plugins.PluginManager;
-import org.apache.logging.log4j.core.config.plugins.PluginNode;
 import org.apache.logging.log4j.core.config.plugins.PluginType;
-import org.apache.logging.log4j.core.config.plugins.PluginValue;
 import org.apache.logging.log4j.core.filter.AbstractFilterable;
 import org.apache.logging.log4j.core.helpers.Constants;
 import org.apache.logging.log4j.core.helpers.NameUtil;
@@ -666,10 +655,11 @@ public abstract class AbstractConfigurat
         }
     }
 
-   /*
-    * Retrieve a static public 'method to create the desired object. Every parameter
-    * will be annotated to identify the appropriate attribute or element to use to
+   /**
+    * Invokes a static factory method to create the desired object. Every parameter
+    * must be annotated to identify the appropriate attribute or element to use to
     * set the value of the parameter.
+    *
     * Parameters annotated with PluginAttribute will always be set as Strings.
     * Parameters annotated with PluginElement may be Objects or arrays. Collections
     * and Maps are currently not supported, although the factory method that is called
@@ -677,9 +667,11 @@ public abstract class AbstractConfigurat
     *
     * Although the happy path works, more work still needs to be done to log incorrect
     * parameters. These will generally result in unhelpful InvocationTargetExceptions.
-    * @param classClass the class.
-    * @return the instantiate method or null if there is none by that
-    * description.
+    *
+    * @param type the type of plugin to create.
+    * @param node the corresponding configuration node for this plugin to create.
+    * @param event the LogEvent that spurred the creation of this plugin
+    * @return the created plugin object or {@code null} if there was an error setting it up.
     */
     private <T> Object createPluginObject(final PluginType<T> type, final Node node, final LogEvent event)
     {
@@ -702,216 +694,17 @@ public abstract class AbstractConfigurat
             }
         }
 
-        Method factoryMethod = findFactoryMethod(clazz);
-        if (factoryMethod == null) return null;
-
-        final Annotation[][] parmArray = factoryMethod.getParameterAnnotations();
-        final Class<?>[] parmClasses = factoryMethod.getParameterTypes();
-        if (parmArray.length != parmClasses.length) {
-            LOGGER.error("Number of parameter annotations ({}) does not equal the number of parameters ({})",
-                    parmArray.length, parmClasses.length
-            );
-        }
-        final Object[] parms = new Object[parmClasses.length];
-
-        int index = 0;
-        final Map<String, String> attrs = node.getAttributes();
-        final List<Node> children = node.getChildren();
-        final StringBuilder sb = new StringBuilder();
-        final List<Node> used = new ArrayList<Node>();
-
-        /*
-         * For each parameter:
-         * If the parameter is an attribute store the value of the attribute in the parameter array.
-         * If the parameter is an element:
-         *   Determine if the required parameter is an array.
-         *     If so, if a child contains the array, use it,
-         *      otherwise create the array from all child nodes of the correct type.
-         *     Store the array into the parameter array.
-         *   If not an array, store the object in the child node into the parameter array.
-         */
-        for (final Annotation[] parmTypes : parmArray) {
-            String[] aliases = extractPluginAliases(parmTypes);
-            for (final Annotation a : parmTypes) {
-                if (a instanceof PluginAliases) {
-                    continue;
-                }
-                if (sb.length() == 0) {
-                    sb.append(" with params(");
-                } else {
-                    sb.append(", ");
-                }
-                if (a instanceof PluginNode) {
-                    parms[index] = node;
-                    sb.append("Node=").append(node.getName());
-                } else if (a instanceof PluginConfiguration) {
-                    parms[index] = this;
-                    if (this.name != null) {
-                        sb.append("Configuration(").append(name).append(')');
-                    } else {
-                        sb.append("Configuration");
-                    }
-                } else if (a instanceof PluginValue) {
-                    final String name = ((PluginValue) a).value();
-                    String v = node.getValue();
-                    if (v == null) {
-                        v = getAttrValue("value", null, attrs);
-                    }
-                    final String value = subst.replace(event, v);
-                    sb.append(name).append("=\"").append(value).append('"');
-                    parms[index] = value;
-                } else if (a instanceof PluginAttribute) {
-                    PluginAttribute attr = (PluginAttribute) a;
-                    final String name = attr.value();
-                    final String value = subst.replace(event, getAttrValue(name, aliases, attrs));
-                    sb.append(name).append("=\"").append(value).append('"');
-                    parms[index] = value;
-                } else if (a instanceof PluginElement) {
-                    final PluginElement elem = (PluginElement) a;
-                    final String name = elem.value();
-                    if (parmClasses[index].isArray()) {
-                        final Class<?> parmClass = parmClasses[index].getComponentType();
-                        final List<Object> list = new ArrayList<Object>();
-                        sb.append(name).append("={");
-                        boolean first = true;
-                        for (final Node child : children) {
-                            final PluginType<?> childType = child.getType();
-                            if (name.equalsIgnoreCase(childType.getElementName()) ||
-                                    parmClass.isAssignableFrom(childType.getPluginClass())) {
-                                used.add(child);
-                                if (!first) {
-                                    sb.append(", ");
-                                }
-                                first = false;
-                                final Object obj = child.getObject();
-                                if (obj == null) {
-                                    LOGGER.error("Null object returned for {} in {}", child.getName(), node.getName());
-                                    continue;
-                                }
-                                if (obj.getClass().isArray()) {
-                                    printArray(sb, (Object[]) obj);
-                                    parms[index] = obj;
-                                    break;
-                                }
-                                sb.append(child.toString());
-                                list.add(obj);
-                            }
-                        }
-                        sb.append('}');
-                        if (parms[index] != null) {
-                            break;
-                        }
-                        if (!(list.isEmpty() || parmClass.isAssignableFrom(list.get(0).getClass()))) {
-                            LOGGER.error(
-                                    "Attempted to assign List containing class {} to array of type {} for attribute {}",
-                                    list.get(0).getClass().getName(), parmClass, name
-                            );
-                            break;
-                        }
-                        parms[index] = collectionToArray(list, parmClass);
-                    } else {
-                        final Node child = findNamedNode(name, parmClasses[index], children);
-                        if (child == null) {
-                            sb.append("null");
-                        } else {
-                            sb.append(child.getName()).append('(').append(child.toString()).append(')');
-                            used.add(child);
-                            parms[index] = child.getObject();
-                        }
-                    }
-                }
-            }
-            ++index;
-        }
-        if (sb.length() > 0) {
-            sb.append(')');
-        }
-
-        checkForRemainingAttributes(node);
-
-        if (!type.isDeferChildren() && used.size() != children.size()) {
-            children.removeAll(used);
-            for (final Node child : children) {
-                final String nodeType = node.getType().getElementName();
-                final String start = nodeType.equals(node.getName()) ? node.getName() : nodeType + ' ' + node.getName();
-                LOGGER.error("{} has no parameter that matches element {}", start, child.getName());
-            }
-        }
-
         try {
-            final int mod = factoryMethod.getModifiers();
-            if (!Modifier.isStatic(mod)) {
-                LOGGER.error("{} method is not static on class {} for element {}",
-                        factoryMethod.getName(), clazz.getName(), node.getName());
-                return null;
-            }
-            LOGGER.debug("Calling {} on class {} for element {}{}", factoryMethod.getName(), clazz.getName(),
-                    node.getName(), sb.toString());
-            //if (parms.length > 0) {
-            return factoryMethod.invoke(null, parms);
-            //}
-            //return factoryMethod.invoke(null, node);
-        } catch (final Exception e) {
-            LOGGER.error("Unable to invoke method {} in class {} for element {}",
-                    factoryMethod.getName(), clazz.getName(), node.getName(), e);
-        }
-        return null;
-    }
-
-    private static Object[] collectionToArray(final Collection<?> collection, final Class<?> type) {
-        final Object[] array = (Object[]) Array.newInstance(type, collection.size());
-        int i = 0;
-        for (final Object obj : collection) {
-            array[i] = obj;
-            ++i;
-        }
-        return array;
-    }
-
-    private static Node findNamedNode(final String name, final Class<?> type, final Iterable<Node> nodes) {
-        for (final Node child : nodes) {
-            final PluginType<?> childType = child.getType();
-            if (name.equalsIgnoreCase(childType.getElementName()) ||
-                    type.isAssignableFrom(childType.getPluginClass())) {
-                return child;
-            }
-        }
-        return null;
-    }
-
-    private static void checkForRemainingAttributes(final Node node) {
-        final Map<String, String> attrs = node.getAttributes();
-        if (!attrs.isEmpty()) {
-            final StringBuilder eb = new StringBuilder();
-            for (final String key : attrs.keySet()) {
-                if (eb.length() == 0) {
-                    eb.append(node.getName());
-                    eb.append(" contains ");
-                    if (attrs.size() == 1) {
-                        eb.append("an invalid element or attribute ");
-                    } else {
-                        eb.append("invalid attributes ");
-                    }
-                } else {
-                    eb.append(", ");
-                }
-                eb.append('"');
-                eb.append(key);
-                eb.append('"');
-
-            }
-            LOGGER.error(eb.toString());
-        }
-    }
-
-    private static String[] extractPluginAliases(final Annotation... parmTypes) {
-        String[] aliases = null;
-        for (final Annotation a : parmTypes) {
-            if (a instanceof PluginAliases) {
-                aliases = ((PluginAliases) a).value();
-            }
+            return new PluginBuilder<T>(type)
+                    .withFactoryMethodAnnotatedBy(PluginFactory.class)
+                    .withConfiguration(this)
+                    .withConfigurationNode(node)
+                    .forLogEvent(event)
+                    .build();
+        } catch (NoSuchMethodException e) {
+            LOGGER.error("No suitable factory method could be found on class {}", clazz, e);
+            return null;
         }
-        return aliases;
     }
 
     private static <T> Object createPluginMap(final Node node, final Class<T> clazz) throws InstantiationException, IllegalAccessException {
@@ -932,48 +725,6 @@ public abstract class AbstractConfigurat
         return list;
     }
 
-    private static <T> Method findFactoryMethod(final Class<T> clazz) {
-        for (final Method method : clazz.getMethods()) {
-            if (method.isAnnotationPresent(PluginFactory.class)) {
-                return method;
-            }
-        }
-        // TODO: this should probably throw an exception instead of returning null
-        return null;
-    }
-
-    private void printArray(final StringBuilder sb, final Object... array) {
-        boolean first = true;
-        for (final Object obj : array) {
-            if (!first) {
-                sb.append(", ");
-            }
-            sb.append(obj.toString());
-            first = false;
-        }
-    }
-
-    private String getAttrValue(final String name, final String[] aliases, final Map<String, String> attrs) {
-        for (final Map.Entry<String, String> entry : attrs.entrySet()) {
-            final String key = entry.getKey();
-            if (key.equalsIgnoreCase(name)) {
-                final String attr = entry.getValue();
-                attrs.remove(key);
-                return attr;
-            }
-            if (aliases != null) {
-                for (String alias : aliases) {
-                    if (key.equalsIgnoreCase(alias)) {
-                        final String attr = entry.getValue();
-                        attrs.remove(key);
-                        return attr;
-                    }
-                }
-            }
-        }
-        return null;
-    }
-
     private void setParents() {
          for (final Map.Entry<String, LoggerConfig> entry : loggers.entrySet()) {
             final LoggerConfig logger = entry.getValue();

Added: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilder.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilder.java?rev=1586456&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilder.java (added)
+++ logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilder.java Thu Apr 10 20:06:14 2014
@@ -0,0 +1,337 @@
+/*
+ * 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.plugins;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.Node;
+import org.apache.logging.log4j.status.StatusLogger;
+
+/**
+ * Builder class to instantiate and configure a Plugin object using a PluginFactory method.
+ *
+ * @param <T> type of Plugin class.
+ */
+public class PluginBuilder<T> {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+
+    private final PluginType<T> pluginType;
+    private final Class<T> clazz;
+
+    private Configuration configuration;
+    private Node node;
+    private LogEvent event;
+
+    private Method factory;
+    private Annotation[][] annotations;
+    private Class<?>[] types;
+    private List<Node> children;
+    private Collection<Node> used;
+
+    /**
+     * Constructs a PluginBuilder for a given PluginType.
+     *
+     * @param pluginType type of plugin to configure
+     */
+    public PluginBuilder(final PluginType<T> pluginType) {
+        this.pluginType = pluginType;
+        this.clazz = pluginType.getPluginClass();
+    }
+
+    /**
+     * Specifies which annotation denotes a plugin factory method. The method must be static.
+     *
+     * @param annotationType class of annotation marking the plugin factory.
+     * @param <A>            type of annotation.
+     * @return {@code this}
+     * @throws NoSuchMethodException
+     */
+    public <A extends Annotation> PluginBuilder<T> withFactoryMethodAnnotatedBy(final Class<A> annotationType)
+            throws NoSuchMethodException {
+        for (final Method method : clazz.getMethods()) {
+            if (method.isAnnotationPresent(annotationType) && Modifier.isStatic(method.getModifiers())) {
+                factory = method;
+                LOGGER.trace("Using factory method {} on class {}", method.getName(), clazz.getName());
+                return this;
+            }
+        }
+        throw new NoSuchMethodException("No method annotated with " + annotationType.getName() + "was found in " + clazz.getName());
+    }
+
+    /**
+     * Specifies the Configuration to use for constructing the plugin instance.
+     *
+     * @param configuration the configuration to use.
+     * @return {@code this}
+     */
+    public PluginBuilder<T> withConfiguration(final Configuration configuration) {
+        this.configuration = configuration;
+        return this;
+    }
+
+    /**
+     * Specifies the Node corresponding to the plugin object that will be created.
+     *
+     * @param node the plugin configuration node to use.
+     * @return {@code this}
+     */
+    public PluginBuilder<T> withConfigurationNode(final Node node) {
+        this.node = node;
+        this.children = this.node.getChildren();
+        this.used = new HashSet<Node>(this.children.size());
+        return this;
+    }
+
+    /**
+     * Specifies the LogEvent that may be used to provide extra context for string substitutions.
+     *
+     * @param event the event to use for extra information.
+     * @return {@code this}
+     */
+    public PluginBuilder<T> forLogEvent(final LogEvent event) {
+        this.event = event;
+        return this;
+    }
+
+    /**
+     * Builds the plugin object.
+     *
+     * @return the plugin object or {@code null} if there was a problem creating it.
+     */
+    public Object build() {
+        init();
+        try {
+            return factory.invoke(null, generateParameters());
+        } catch (final Exception e) {
+            LOGGER.error("Unable to invoke method {} in class {} for element {}",
+                    factory.getName(), clazz.getName(), node.getName(), e);
+            return null;
+        }
+    }
+
+    private void init() {
+        if (factory == null) {
+            throw new IllegalStateException("No factory method was found.");
+        }
+        if (configuration == null) {
+            throw new IllegalStateException("No Configuration object was set.");
+        }
+        if (node == null) {
+            throw new IllegalStateException("No Node object was set.");
+        }
+        annotations = factory.getParameterAnnotations();
+        types = factory.getParameterTypes();
+    }
+
+    private Object[] generateParameters() {
+        final StringBuilder sb = new StringBuilder();
+        final Object[] args = new Object[annotations.length];
+        for (int i = 0; i < annotations.length; i++) {
+            final String[] aliases = extractPluginAliases(annotations[i]);
+            for (Annotation a : annotations[i]) {
+                if (a instanceof PluginAliases) {
+                    continue; // already processed
+                }
+                sb.append(sb.length() == 0 ? "with params(" : ", ");
+                if (a instanceof PluginNode) {
+                    args[i] = node;
+                    sb.append("Node=").append(node.getName());
+                } else if (a instanceof PluginConfiguration) {
+                    args[i] = configuration;
+                    sb.append("Configuration");
+                    if (configuration.getName() != null) {
+                        sb.append('(').append(configuration.getName()).append(')');
+                    }
+                } else if (a instanceof PluginValue) {
+                    final String name = ((PluginValue) a).value();
+                    final String v = node.getValue() != null ? node.getValue() : getAttrValue("value");
+                    final String value = configuration.getStrSubstitutor().replace(event, v);
+                    args[i] = value;
+                    sb.append(name).append("=\"").append(value).append('"');
+                } else if (a instanceof PluginAttribute) {
+                    final PluginAttribute attribute = (PluginAttribute) a;
+                    final String name = attribute.value();
+                    final String value = configuration.getStrSubstitutor().replace(event, getAttrValue(name, aliases));
+                    args[i] = value;
+                    sb.append(name).append("=\"").append(value).append('"');
+                } else if (a instanceof PluginElement) {
+                    final PluginElement element = (PluginElement) a;
+                    final String name = element.value();
+                    if (types[i].isArray()) {
+                        final Class<?> type = types[i].getComponentType();
+                        sb.append(name).append("={");
+                        final List<Object> values = new ArrayList<Object>();
+                        boolean first = true;
+                        for (final Node child : children) {
+                            final PluginType<?> childType = child.getType();
+                            if (name.equalsIgnoreCase(childType.getElementName()) ||
+                                    type.isAssignableFrom(childType.getPluginClass())) {
+                                if (!first) {
+                                    sb.append(", ");
+                                }
+                                first = false;
+                                used.add(child);
+                                final Object o = child.getObject();
+                                if (o == null) {
+                                    LOGGER.error("Null object returned for {} in {}", child.getName(), node.getName());
+                                    continue;
+                                }
+                                if (o.getClass().isArray()) {
+                                    sb.append(Arrays.toString((Object[]) o));
+                                    args[i] = o;
+                                    break;
+                                }
+                                sb.append(child.toString());
+                                values.add(o);
+                            }
+                        }
+                        sb.append('}');
+                        if (args[i] != null) {
+                            break;
+                        }
+                        if (!(values.isEmpty() || type.isAssignableFrom(values.get(0).getClass()))) {
+                            LOGGER.error(
+                                    "Attempted to assign List containing class {} to array of type {} for attribute {}",
+                                    values.get(0).getClass().getName(), type, name
+                            );
+                            break;
+                        }
+                        args[i] = collectionToArray(values, type);
+                    } else {
+                        final Node namedNode = findNamedNode(name, types[i], children);
+                        if (namedNode == null) {
+                            sb.append("null");
+                        } else {
+                            sb.append(namedNode.getName()).append('(').append(namedNode.toString()).append(')');
+                            used.add(namedNode);
+                            args[i] = namedNode.getObject();
+                        }
+                    }
+                }
+            }
+        }
+        if (sb.length() > 0) {
+            sb.append(')');
+        }
+        checkForRemainingAttributes();
+        verifyNodeChildrenUsed();
+        LOGGER.debug("Calling {} on class {} for element {} {}", factory.getName(), clazz.getName(), node.getName(), sb.toString());
+        return args;
+    }
+
+    private static String[] extractPluginAliases(final Annotation... parmTypes) {
+        String[] aliases = null;
+        for (final Annotation a : parmTypes) {
+            if (a instanceof PluginAliases) {
+                aliases = ((PluginAliases) a).value();
+            }
+        }
+        return aliases;
+    }
+
+    private String getAttrValue(final String name, final String... aliases) {
+        final Map<String, String> attrs = node.getAttributes();
+        for (final Map.Entry<String, String> entry : attrs.entrySet()) {
+            final String key = entry.getKey();
+            if (key.equalsIgnoreCase(name)) {
+                final String attr = entry.getValue();
+                attrs.remove(key);
+                return attr;
+            }
+            if (aliases != null) {
+                for (String alias : aliases) {
+                    if (key.equalsIgnoreCase(alias)) {
+                        final String attr = entry.getValue();
+                        attrs.remove(key);
+                        return attr;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    private static Object[] collectionToArray(final Collection<?> collection, final Class<?> type) {
+        final Object[] array = (Object[]) Array.newInstance(type, collection.size());
+        int i = 0;
+        for (final Object obj : collection) {
+            array[i] = obj;
+            ++i;
+        }
+        return array;
+    }
+
+    private static Node findNamedNode(final String name, final Class<?> type, final Iterable<Node> nodes) {
+        for (final Node child : nodes) {
+            final PluginType<?> childType = child.getType();
+            if (name.equalsIgnoreCase(childType.getElementName()) ||
+                    type.isAssignableFrom(childType.getPluginClass())) {
+                return child;
+            }
+        }
+        return null;
+    }
+
+    private void checkForRemainingAttributes() {
+        final Map<String, String> attrs = node.getAttributes();
+        if (!attrs.isEmpty()) {
+            final StringBuilder eb = new StringBuilder();
+            for (final String key : attrs.keySet()) {
+                if (eb.length() == 0) {
+                    eb.append(node.getName());
+                    eb.append(" contains ");
+                    if (attrs.size() == 1) {
+                        eb.append("an invalid element or attribute ");
+                    } else {
+                        eb.append("invalid attributes ");
+                    }
+                } else {
+                    eb.append(", ");
+                }
+                eb.append('"');
+                eb.append(key);
+                eb.append('"');
+
+            }
+            LOGGER.error(eb.toString());
+        }
+    }
+
+    private void verifyNodeChildrenUsed() {
+        if (!(pluginType.isDeferChildren() || used.size() == children.size())) {
+            children.removeAll(used);
+            for (final Node child : children) {
+                final String nodeType = node.getType().getElementName();
+                final String start = nodeType.equals(node.getName()) ? node.getName() : nodeType + ' ' + node.getName();
+                LOGGER.error("{} has no parameter that matches element {}", start, child.getName());
+            }
+        }
+    }
+}

Propchange: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilder.java
------------------------------------------------------------------------------
    svn:eol-style = native



Re: svn commit: r1586456 - in /logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config: AbstractConfiguration.java plugins/PluginBuilder.java

Posted by Matt Sicker <bo...@gmail.com>.
The funny thing is that my refactoring is still using the "old way" logic
anyhow. Thanks to Java's lack of support for methods on annotations, we
can't use a simple visitor pattern here. I was considering trying out a
method similar to bean validation where every annotation has an
implementation class that implements a common interface, but for most of
the annotations, it would currently be a bit overkill. Oh well.


On 12 April 2014 01:01, Ralph Goers <ra...@dslextreme.com> wrote:

> I understood it the old way just fine. ;-)
>
> Actually, I have no problem with this refactoring.
>
> Ralph
>
> On Apr 10, 2014, at 1:13 PM, Matt Sicker <ma...@apache.org> wrote:
>
> You may notice that the diff is not very good at showing this, but the
> PluginBuilder class I added was essentially created by moving a lot of code
> from AbstractConfiguration to its own class. Now that method is a bit
> easier to understand, so now I'll be working on supporting more attribute
> types and such.
>
>
> On 10 April 2014 15:06, <ma...@apache.org> wrote:
>
>> Author: mattsicker
>> Date: Thu Apr 10 20:06:14 2014
>> New Revision: 1586456
>>
>> URL: http://svn.apache.org/r1586456
>> Log:
>> Create PluginBuilder for Configuration objects.
>>
>> Added:
>>
>> logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilder.java
>>   (with props)
>> Modified:
>>
>> logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
>>
>> Modified:
>> logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
>> URL:
>> http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java?rev=1586456&r1=1586455&r2=1586456&view=diff
>>
>> ==============================================================================
>> ---
>> logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
>> (original)
>> +++
>> logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
>> Thu Apr 10 20:06:14 2014
>> @@ -20,11 +20,6 @@ import java.io.ByteArrayOutputStream;
>>  import java.io.IOException;
>>  import java.io.InputStream;
>>  import java.io.Serializable;
>> -import java.lang.annotation.Annotation;
>> -import java.lang.reflect.Array;
>> -import java.lang.reflect.Method;
>> -import java.lang.reflect.Modifier;
>> -import java.util.ArrayList;
>>  import java.util.Collection;
>>  import java.util.Collections;
>>  import java.util.HashSet;
>> @@ -34,7 +29,6 @@ import java.util.Set;
>>  import java.util.concurrent.ConcurrentHashMap;
>>  import java.util.concurrent.ConcurrentMap;
>>  import java.util.concurrent.CopyOnWriteArrayList;
>> -
>>  import org.apache.logging.log4j.Level;
>>  import org.apache.logging.log4j.LogManager;
>>  import org.apache.logging.log4j.Logger;
>> @@ -46,15 +40,10 @@ import org.apache.logging.log4j.core.app
>>  import org.apache.logging.log4j.core.appender.ConsoleAppender;
>>  import org.apache.logging.log4j.core.async.AsyncLoggerConfig;
>>  import org.apache.logging.log4j.core.async.AsyncLoggerContextSelector;
>> -import org.apache.logging.log4j.core.config.plugins.PluginAliases;
>> -import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
>> -import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
>> -import org.apache.logging.log4j.core.config.plugins.PluginElement;
>> +import org.apache.logging.log4j.core.config.plugins.PluginBuilder;
>>  import org.apache.logging.log4j.core.config.plugins.PluginFactory;
>>  import org.apache.logging.log4j.core.config.plugins.PluginManager;
>> -import org.apache.logging.log4j.core.config.plugins.PluginNode;
>>  import org.apache.logging.log4j.core.config.plugins.PluginType;
>> -import org.apache.logging.log4j.core.config.plugins.PluginValue;
>>  import org.apache.logging.log4j.core.filter.AbstractFilterable;
>>  import org.apache.logging.log4j.core.helpers.Constants;
>>  import org.apache.logging.log4j.core.helpers.NameUtil;
>> @@ -666,10 +655,11 @@ public abstract class AbstractConfigurat
>>          }
>>      }
>>
>> -   /*
>> -    * Retrieve a static public 'method to create the desired object.
>> Every parameter
>> -    * will be annotated to identify the appropriate attribute or element
>> to use to
>> +   /**
>> +    * Invokes a static factory method to create the desired object.
>> Every parameter
>> +    * must be annotated to identify the appropriate attribute or element
>> to use to
>>      * set the value of the parameter.
>> +    *
>>      * Parameters annotated with PluginAttribute will always be set as
>> Strings.
>>      * Parameters annotated with PluginElement may be Objects or arrays.
>> Collections
>>      * and Maps are currently not supported, although the factory method
>> that is called
>> @@ -677,9 +667,11 @@ public abstract class AbstractConfigurat
>>      *
>>      * Although the happy path works, more work still needs to be done to
>> log incorrect
>>      * parameters. These will generally result in unhelpful
>> InvocationTargetExceptions.
>> -    * @param classClass the class.
>> -    * @return the instantiate method or null if there is none by that
>> -    * description.
>> +    *
>> +    * @param type the type of plugin to create.
>> +    * @param node the corresponding configuration node for this plugin
>> to create.
>> +    * @param event the LogEvent that spurred the creation of this plugin
>> +    * @return the created plugin object or {@code null} if there was an
>> error setting it up.
>>      */
>>      private <T> Object createPluginObject(final PluginType<T> type,
>> final Node node, final LogEvent event)
>>      {
>> @@ -702,216 +694,17 @@ public abstract class AbstractConfigurat
>>              }
>>          }
>>
>> -        Method factoryMethod = findFactoryMethod(clazz);
>> -        if (factoryMethod == null) return null;
>> -
>> -        final Annotation[][] parmArray =
>> factoryMethod.getParameterAnnotations();
>> -        final Class<?>[] parmClasses = factoryMethod.getParameterTypes();
>> -        if (parmArray.length != parmClasses.length) {
>> -            LOGGER.error("Number of parameter annotations ({}) does not
>> equal the number of parameters ({})",
>> -                    parmArray.length, parmClasses.length
>> -            );
>> -        }
>> -        final Object[] parms = new Object[parmClasses.length];
>> -
>> -        int index = 0;
>> -        final Map<String, String> attrs = node.getAttributes();
>> -        final List<Node> children = node.getChildren();
>> -        final StringBuilder sb = new StringBuilder();
>> -        final List<Node> used = new ArrayList<Node>();
>> -
>> -        /*
>> -         * For each parameter:
>> -         * If the parameter is an attribute store the value of the
>> attribute in the parameter array.
>> -         * If the parameter is an element:
>> -         *   Determine if the required parameter is an array.
>> -         *     If so, if a child contains the array, use it,
>> -         *      otherwise create the array from all child nodes of the
>> correct type.
>> -         *     Store the array into the parameter array.
>> -         *   If not an array, store the object in the child node into
>> the parameter array.
>> -         */
>> -        for (final Annotation[] parmTypes : parmArray) {
>> -            String[] aliases = extractPluginAliases(parmTypes);
>> -            for (final Annotation a : parmTypes) {
>> -                if (a instanceof PluginAliases) {
>> -                    continue;
>> -                }
>> -                if (sb.length() == 0) {
>> -                    sb.append(" with params(");
>> -                } else {
>> -                    sb.append(", ");
>> -                }
>> -                if (a instanceof PluginNode) {
>> -                    parms[index] = node;
>> -                    sb.append("Node=").append(node.getName());
>> -                } else if (a instanceof PluginConfiguration) {
>> -                    parms[index] = this;
>> -                    if (this.name != null) {
>> -
>>  sb.append("Configuration(").append(name).append(')');
>> -                    } else {
>> -                        sb.append("Configuration");
>> -                    }
>> -                } else if (a instanceof PluginValue) {
>> -                    final String name = ((PluginValue) a).value();
>> -                    String v = node.getValue();
>> -                    if (v == null) {
>> -                        v = getAttrValue("value", null, attrs);
>> -                    }
>> -                    final String value = subst.replace(event, v);
>> -
>>  sb.append(name).append("=\"").append(value).append('"');
>> -                    parms[index] = value;
>> -                } else if (a instanceof PluginAttribute) {
>> -                    PluginAttribute attr = (PluginAttribute) a;
>> -                    final String name = attr.value();
>> -                    final String value = subst.replace(event,
>> getAttrValue(name, aliases, attrs));
>> -
>>  sb.append(name).append("=\"").append(value).append('"');
>> -                    parms[index] = value;
>> -                } else if (a instanceof PluginElement) {
>> -                    final PluginElement elem = (PluginElement) a;
>> -                    final String name = elem.value();
>> -                    if (parmClasses[index].isArray()) {
>> -                        final Class<?> parmClass =
>> parmClasses[index].getComponentType();
>> -                        final List<Object> list = new
>> ArrayList<Object>();
>> -                        sb.append(name).append("={");
>> -                        boolean first = true;
>> -                        for (final Node child : children) {
>> -                            final PluginType<?> childType =
>> child.getType();
>> -                            if
>> (name.equalsIgnoreCase(childType.getElementName()) ||
>> -
>>  parmClass.isAssignableFrom(childType.getPluginClass())) {
>> -                                used.add(child);
>> -                                if (!first) {
>> -                                    sb.append(", ");
>> -                                }
>> -                                first = false;
>> -                                final Object obj = child.getObject();
>> -                                if (obj == null) {
>> -                                    LOGGER.error("Null object returned
>> for {} in {}", child.getName(), node.getName());
>> -                                    continue;
>> -                                }
>> -                                if (obj.getClass().isArray()) {
>> -                                    printArray(sb, (Object[]) obj);
>> -                                    parms[index] = obj;
>> -                                    break;
>> -                                }
>> -                                sb.append(child.toString());
>> -                                list.add(obj);
>> -                            }
>> -                        }
>> -                        sb.append('}');
>> -                        if (parms[index] != null) {
>> -                            break;
>> -                        }
>> -                        if (!(list.isEmpty() ||
>> parmClass.isAssignableFrom(list.get(0).getClass()))) {
>> -                            LOGGER.error(
>> -                                    "Attempted to assign List containing
>> class {} to array of type {} for attribute {}",
>> -                                    list.get(0).getClass().getName(),
>> parmClass, name
>> -                            );
>> -                            break;
>> -                        }
>> -                        parms[index] = collectionToArray(list,
>> parmClass);
>> -                    } else {
>> -                        final Node child = findNamedNode(name,
>> parmClasses[index], children);
>> -                        if (child == null) {
>> -                            sb.append("null");
>> -                        } else {
>> -
>>  sb.append(child.getName()).append('(').append(child.toString()).append(')');
>> -                            used.add(child);
>> -                            parms[index] = child.getObject();
>> -                        }
>> -                    }
>> -                }
>> -            }
>> -            ++index;
>> -        }
>> -        if (sb.length() > 0) {
>> -            sb.append(')');
>> -        }
>> -
>> -        checkForRemainingAttributes(node);
>> -
>> -        if (!type.isDeferChildren() && used.size() != children.size()) {
>> -            children.removeAll(used);
>> -            for (final Node child : children) {
>> -                final String nodeType = node.getType().getElementName();
>> -                final String start = nodeType.equals(node.getName()) ?
>> node.getName() : nodeType + ' ' + node.getName();
>> -                LOGGER.error("{} has no parameter that matches element
>> {}", start, child.getName());
>> -            }
>> -        }
>> -
>>          try {
>> -            final int mod = factoryMethod.getModifiers();
>> -            if (!Modifier.isStatic(mod)) {
>> -                LOGGER.error("{} method is not static on class {} for
>> element {}",
>> -                        factoryMethod.getName(), clazz.getName(),
>> node.getName());
>> -                return null;
>> -            }
>> -            LOGGER.debug("Calling {} on class {} for element {}{}",
>> factoryMethod.getName(), clazz.getName(),
>> -                    node.getName(), sb.toString());
>> -            //if (parms.length > 0) {
>> -            return factoryMethod.invoke(null, parms);
>> -            //}
>> -            //return factoryMethod.invoke(null, node);
>> -        } catch (final Exception e) {
>> -            LOGGER.error("Unable to invoke method {} in class {} for
>> element {}",
>> -                    factoryMethod.getName(), clazz.getName(),
>> node.getName(), e);
>> -        }
>> -        return null;
>> -    }
>> -
>> -    private static Object[] collectionToArray(final Collection<?>
>> collection, final Class<?> type) {
>> -        final Object[] array = (Object[]) Array.newInstance(type,
>> collection.size());
>> -        int i = 0;
>> -        for (final Object obj : collection) {
>> -            array[i] = obj;
>> -            ++i;
>> -        }
>> -        return array;
>> -    }
>> -
>> -    private static Node findNamedNode(final String name, final Class<?>
>> type, final Iterable<Node> nodes) {
>> -        for (final Node child : nodes) {
>> -            final PluginType<?> childType = child.getType();
>> -            if (name.equalsIgnoreCase(childType.getElementName()) ||
>> -                    type.isAssignableFrom(childType.getPluginClass())) {
>> -                return child;
>> -            }
>> -        }
>> -        return null;
>> -    }
>> -
>> -    private static void checkForRemainingAttributes(final Node node) {
>> -        final Map<String, String> attrs = node.getAttributes();
>> -        if (!attrs.isEmpty()) {
>> -            final StringBuilder eb = new StringBuilder();
>> -            for (final String key : attrs.keySet()) {
>> -                if (eb.length() == 0) {
>> -                    eb.append(node.getName());
>> -                    eb.append(" contains ");
>> -                    if (attrs.size() == 1) {
>> -                        eb.append("an invalid element or attribute ");
>> -                    } else {
>> -                        eb.append("invalid attributes ");
>> -                    }
>> -                } else {
>> -                    eb.append(", ");
>> -                }
>> -                eb.append('"');
>> -                eb.append(key);
>> -                eb.append('"');
>> -
>> -            }
>> -            LOGGER.error(eb.toString());
>> -        }
>> -    }
>> -
>> -    private static String[] extractPluginAliases(final Annotation...
>> parmTypes) {
>> -        String[] aliases = null;
>> -        for (final Annotation a : parmTypes) {
>> -            if (a instanceof PluginAliases) {
>> -                aliases = ((PluginAliases) a).value();
>> -            }
>> +            return new PluginBuilder<T>(type)
>> +                    .withFactoryMethodAnnotatedBy(PluginFactory.class)
>> +                    .withConfiguration(this)
>> +                    .withConfigurationNode(node)
>> +                    .forLogEvent(event)
>> +                    .build();
>> +        } catch (NoSuchMethodException e) {
>> +            LOGGER.error("No suitable factory method could be found on
>> class {}", clazz, e);
>> +            return null;
>>          }
>> -        return aliases;
>>      }
>>
>>      private static <T> Object createPluginMap(final Node node, final
>> Class<T> clazz) throws InstantiationException, IllegalAccessException {
>> @@ -932,48 +725,6 @@ public abstract class AbstractConfigurat
>>          return list;
>>      }
>>
>> -    private static <T> Method findFactoryMethod(final Class<T> clazz) {
>> -        for (final Method method : clazz.getMethods()) {
>> -            if (method.isAnnotationPresent(PluginFactory.class)) {
>> -                return method;
>> -            }
>> -        }
>> -        // TODO: this should probably throw an exception instead of
>> returning null
>> -        return null;
>> -    }
>> -
>> -    private void printArray(final StringBuilder sb, final Object...
>> array) {
>> -        boolean first = true;
>> -        for (final Object obj : array) {
>> -            if (!first) {
>> -                sb.append(", ");
>> -            }
>> -            sb.append(obj.toString());
>> -            first = false;
>> -        }
>> -    }
>> -
>> -    private String getAttrValue(final String name, final String[]
>> aliases, final Map<String, String> attrs) {
>> -        for (final Map.Entry<String, String> entry : attrs.entrySet()) {
>> -            final String key = entry.getKey();
>> -            if (key.equalsIgnoreCase(name)) {
>> -                final String attr = entry.getValue();
>> -                attrs.remove(key);
>> -                return attr;
>> -            }
>> -            if (aliases != null) {
>> -                for (String alias : aliases) {
>> -                    if (key.equalsIgnoreCase(alias)) {
>> -                        final String attr = entry.getValue();
>> -                        attrs.remove(key);
>> -                        return attr;
>> -                    }
>> -                }
>> -            }
>> -        }
>> -        return null;
>> -    }
>> -
>>      private void setParents() {
>>           for (final Map.Entry<String, LoggerConfig> entry :
>> loggers.entrySet()) {
>>              final LoggerConfig logger = entry.getValue();
>>
>> Added:
>> logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilder.java
>> URL:
>> http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilder.java?rev=1586456&view=auto
>>
>> ==============================================================================
>> ---
>> logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilder.java
>> (added)
>> +++
>> logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilder.java
>> Thu Apr 10 20:06:14 2014
>> @@ -0,0 +1,337 @@
>> +/*
>> + * 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.plugins;
>> +
>> +import java.lang.annotation.Annotation;
>> +import java.lang.reflect.Array;
>> +import java.lang.reflect.Method;
>> +import java.lang.reflect.Modifier;
>> +import java.util.ArrayList;
>> +import java.util.Arrays;
>> +import java.util.Collection;
>> +import java.util.HashSet;
>> +import java.util.List;
>> +import java.util.Map;
>> +import org.apache.logging.log4j.Logger;
>> +import org.apache.logging.log4j.core.LogEvent;
>> +import org.apache.logging.log4j.core.config.Configuration;
>> +import org.apache.logging.log4j.core.config.Node;
>> +import org.apache.logging.log4j.status.StatusLogger;
>> +
>> +/**
>> + * Builder class to instantiate and configure a Plugin object using a
>> PluginFactory method.
>> + *
>> + * @param <T> type of Plugin class.
>> + */
>> +public class PluginBuilder<T> {
>> +
>> +    private static final Logger LOGGER = StatusLogger.getLogger();
>> +
>> +    private final PluginType<T> pluginType;
>> +    private final Class<T> clazz;
>> +
>> +    private Configuration configuration;
>> +    private Node node;
>> +    private LogEvent event;
>> +
>> +    private Method factory;
>> +    private Annotation[][] annotations;
>> +    private Class<?>[] types;
>> +    private List<Node> children;
>> +    private Collection<Node> used;
>> +
>> +    /**
>> +     * Constructs a PluginBuilder for a given PluginType.
>> +     *
>> +     * @param pluginType type of plugin to configure
>> +     */
>> +    public PluginBuilder(final PluginType<T> pluginType) {
>> +        this.pluginType = pluginType;
>> +        this.clazz = pluginType.getPluginClass();
>> +    }
>> +
>> +    /**
>> +     * Specifies which annotation denotes a plugin factory method. The
>> method must be static.
>> +     *
>> +     * @param annotationType class of annotation marking the plugin
>> factory.
>> +     * @param <A>            type of annotation.
>> +     * @return {@code this}
>> +     * @throws NoSuchMethodException
>> +     */
>> +    public <A extends Annotation> PluginBuilder<T>
>> withFactoryMethodAnnotatedBy(final Class<A> annotationType)
>> +            throws NoSuchMethodException {
>> +        for (final Method method : clazz.getMethods()) {
>> +            if (method.isAnnotationPresent(annotationType) &&
>> Modifier.isStatic(method.getModifiers())) {
>> +                factory = method;
>> +                LOGGER.trace("Using factory method {} on class {}",
>> method.getName(), clazz.getName());
>> +                return this;
>> +            }
>> +        }
>> +        throw new NoSuchMethodException("No method annotated with " +
>> annotationType.getName() + "was found in " + clazz.getName());
>> +    }
>> +
>> +    /**
>> +     * Specifies the Configuration to use for constructing the plugin
>> instance.
>> +     *
>> +     * @param configuration the configuration to use.
>> +     * @return {@code this}
>> +     */
>> +    public PluginBuilder<T> withConfiguration(final Configuration
>> configuration) {
>> +        this.configuration = configuration;
>> +        return this;
>> +    }
>> +
>> +    /**
>> +     * Specifies the Node corresponding to the plugin object that will
>> be created.
>> +     *
>> +     * @param node the plugin configuration node to use.
>> +     * @return {@code this}
>> +     */
>> +    public PluginBuilder<T> withConfigurationNode(final Node node) {
>> +        this.node = node;
>> +        this.children = this.node.getChildren();
>> +        this.used = new HashSet<Node>(this.children.size());
>> +        return this;
>> +    }
>> +
>> +    /**
>> +     * Specifies the LogEvent that may be used to provide extra context
>> for string substitutions.
>> +     *
>> +     * @param event the event to use for extra information.
>> +     * @return {@code this}
>> +     */
>> +    public PluginBuilder<T> forLogEvent(final LogEvent event) {
>> +        this.event = event;
>> +        return this;
>> +    }
>> +
>> +    /**
>> +     * Builds the plugin object.
>> +     *
>> +     * @return the plugin object or {@code null} if there was a problem
>> creating it.
>> +     */
>> +    public Object build() {
>> +        init();
>> +        try {
>> +            return factory.invoke(null, generateParameters());
>> +        } catch (final Exception e) {
>> +            LOGGER.error("Unable to invoke method {} in class {} for
>> element {}",
>> +                    factory.getName(), clazz.getName(), node.getName(),
>> e);
>> +            return null;
>> +        }
>> +    }
>> +
>> +    private void init() {
>> +        if (factory == null) {
>> +            throw new IllegalStateException("No factory method was
>> found.");
>> +        }
>> +        if (configuration == null) {
>> +            throw new IllegalStateException("No Configuration object was
>> set.");
>> +        }
>> +        if (node == null) {
>> +            throw new IllegalStateException("No Node object was set.");
>> +        }
>> +        annotations = factory.getParameterAnnotations();
>> +        types = factory.getParameterTypes();
>> +    }
>> +
>> +    private Object[] generateParameters() {
>> +        final StringBuilder sb = new StringBuilder();
>> +        final Object[] args = new Object[annotations.length];
>> +        for (int i = 0; i < annotations.length; i++) {
>> +            final String[] aliases =
>> extractPluginAliases(annotations[i]);
>> +            for (Annotation a : annotations[i]) {
>> +                if (a instanceof PluginAliases) {
>> +                    continue; // already processed
>> +                }
>> +                sb.append(sb.length() == 0 ? "with params(" : ", ");
>> +                if (a instanceof PluginNode) {
>> +                    args[i] = node;
>> +                    sb.append("Node=").append(node.getName());
>> +                } else if (a instanceof PluginConfiguration) {
>> +                    args[i] = configuration;
>> +                    sb.append("Configuration");
>> +                    if (configuration.getName() != null) {
>> +
>>  sb.append('(').append(configuration.getName()).append(')');
>> +                    }
>> +                } else if (a instanceof PluginValue) {
>> +                    final String name = ((PluginValue) a).value();
>> +                    final String v = node.getValue() != null ?
>> node.getValue() : getAttrValue("value");
>> +                    final String value =
>> configuration.getStrSubstitutor().replace(event, v);
>> +                    args[i] = value;
>> +
>>  sb.append(name).append("=\"").append(value).append('"');
>> +                } else if (a instanceof PluginAttribute) {
>> +                    final PluginAttribute attribute = (PluginAttribute)
>> a;
>> +                    final String name = attribute.value();
>> +                    final String value =
>> configuration.getStrSubstitutor().replace(event, getAttrValue(name,
>> aliases));
>> +                    args[i] = value;
>> +
>>  sb.append(name).append("=\"").append(value).append('"');
>> +                } else if (a instanceof PluginElement) {
>> +                    final PluginElement element = (PluginElement) a;
>> +                    final String name = element.value();
>> +                    if (types[i].isArray()) {
>> +                        final Class<?> type =
>> types[i].getComponentType();
>> +                        sb.append(name).append("={");
>> +                        final List<Object> values = new
>> ArrayList<Object>();
>> +                        boolean first = true;
>> +                        for (final Node child : children) {
>> +                            final PluginType<?> childType =
>> child.getType();
>> +                            if
>> (name.equalsIgnoreCase(childType.getElementName()) ||
>> +
>>  type.isAssignableFrom(childType.getPluginClass())) {
>> +                                if (!first) {
>> +                                    sb.append(", ");
>> +                                }
>> +                                first = false;
>> +                                used.add(child);
>> +                                final Object o = child.getObject();
>> +                                if (o == null) {
>> +                                    LOGGER.error("Null object returned
>> for {} in {}", child.getName(), node.getName());
>> +                                    continue;
>> +                                }
>> +                                if (o.getClass().isArray()) {
>> +                                    sb.append(Arrays.toString((Object[])
>> o));
>> +                                    args[i] = o;
>> +                                    break;
>> +                                }
>> +                                sb.append(child.toString());
>> +                                values.add(o);
>> +                            }
>> +                        }
>> +                        sb.append('}');
>> +                        if (args[i] != null) {
>> +                            break;
>> +                        }
>> +                        if (!(values.isEmpty() ||
>> type.isAssignableFrom(values.get(0).getClass()))) {
>> +                            LOGGER.error(
>> +                                    "Attempted to assign List containing
>> class {} to array of type {} for attribute {}",
>> +                                    values.get(0).getClass().getName(),
>> type, name
>> +                            );
>> +                            break;
>> +                        }
>> +                        args[i] = collectionToArray(values, type);
>> +                    } else {
>> +                        final Node namedNode = findNamedNode(name,
>> types[i], children);
>> +                        if (namedNode == null) {
>> +                            sb.append("null");
>> +                        } else {
>> +
>>  sb.append(namedNode.getName()).append('(').append(namedNode.toString()).append(')');
>> +                            used.add(namedNode);
>> +                            args[i] = namedNode.getObject();
>> +                        }
>> +                    }
>> +                }
>> +            }
>> +        }
>> +        if (sb.length() > 0) {
>> +            sb.append(')');
>> +        }
>> +        checkForRemainingAttributes();
>> +        verifyNodeChildrenUsed();
>> +        LOGGER.debug("Calling {} on class {} for element {} {}",
>> factory.getName(), clazz.getName(), node.getName(), sb.toString());
>> +        return args;
>> +    }
>> +
>> +    private static String[] extractPluginAliases(final Annotation...
>> parmTypes) {
>> +        String[] aliases = null;
>> +        for (final Annotation a : parmTypes) {
>> +            if (a instanceof PluginAliases) {
>> +                aliases = ((PluginAliases) a).value();
>> +            }
>> +        }
>> +        return aliases;
>> +    }
>> +
>> +    private String getAttrValue(final String name, final String...
>> aliases) {
>> +        final Map<String, String> attrs = node.getAttributes();
>> +        for (final Map.Entry<String, String> entry : attrs.entrySet()) {
>> +            final String key = entry.getKey();
>> +            if (key.equalsIgnoreCase(name)) {
>> +                final String attr = entry.getValue();
>> +                attrs.remove(key);
>> +                return attr;
>> +            }
>> +            if (aliases != null) {
>> +                for (String alias : aliases) {
>> +                    if (key.equalsIgnoreCase(alias)) {
>> +                        final String attr = entry.getValue();
>> +                        attrs.remove(key);
>> +                        return attr;
>> +                    }
>> +                }
>> +            }
>> +        }
>> +        return null;
>> +    }
>> +
>> +    private static Object[] collectionToArray(final Collection<?>
>> collection, final Class<?> type) {
>> +        final Object[] array = (Object[]) Array.newInstance(type,
>> collection.size());
>> +        int i = 0;
>> +        for (final Object obj : collection) {
>> +            array[i] = obj;
>> +            ++i;
>> +        }
>> +        return array;
>> +    }
>> +
>> +    private static Node findNamedNode(final String name, final Class<?>
>> type, final Iterable<Node> nodes) {
>> +        for (final Node child : nodes) {
>> +            final PluginType<?> childType = child.getType();
>> +            if (name.equalsIgnoreCase(childType.getElementName()) ||
>> +                    type.isAssignableFrom(childType.getPluginClass())) {
>> +                return child;
>> +            }
>> +        }
>> +        return null;
>> +    }
>> +
>> +    private void checkForRemainingAttributes() {
>> +        final Map<String, String> attrs = node.getAttributes();
>> +        if (!attrs.isEmpty()) {
>> +            final StringBuilder eb = new StringBuilder();
>> +            for (final String key : attrs.keySet()) {
>> +                if (eb.length() == 0) {
>> +                    eb.append(node.getName());
>> +                    eb.append(" contains ");
>> +                    if (attrs.size() == 1) {
>> +                        eb.append("an invalid element or attribute ");
>> +                    } else {
>> +                        eb.append("invalid attributes ");
>> +                    }
>> +                } else {
>> +                    eb.append(", ");
>> +                }
>> +                eb.append('"');
>> +                eb.append(key);
>> +                eb.append('"');
>> +
>> +            }
>> +            LOGGER.error(eb.toString());
>> +        }
>> +    }
>> +
>> +    private void verifyNodeChildrenUsed() {
>> +        if (!(pluginType.isDeferChildren() || used.size() ==
>> children.size())) {
>> +            children.removeAll(used);
>> +            for (final Node child : children) {
>> +                final String nodeType = node.getType().getElementName();
>> +                final String start = nodeType.equals(node.getName()) ?
>> node.getName() : nodeType + ' ' + node.getName();
>> +                LOGGER.error("{} has no parameter that matches element
>> {}", start, child.getName());
>> +            }
>> +        }
>> +    }
>> +}
>>
>> Propchange:
>> logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilder.java
>>
>> ------------------------------------------------------------------------------
>>     svn:eol-style = native
>>
>>
>>
>
>


-- 
Matt Sicker <bo...@gmail.com>

Re: svn commit: r1586456 - in /logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config: AbstractConfiguration.java plugins/PluginBuilder.java

Posted by Ralph Goers <ra...@dslextreme.com>.
I understood it the old way just fine. ;-)

Actually, I have no problem with this refactoring.

Ralph

On Apr 10, 2014, at 1:13 PM, Matt Sicker <ma...@apache.org> wrote:

> You may notice that the diff is not very good at showing this, but the PluginBuilder class I added was essentially created by moving a lot of code from AbstractConfiguration to its own class. Now that method is a bit easier to understand, so now I'll be working on supporting more attribute types and such.
> 
> 
> On 10 April 2014 15:06, <ma...@apache.org> wrote:
> Author: mattsicker
> Date: Thu Apr 10 20:06:14 2014
> New Revision: 1586456
> 
> URL: http://svn.apache.org/r1586456
> Log:
> Create PluginBuilder for Configuration objects.
> 
> Added:
>     logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilder.java   (with props)
> Modified:
>     logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
> 
> Modified: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
> URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java?rev=1586456&r1=1586455&r2=1586456&view=diff
> ==============================================================================
> --- logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java (original)
> +++ logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java Thu Apr 10 20:06:14 2014
> @@ -20,11 +20,6 @@ import java.io.ByteArrayOutputStream;
>  import java.io.IOException;
>  import java.io.InputStream;
>  import java.io.Serializable;
> -import java.lang.annotation.Annotation;
> -import java.lang.reflect.Array;
> -import java.lang.reflect.Method;
> -import java.lang.reflect.Modifier;
> -import java.util.ArrayList;
>  import java.util.Collection;
>  import java.util.Collections;
>  import java.util.HashSet;
> @@ -34,7 +29,6 @@ import java.util.Set;
>  import java.util.concurrent.ConcurrentHashMap;
>  import java.util.concurrent.ConcurrentMap;
>  import java.util.concurrent.CopyOnWriteArrayList;
> -
>  import org.apache.logging.log4j.Level;
>  import org.apache.logging.log4j.LogManager;
>  import org.apache.logging.log4j.Logger;
> @@ -46,15 +40,10 @@ import org.apache.logging.log4j.core.app
>  import org.apache.logging.log4j.core.appender.ConsoleAppender;
>  import org.apache.logging.log4j.core.async.AsyncLoggerConfig;
>  import org.apache.logging.log4j.core.async.AsyncLoggerContextSelector;
> -import org.apache.logging.log4j.core.config.plugins.PluginAliases;
> -import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
> -import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
> -import org.apache.logging.log4j.core.config.plugins.PluginElement;
> +import org.apache.logging.log4j.core.config.plugins.PluginBuilder;
>  import org.apache.logging.log4j.core.config.plugins.PluginFactory;
>  import org.apache.logging.log4j.core.config.plugins.PluginManager;
> -import org.apache.logging.log4j.core.config.plugins.PluginNode;
>  import org.apache.logging.log4j.core.config.plugins.PluginType;
> -import org.apache.logging.log4j.core.config.plugins.PluginValue;
>  import org.apache.logging.log4j.core.filter.AbstractFilterable;
>  import org.apache.logging.log4j.core.helpers.Constants;
>  import org.apache.logging.log4j.core.helpers.NameUtil;
> @@ -666,10 +655,11 @@ public abstract class AbstractConfigurat
>          }
>      }
> 
> -   /*
> -    * Retrieve a static public 'method to create the desired object. Every parameter
> -    * will be annotated to identify the appropriate attribute or element to use to
> +   /**
> +    * Invokes a static factory method to create the desired object. Every parameter
> +    * must be annotated to identify the appropriate attribute or element to use to
>      * set the value of the parameter.
> +    *
>      * Parameters annotated with PluginAttribute will always be set as Strings.
>      * Parameters annotated with PluginElement may be Objects or arrays. Collections
>      * and Maps are currently not supported, although the factory method that is called
> @@ -677,9 +667,11 @@ public abstract class AbstractConfigurat
>      *
>      * Although the happy path works, more work still needs to be done to log incorrect
>      * parameters. These will generally result in unhelpful InvocationTargetExceptions.
> -    * @param classClass the class.
> -    * @return the instantiate method or null if there is none by that
> -    * description.
> +    *
> +    * @param type the type of plugin to create.
> +    * @param node the corresponding configuration node for this plugin to create.
> +    * @param event the LogEvent that spurred the creation of this plugin
> +    * @return the created plugin object or {@code null} if there was an error setting it up.
>      */
>      private <T> Object createPluginObject(final PluginType<T> type, final Node node, final LogEvent event)
>      {
> @@ -702,216 +694,17 @@ public abstract class AbstractConfigurat
>              }
>          }
> 
> -        Method factoryMethod = findFactoryMethod(clazz);
> -        if (factoryMethod == null) return null;
> -
> -        final Annotation[][] parmArray = factoryMethod.getParameterAnnotations();
> -        final Class<?>[] parmClasses = factoryMethod.getParameterTypes();
> -        if (parmArray.length != parmClasses.length) {
> -            LOGGER.error("Number of parameter annotations ({}) does not equal the number of parameters ({})",
> -                    parmArray.length, parmClasses.length
> -            );
> -        }
> -        final Object[] parms = new Object[parmClasses.length];
> -
> -        int index = 0;
> -        final Map<String, String> attrs = node.getAttributes();
> -        final List<Node> children = node.getChildren();
> -        final StringBuilder sb = new StringBuilder();
> -        final List<Node> used = new ArrayList<Node>();
> -
> -        /*
> -         * For each parameter:
> -         * If the parameter is an attribute store the value of the attribute in the parameter array.
> -         * If the parameter is an element:
> -         *   Determine if the required parameter is an array.
> -         *     If so, if a child contains the array, use it,
> -         *      otherwise create the array from all child nodes of the correct type.
> -         *     Store the array into the parameter array.
> -         *   If not an array, store the object in the child node into the parameter array.
> -         */
> -        for (final Annotation[] parmTypes : parmArray) {
> -            String[] aliases = extractPluginAliases(parmTypes);
> -            for (final Annotation a : parmTypes) {
> -                if (a instanceof PluginAliases) {
> -                    continue;
> -                }
> -                if (sb.length() == 0) {
> -                    sb.append(" with params(");
> -                } else {
> -                    sb.append(", ");
> -                }
> -                if (a instanceof PluginNode) {
> -                    parms[index] = node;
> -                    sb.append("Node=").append(node.getName());
> -                } else if (a instanceof PluginConfiguration) {
> -                    parms[index] = this;
> -                    if (this.name != null) {
> -                        sb.append("Configuration(").append(name).append(')');
> -                    } else {
> -                        sb.append("Configuration");
> -                    }
> -                } else if (a instanceof PluginValue) {
> -                    final String name = ((PluginValue) a).value();
> -                    String v = node.getValue();
> -                    if (v == null) {
> -                        v = getAttrValue("value", null, attrs);
> -                    }
> -                    final String value = subst.replace(event, v);
> -                    sb.append(name).append("=\"").append(value).append('"');
> -                    parms[index] = value;
> -                } else if (a instanceof PluginAttribute) {
> -                    PluginAttribute attr = (PluginAttribute) a;
> -                    final String name = attr.value();
> -                    final String value = subst.replace(event, getAttrValue(name, aliases, attrs));
> -                    sb.append(name).append("=\"").append(value).append('"');
> -                    parms[index] = value;
> -                } else if (a instanceof PluginElement) {
> -                    final PluginElement elem = (PluginElement) a;
> -                    final String name = elem.value();
> -                    if (parmClasses[index].isArray()) {
> -                        final Class<?> parmClass = parmClasses[index].getComponentType();
> -                        final List<Object> list = new ArrayList<Object>();
> -                        sb.append(name).append("={");
> -                        boolean first = true;
> -                        for (final Node child : children) {
> -                            final PluginType<?> childType = child.getType();
> -                            if (name.equalsIgnoreCase(childType.getElementName()) ||
> -                                    parmClass.isAssignableFrom(childType.getPluginClass())) {
> -                                used.add(child);
> -                                if (!first) {
> -                                    sb.append(", ");
> -                                }
> -                                first = false;
> -                                final Object obj = child.getObject();
> -                                if (obj == null) {
> -                                    LOGGER.error("Null object returned for {} in {}", child.getName(), node.getName());
> -                                    continue;
> -                                }
> -                                if (obj.getClass().isArray()) {
> -                                    printArray(sb, (Object[]) obj);
> -                                    parms[index] = obj;
> -                                    break;
> -                                }
> -                                sb.append(child.toString());
> -                                list.add(obj);
> -                            }
> -                        }
> -                        sb.append('}');
> -                        if (parms[index] != null) {
> -                            break;
> -                        }
> -                        if (!(list.isEmpty() || parmClass.isAssignableFrom(list.get(0).getClass()))) {
> -                            LOGGER.error(
> -                                    "Attempted to assign List containing class {} to array of type {} for attribute {}",
> -                                    list.get(0).getClass().getName(), parmClass, name
> -                            );
> -                            break;
> -                        }
> -                        parms[index] = collectionToArray(list, parmClass);
> -                    } else {
> -                        final Node child = findNamedNode(name, parmClasses[index], children);
> -                        if (child == null) {
> -                            sb.append("null");
> -                        } else {
> -                            sb.append(child.getName()).append('(').append(child.toString()).append(')');
> -                            used.add(child);
> -                            parms[index] = child.getObject();
> -                        }
> -                    }
> -                }
> -            }
> -            ++index;
> -        }
> -        if (sb.length() > 0) {
> -            sb.append(')');
> -        }
> -
> -        checkForRemainingAttributes(node);
> -
> -        if (!type.isDeferChildren() && used.size() != children.size()) {
> -            children.removeAll(used);
> -            for (final Node child : children) {
> -                final String nodeType = node.getType().getElementName();
> -                final String start = nodeType.equals(node.getName()) ? node.getName() : nodeType + ' ' + node.getName();
> -                LOGGER.error("{} has no parameter that matches element {}", start, child.getName());
> -            }
> -        }
> -
>          try {
> -            final int mod = factoryMethod.getModifiers();
> -            if (!Modifier.isStatic(mod)) {
> -                LOGGER.error("{} method is not static on class {} for element {}",
> -                        factoryMethod.getName(), clazz.getName(), node.getName());
> -                return null;
> -            }
> -            LOGGER.debug("Calling {} on class {} for element {}{}", factoryMethod.getName(), clazz.getName(),
> -                    node.getName(), sb.toString());
> -            //if (parms.length > 0) {
> -            return factoryMethod.invoke(null, parms);
> -            //}
> -            //return factoryMethod.invoke(null, node);
> -        } catch (final Exception e) {
> -            LOGGER.error("Unable to invoke method {} in class {} for element {}",
> -                    factoryMethod.getName(), clazz.getName(), node.getName(), e);
> -        }
> -        return null;
> -    }
> -
> -    private static Object[] collectionToArray(final Collection<?> collection, final Class<?> type) {
> -        final Object[] array = (Object[]) Array.newInstance(type, collection.size());
> -        int i = 0;
> -        for (final Object obj : collection) {
> -            array[i] = obj;
> -            ++i;
> -        }
> -        return array;
> -    }
> -
> -    private static Node findNamedNode(final String name, final Class<?> type, final Iterable<Node> nodes) {
> -        for (final Node child : nodes) {
> -            final PluginType<?> childType = child.getType();
> -            if (name.equalsIgnoreCase(childType.getElementName()) ||
> -                    type.isAssignableFrom(childType.getPluginClass())) {
> -                return child;
> -            }
> -        }
> -        return null;
> -    }
> -
> -    private static void checkForRemainingAttributes(final Node node) {
> -        final Map<String, String> attrs = node.getAttributes();
> -        if (!attrs.isEmpty()) {
> -            final StringBuilder eb = new StringBuilder();
> -            for (final String key : attrs.keySet()) {
> -                if (eb.length() == 0) {
> -                    eb.append(node.getName());
> -                    eb.append(" contains ");
> -                    if (attrs.size() == 1) {
> -                        eb.append("an invalid element or attribute ");
> -                    } else {
> -                        eb.append("invalid attributes ");
> -                    }
> -                } else {
> -                    eb.append(", ");
> -                }
> -                eb.append('"');
> -                eb.append(key);
> -                eb.append('"');
> -
> -            }
> -            LOGGER.error(eb.toString());
> -        }
> -    }
> -
> -    private static String[] extractPluginAliases(final Annotation... parmTypes) {
> -        String[] aliases = null;
> -        for (final Annotation a : parmTypes) {
> -            if (a instanceof PluginAliases) {
> -                aliases = ((PluginAliases) a).value();
> -            }
> +            return new PluginBuilder<T>(type)
> +                    .withFactoryMethodAnnotatedBy(PluginFactory.class)
> +                    .withConfiguration(this)
> +                    .withConfigurationNode(node)
> +                    .forLogEvent(event)
> +                    .build();
> +        } catch (NoSuchMethodException e) {
> +            LOGGER.error("No suitable factory method could be found on class {}", clazz, e);
> +            return null;
>          }
> -        return aliases;
>      }
> 
>      private static <T> Object createPluginMap(final Node node, final Class<T> clazz) throws InstantiationException, IllegalAccessException {
> @@ -932,48 +725,6 @@ public abstract class AbstractConfigurat
>          return list;
>      }
> 
> -    private static <T> Method findFactoryMethod(final Class<T> clazz) {
> -        for (final Method method : clazz.getMethods()) {
> -            if (method.isAnnotationPresent(PluginFactory.class)) {
> -                return method;
> -            }
> -        }
> -        // TODO: this should probably throw an exception instead of returning null
> -        return null;
> -    }
> -
> -    private void printArray(final StringBuilder sb, final Object... array) {
> -        boolean first = true;
> -        for (final Object obj : array) {
> -            if (!first) {
> -                sb.append(", ");
> -            }
> -            sb.append(obj.toString());
> -            first = false;
> -        }
> -    }
> -
> -    private String getAttrValue(final String name, final String[] aliases, final Map<String, String> attrs) {
> -        for (final Map.Entry<String, String> entry : attrs.entrySet()) {
> -            final String key = entry.getKey();
> -            if (key.equalsIgnoreCase(name)) {
> -                final String attr = entry.getValue();
> -                attrs.remove(key);
> -                return attr;
> -            }
> -            if (aliases != null) {
> -                for (String alias : aliases) {
> -                    if (key.equalsIgnoreCase(alias)) {
> -                        final String attr = entry.getValue();
> -                        attrs.remove(key);
> -                        return attr;
> -                    }
> -                }
> -            }
> -        }
> -        return null;
> -    }
> -
>      private void setParents() {
>           for (final Map.Entry<String, LoggerConfig> entry : loggers.entrySet()) {
>              final LoggerConfig logger = entry.getValue();
> 
> Added: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilder.java
> URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilder.java?rev=1586456&view=auto
> ==============================================================================
> --- logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilder.java (added)
> +++ logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilder.java Thu Apr 10 20:06:14 2014
> @@ -0,0 +1,337 @@
> +/*
> + * 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.plugins;
> +
> +import java.lang.annotation.Annotation;
> +import java.lang.reflect.Array;
> +import java.lang.reflect.Method;
> +import java.lang.reflect.Modifier;
> +import java.util.ArrayList;
> +import java.util.Arrays;
> +import java.util.Collection;
> +import java.util.HashSet;
> +import java.util.List;
> +import java.util.Map;
> +import org.apache.logging.log4j.Logger;
> +import org.apache.logging.log4j.core.LogEvent;
> +import org.apache.logging.log4j.core.config.Configuration;
> +import org.apache.logging.log4j.core.config.Node;
> +import org.apache.logging.log4j.status.StatusLogger;
> +
> +/**
> + * Builder class to instantiate and configure a Plugin object using a PluginFactory method.
> + *
> + * @param <T> type of Plugin class.
> + */
> +public class PluginBuilder<T> {
> +
> +    private static final Logger LOGGER = StatusLogger.getLogger();
> +
> +    private final PluginType<T> pluginType;
> +    private final Class<T> clazz;
> +
> +    private Configuration configuration;
> +    private Node node;
> +    private LogEvent event;
> +
> +    private Method factory;
> +    private Annotation[][] annotations;
> +    private Class<?>[] types;
> +    private List<Node> children;
> +    private Collection<Node> used;
> +
> +    /**
> +     * Constructs a PluginBuilder for a given PluginType.
> +     *
> +     * @param pluginType type of plugin to configure
> +     */
> +    public PluginBuilder(final PluginType<T> pluginType) {
> +        this.pluginType = pluginType;
> +        this.clazz = pluginType.getPluginClass();
> +    }
> +
> +    /**
> +     * Specifies which annotation denotes a plugin factory method. The method must be static.
> +     *
> +     * @param annotationType class of annotation marking the plugin factory.
> +     * @param <A>            type of annotation.
> +     * @return {@code this}
> +     * @throws NoSuchMethodException
> +     */
> +    public <A extends Annotation> PluginBuilder<T> withFactoryMethodAnnotatedBy(final Class<A> annotationType)
> +            throws NoSuchMethodException {
> +        for (final Method method : clazz.getMethods()) {
> +            if (method.isAnnotationPresent(annotationType) && Modifier.isStatic(method.getModifiers())) {
> +                factory = method;
> +                LOGGER.trace("Using factory method {} on class {}", method.getName(), clazz.getName());
> +                return this;
> +            }
> +        }
> +        throw new NoSuchMethodException("No method annotated with " + annotationType.getName() + "was found in " + clazz.getName());
> +    }
> +
> +    /**
> +     * Specifies the Configuration to use for constructing the plugin instance.
> +     *
> +     * @param configuration the configuration to use.
> +     * @return {@code this}
> +     */
> +    public PluginBuilder<T> withConfiguration(final Configuration configuration) {
> +        this.configuration = configuration;
> +        return this;
> +    }
> +
> +    /**
> +     * Specifies the Node corresponding to the plugin object that will be created.
> +     *
> +     * @param node the plugin configuration node to use.
> +     * @return {@code this}
> +     */
> +    public PluginBuilder<T> withConfigurationNode(final Node node) {
> +        this.node = node;
> +        this.children = this.node.getChildren();
> +        this.used = new HashSet<Node>(this.children.size());
> +        return this;
> +    }
> +
> +    /**
> +     * Specifies the LogEvent that may be used to provide extra context for string substitutions.
> +     *
> +     * @param event the event to use for extra information.
> +     * @return {@code this}
> +     */
> +    public PluginBuilder<T> forLogEvent(final LogEvent event) {
> +        this.event = event;
> +        return this;
> +    }
> +
> +    /**
> +     * Builds the plugin object.
> +     *
> +     * @return the plugin object or {@code null} if there was a problem creating it.
> +     */
> +    public Object build() {
> +        init();
> +        try {
> +            return factory.invoke(null, generateParameters());
> +        } catch (final Exception e) {
> +            LOGGER.error("Unable to invoke method {} in class {} for element {}",
> +                    factory.getName(), clazz.getName(), node.getName(), e);
> +            return null;
> +        }
> +    }
> +
> +    private void init() {
> +        if (factory == null) {
> +            throw new IllegalStateException("No factory method was found.");
> +        }
> +        if (configuration == null) {
> +            throw new IllegalStateException("No Configuration object was set.");
> +        }
> +        if (node == null) {
> +            throw new IllegalStateException("No Node object was set.");
> +        }
> +        annotations = factory.getParameterAnnotations();
> +        types = factory.getParameterTypes();
> +    }
> +
> +    private Object[] generateParameters() {
> +        final StringBuilder sb = new StringBuilder();
> +        final Object[] args = new Object[annotations.length];
> +        for (int i = 0; i < annotations.length; i++) {
> +            final String[] aliases = extractPluginAliases(annotations[i]);
> +            for (Annotation a : annotations[i]) {
> +                if (a instanceof PluginAliases) {
> +                    continue; // already processed
> +                }
> +                sb.append(sb.length() == 0 ? "with params(" : ", ");
> +                if (a instanceof PluginNode) {
> +                    args[i] = node;
> +                    sb.append("Node=").append(node.getName());
> +                } else if (a instanceof PluginConfiguration) {
> +                    args[i] = configuration;
> +                    sb.append("Configuration");
> +                    if (configuration.getName() != null) {
> +                        sb.append('(').append(configuration.getName()).append(')');
> +                    }
> +                } else if (a instanceof PluginValue) {
> +                    final String name = ((PluginValue) a).value();
> +                    final String v = node.getValue() != null ? node.getValue() : getAttrValue("value");
> +                    final String value = configuration.getStrSubstitutor().replace(event, v);
> +                    args[i] = value;
> +                    sb.append(name).append("=\"").append(value).append('"');
> +                } else if (a instanceof PluginAttribute) {
> +                    final PluginAttribute attribute = (PluginAttribute) a;
> +                    final String name = attribute.value();
> +                    final String value = configuration.getStrSubstitutor().replace(event, getAttrValue(name, aliases));
> +                    args[i] = value;
> +                    sb.append(name).append("=\"").append(value).append('"');
> +                } else if (a instanceof PluginElement) {
> +                    final PluginElement element = (PluginElement) a;
> +                    final String name = element.value();
> +                    if (types[i].isArray()) {
> +                        final Class<?> type = types[i].getComponentType();
> +                        sb.append(name).append("={");
> +                        final List<Object> values = new ArrayList<Object>();
> +                        boolean first = true;
> +                        for (final Node child : children) {
> +                            final PluginType<?> childType = child.getType();
> +                            if (name.equalsIgnoreCase(childType.getElementName()) ||
> +                                    type.isAssignableFrom(childType.getPluginClass())) {
> +                                if (!first) {
> +                                    sb.append(", ");
> +                                }
> +                                first = false;
> +                                used.add(child);
> +                                final Object o = child.getObject();
> +                                if (o == null) {
> +                                    LOGGER.error("Null object returned for {} in {}", child.getName(), node.getName());
> +                                    continue;
> +                                }
> +                                if (o.getClass().isArray()) {
> +                                    sb.append(Arrays.toString((Object[]) o));
> +                                    args[i] = o;
> +                                    break;
> +                                }
> +                                sb.append(child.toString());
> +                                values.add(o);
> +                            }
> +                        }
> +                        sb.append('}');
> +                        if (args[i] != null) {
> +                            break;
> +                        }
> +                        if (!(values.isEmpty() || type.isAssignableFrom(values.get(0).getClass()))) {
> +                            LOGGER.error(
> +                                    "Attempted to assign List containing class {} to array of type {} for attribute {}",
> +                                    values.get(0).getClass().getName(), type, name
> +                            );
> +                            break;
> +                        }
> +                        args[i] = collectionToArray(values, type);
> +                    } else {
> +                        final Node namedNode = findNamedNode(name, types[i], children);
> +                        if (namedNode == null) {
> +                            sb.append("null");
> +                        } else {
> +                            sb.append(namedNode.getName()).append('(').append(namedNode.toString()).append(')');
> +                            used.add(namedNode);
> +                            args[i] = namedNode.getObject();
> +                        }
> +                    }
> +                }
> +            }
> +        }
> +        if (sb.length() > 0) {
> +            sb.append(')');
> +        }
> +        checkForRemainingAttributes();
> +        verifyNodeChildrenUsed();
> +        LOGGER.debug("Calling {} on class {} for element {} {}", factory.getName(), clazz.getName(), node.getName(), sb.toString());
> +        return args;
> +    }
> +
> +    private static String[] extractPluginAliases(final Annotation... parmTypes) {
> +        String[] aliases = null;
> +        for (final Annotation a : parmTypes) {
> +            if (a instanceof PluginAliases) {
> +                aliases = ((PluginAliases) a).value();
> +            }
> +        }
> +        return aliases;
> +    }
> +
> +    private String getAttrValue(final String name, final String... aliases) {
> +        final Map<String, String> attrs = node.getAttributes();
> +        for (final Map.Entry<String, String> entry : attrs.entrySet()) {
> +            final String key = entry.getKey();
> +            if (key.equalsIgnoreCase(name)) {
> +                final String attr = entry.getValue();
> +                attrs.remove(key);
> +                return attr;
> +            }
> +            if (aliases != null) {
> +                for (String alias : aliases) {
> +                    if (key.equalsIgnoreCase(alias)) {
> +                        final String attr = entry.getValue();
> +                        attrs.remove(key);
> +                        return attr;
> +                    }
> +                }
> +            }
> +        }
> +        return null;
> +    }
> +
> +    private static Object[] collectionToArray(final Collection<?> collection, final Class<?> type) {
> +        final Object[] array = (Object[]) Array.newInstance(type, collection.size());
> +        int i = 0;
> +        for (final Object obj : collection) {
> +            array[i] = obj;
> +            ++i;
> +        }
> +        return array;
> +    }
> +
> +    private static Node findNamedNode(final String name, final Class<?> type, final Iterable<Node> nodes) {
> +        for (final Node child : nodes) {
> +            final PluginType<?> childType = child.getType();
> +            if (name.equalsIgnoreCase(childType.getElementName()) ||
> +                    type.isAssignableFrom(childType.getPluginClass())) {
> +                return child;
> +            }
> +        }
> +        return null;
> +    }
> +
> +    private void checkForRemainingAttributes() {
> +        final Map<String, String> attrs = node.getAttributes();
> +        if (!attrs.isEmpty()) {
> +            final StringBuilder eb = new StringBuilder();
> +            for (final String key : attrs.keySet()) {
> +                if (eb.length() == 0) {
> +                    eb.append(node.getName());
> +                    eb.append(" contains ");
> +                    if (attrs.size() == 1) {
> +                        eb.append("an invalid element or attribute ");
> +                    } else {
> +                        eb.append("invalid attributes ");
> +                    }
> +                } else {
> +                    eb.append(", ");
> +                }
> +                eb.append('"');
> +                eb.append(key);
> +                eb.append('"');
> +
> +            }
> +            LOGGER.error(eb.toString());
> +        }
> +    }
> +
> +    private void verifyNodeChildrenUsed() {
> +        if (!(pluginType.isDeferChildren() || used.size() == children.size())) {
> +            children.removeAll(used);
> +            for (final Node child : children) {
> +                final String nodeType = node.getType().getElementName();
> +                final String start = nodeType.equals(node.getName()) ? node.getName() : nodeType + ' ' + node.getName();
> +                LOGGER.error("{} has no parameter that matches element {}", start, child.getName());
> +            }
> +        }
> +    }
> +}
> 
> Propchange: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilder.java
> ------------------------------------------------------------------------------
>     svn:eol-style = native
> 
> 
> 


Re: svn commit: r1586456 - in /logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config: AbstractConfiguration.java plugins/PluginBuilder.java

Posted by Matt Sicker <ma...@apache.org>.
You may notice that the diff is not very good at showing this, but the
PluginBuilder class I added was essentially created by moving a lot of code
from AbstractConfiguration to its own class. Now that method is a bit
easier to understand, so now I'll be working on supporting more attribute
types and such.


On 10 April 2014 15:06, <ma...@apache.org> wrote:

> Author: mattsicker
> Date: Thu Apr 10 20:06:14 2014
> New Revision: 1586456
>
> URL: http://svn.apache.org/r1586456
> Log:
> Create PluginBuilder for Configuration objects.
>
> Added:
>
> logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilder.java
>   (with props)
> Modified:
>
> logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
>
> Modified:
> logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
> URL:
> http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java?rev=1586456&r1=1586455&r2=1586456&view=diff
>
> ==============================================================================
> ---
> logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
> (original)
> +++
> logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
> Thu Apr 10 20:06:14 2014
> @@ -20,11 +20,6 @@ import java.io.ByteArrayOutputStream;
>  import java.io.IOException;
>  import java.io.InputStream;
>  import java.io.Serializable;
> -import java.lang.annotation.Annotation;
> -import java.lang.reflect.Array;
> -import java.lang.reflect.Method;
> -import java.lang.reflect.Modifier;
> -import java.util.ArrayList;
>  import java.util.Collection;
>  import java.util.Collections;
>  import java.util.HashSet;
> @@ -34,7 +29,6 @@ import java.util.Set;
>  import java.util.concurrent.ConcurrentHashMap;
>  import java.util.concurrent.ConcurrentMap;
>  import java.util.concurrent.CopyOnWriteArrayList;
> -
>  import org.apache.logging.log4j.Level;
>  import org.apache.logging.log4j.LogManager;
>  import org.apache.logging.log4j.Logger;
> @@ -46,15 +40,10 @@ import org.apache.logging.log4j.core.app
>  import org.apache.logging.log4j.core.appender.ConsoleAppender;
>  import org.apache.logging.log4j.core.async.AsyncLoggerConfig;
>  import org.apache.logging.log4j.core.async.AsyncLoggerContextSelector;
> -import org.apache.logging.log4j.core.config.plugins.PluginAliases;
> -import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
> -import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
> -import org.apache.logging.log4j.core.config.plugins.PluginElement;
> +import org.apache.logging.log4j.core.config.plugins.PluginBuilder;
>  import org.apache.logging.log4j.core.config.plugins.PluginFactory;
>  import org.apache.logging.log4j.core.config.plugins.PluginManager;
> -import org.apache.logging.log4j.core.config.plugins.PluginNode;
>  import org.apache.logging.log4j.core.config.plugins.PluginType;
> -import org.apache.logging.log4j.core.config.plugins.PluginValue;
>  import org.apache.logging.log4j.core.filter.AbstractFilterable;
>  import org.apache.logging.log4j.core.helpers.Constants;
>  import org.apache.logging.log4j.core.helpers.NameUtil;
> @@ -666,10 +655,11 @@ public abstract class AbstractConfigurat
>          }
>      }
>
> -   /*
> -    * Retrieve a static public 'method to create the desired object.
> Every parameter
> -    * will be annotated to identify the appropriate attribute or element
> to use to
> +   /**
> +    * Invokes a static factory method to create the desired object. Every
> parameter
> +    * must be annotated to identify the appropriate attribute or element
> to use to
>      * set the value of the parameter.
> +    *
>      * Parameters annotated with PluginAttribute will always be set as
> Strings.
>      * Parameters annotated with PluginElement may be Objects or arrays.
> Collections
>      * and Maps are currently not supported, although the factory method
> that is called
> @@ -677,9 +667,11 @@ public abstract class AbstractConfigurat
>      *
>      * Although the happy path works, more work still needs to be done to
> log incorrect
>      * parameters. These will generally result in unhelpful
> InvocationTargetExceptions.
> -    * @param classClass the class.
> -    * @return the instantiate method or null if there is none by that
> -    * description.
> +    *
> +    * @param type the type of plugin to create.
> +    * @param node the corresponding configuration node for this plugin to
> create.
> +    * @param event the LogEvent that spurred the creation of this plugin
> +    * @return the created plugin object or {@code null} if there was an
> error setting it up.
>      */
>      private <T> Object createPluginObject(final PluginType<T> type, final
> Node node, final LogEvent event)
>      {
> @@ -702,216 +694,17 @@ public abstract class AbstractConfigurat
>              }
>          }
>
> -        Method factoryMethod = findFactoryMethod(clazz);
> -        if (factoryMethod == null) return null;
> -
> -        final Annotation[][] parmArray =
> factoryMethod.getParameterAnnotations();
> -        final Class<?>[] parmClasses = factoryMethod.getParameterTypes();
> -        if (parmArray.length != parmClasses.length) {
> -            LOGGER.error("Number of parameter annotations ({}) does not
> equal the number of parameters ({})",
> -                    parmArray.length, parmClasses.length
> -            );
> -        }
> -        final Object[] parms = new Object[parmClasses.length];
> -
> -        int index = 0;
> -        final Map<String, String> attrs = node.getAttributes();
> -        final List<Node> children = node.getChildren();
> -        final StringBuilder sb = new StringBuilder();
> -        final List<Node> used = new ArrayList<Node>();
> -
> -        /*
> -         * For each parameter:
> -         * If the parameter is an attribute store the value of the
> attribute in the parameter array.
> -         * If the parameter is an element:
> -         *   Determine if the required parameter is an array.
> -         *     If so, if a child contains the array, use it,
> -         *      otherwise create the array from all child nodes of the
> correct type.
> -         *     Store the array into the parameter array.
> -         *   If not an array, store the object in the child node into the
> parameter array.
> -         */
> -        for (final Annotation[] parmTypes : parmArray) {
> -            String[] aliases = extractPluginAliases(parmTypes);
> -            for (final Annotation a : parmTypes) {
> -                if (a instanceof PluginAliases) {
> -                    continue;
> -                }
> -                if (sb.length() == 0) {
> -                    sb.append(" with params(");
> -                } else {
> -                    sb.append(", ");
> -                }
> -                if (a instanceof PluginNode) {
> -                    parms[index] = node;
> -                    sb.append("Node=").append(node.getName());
> -                } else if (a instanceof PluginConfiguration) {
> -                    parms[index] = this;
> -                    if (this.name != null) {
> -
>  sb.append("Configuration(").append(name).append(')');
> -                    } else {
> -                        sb.append("Configuration");
> -                    }
> -                } else if (a instanceof PluginValue) {
> -                    final String name = ((PluginValue) a).value();
> -                    String v = node.getValue();
> -                    if (v == null) {
> -                        v = getAttrValue("value", null, attrs);
> -                    }
> -                    final String value = subst.replace(event, v);
> -
>  sb.append(name).append("=\"").append(value).append('"');
> -                    parms[index] = value;
> -                } else if (a instanceof PluginAttribute) {
> -                    PluginAttribute attr = (PluginAttribute) a;
> -                    final String name = attr.value();
> -                    final String value = subst.replace(event,
> getAttrValue(name, aliases, attrs));
> -
>  sb.append(name).append("=\"").append(value).append('"');
> -                    parms[index] = value;
> -                } else if (a instanceof PluginElement) {
> -                    final PluginElement elem = (PluginElement) a;
> -                    final String name = elem.value();
> -                    if (parmClasses[index].isArray()) {
> -                        final Class<?> parmClass =
> parmClasses[index].getComponentType();
> -                        final List<Object> list = new ArrayList<Object>();
> -                        sb.append(name).append("={");
> -                        boolean first = true;
> -                        for (final Node child : children) {
> -                            final PluginType<?> childType =
> child.getType();
> -                            if
> (name.equalsIgnoreCase(childType.getElementName()) ||
> -
>  parmClass.isAssignableFrom(childType.getPluginClass())) {
> -                                used.add(child);
> -                                if (!first) {
> -                                    sb.append(", ");
> -                                }
> -                                first = false;
> -                                final Object obj = child.getObject();
> -                                if (obj == null) {
> -                                    LOGGER.error("Null object returned
> for {} in {}", child.getName(), node.getName());
> -                                    continue;
> -                                }
> -                                if (obj.getClass().isArray()) {
> -                                    printArray(sb, (Object[]) obj);
> -                                    parms[index] = obj;
> -                                    break;
> -                                }
> -                                sb.append(child.toString());
> -                                list.add(obj);
> -                            }
> -                        }
> -                        sb.append('}');
> -                        if (parms[index] != null) {
> -                            break;
> -                        }
> -                        if (!(list.isEmpty() ||
> parmClass.isAssignableFrom(list.get(0).getClass()))) {
> -                            LOGGER.error(
> -                                    "Attempted to assign List containing
> class {} to array of type {} for attribute {}",
> -                                    list.get(0).getClass().getName(),
> parmClass, name
> -                            );
> -                            break;
> -                        }
> -                        parms[index] = collectionToArray(list, parmClass);
> -                    } else {
> -                        final Node child = findNamedNode(name,
> parmClasses[index], children);
> -                        if (child == null) {
> -                            sb.append("null");
> -                        } else {
> -
>  sb.append(child.getName()).append('(').append(child.toString()).append(')');
> -                            used.add(child);
> -                            parms[index] = child.getObject();
> -                        }
> -                    }
> -                }
> -            }
> -            ++index;
> -        }
> -        if (sb.length() > 0) {
> -            sb.append(')');
> -        }
> -
> -        checkForRemainingAttributes(node);
> -
> -        if (!type.isDeferChildren() && used.size() != children.size()) {
> -            children.removeAll(used);
> -            for (final Node child : children) {
> -                final String nodeType = node.getType().getElementName();
> -                final String start = nodeType.equals(node.getName()) ?
> node.getName() : nodeType + ' ' + node.getName();
> -                LOGGER.error("{} has no parameter that matches element
> {}", start, child.getName());
> -            }
> -        }
> -
>          try {
> -            final int mod = factoryMethod.getModifiers();
> -            if (!Modifier.isStatic(mod)) {
> -                LOGGER.error("{} method is not static on class {} for
> element {}",
> -                        factoryMethod.getName(), clazz.getName(),
> node.getName());
> -                return null;
> -            }
> -            LOGGER.debug("Calling {} on class {} for element {}{}",
> factoryMethod.getName(), clazz.getName(),
> -                    node.getName(), sb.toString());
> -            //if (parms.length > 0) {
> -            return factoryMethod.invoke(null, parms);
> -            //}
> -            //return factoryMethod.invoke(null, node);
> -        } catch (final Exception e) {
> -            LOGGER.error("Unable to invoke method {} in class {} for
> element {}",
> -                    factoryMethod.getName(), clazz.getName(),
> node.getName(), e);
> -        }
> -        return null;
> -    }
> -
> -    private static Object[] collectionToArray(final Collection<?>
> collection, final Class<?> type) {
> -        final Object[] array = (Object[]) Array.newInstance(type,
> collection.size());
> -        int i = 0;
> -        for (final Object obj : collection) {
> -            array[i] = obj;
> -            ++i;
> -        }
> -        return array;
> -    }
> -
> -    private static Node findNamedNode(final String name, final Class<?>
> type, final Iterable<Node> nodes) {
> -        for (final Node child : nodes) {
> -            final PluginType<?> childType = child.getType();
> -            if (name.equalsIgnoreCase(childType.getElementName()) ||
> -                    type.isAssignableFrom(childType.getPluginClass())) {
> -                return child;
> -            }
> -        }
> -        return null;
> -    }
> -
> -    private static void checkForRemainingAttributes(final Node node) {
> -        final Map<String, String> attrs = node.getAttributes();
> -        if (!attrs.isEmpty()) {
> -            final StringBuilder eb = new StringBuilder();
> -            for (final String key : attrs.keySet()) {
> -                if (eb.length() == 0) {
> -                    eb.append(node.getName());
> -                    eb.append(" contains ");
> -                    if (attrs.size() == 1) {
> -                        eb.append("an invalid element or attribute ");
> -                    } else {
> -                        eb.append("invalid attributes ");
> -                    }
> -                } else {
> -                    eb.append(", ");
> -                }
> -                eb.append('"');
> -                eb.append(key);
> -                eb.append('"');
> -
> -            }
> -            LOGGER.error(eb.toString());
> -        }
> -    }
> -
> -    private static String[] extractPluginAliases(final Annotation...
> parmTypes) {
> -        String[] aliases = null;
> -        for (final Annotation a : parmTypes) {
> -            if (a instanceof PluginAliases) {
> -                aliases = ((PluginAliases) a).value();
> -            }
> +            return new PluginBuilder<T>(type)
> +                    .withFactoryMethodAnnotatedBy(PluginFactory.class)
> +                    .withConfiguration(this)
> +                    .withConfigurationNode(node)
> +                    .forLogEvent(event)
> +                    .build();
> +        } catch (NoSuchMethodException e) {
> +            LOGGER.error("No suitable factory method could be found on
> class {}", clazz, e);
> +            return null;
>          }
> -        return aliases;
>      }
>
>      private static <T> Object createPluginMap(final Node node, final
> Class<T> clazz) throws InstantiationException, IllegalAccessException {
> @@ -932,48 +725,6 @@ public abstract class AbstractConfigurat
>          return list;
>      }
>
> -    private static <T> Method findFactoryMethod(final Class<T> clazz) {
> -        for (final Method method : clazz.getMethods()) {
> -            if (method.isAnnotationPresent(PluginFactory.class)) {
> -                return method;
> -            }
> -        }
> -        // TODO: this should probably throw an exception instead of
> returning null
> -        return null;
> -    }
> -
> -    private void printArray(final StringBuilder sb, final Object...
> array) {
> -        boolean first = true;
> -        for (final Object obj : array) {
> -            if (!first) {
> -                sb.append(", ");
> -            }
> -            sb.append(obj.toString());
> -            first = false;
> -        }
> -    }
> -
> -    private String getAttrValue(final String name, final String[]
> aliases, final Map<String, String> attrs) {
> -        for (final Map.Entry<String, String> entry : attrs.entrySet()) {
> -            final String key = entry.getKey();
> -            if (key.equalsIgnoreCase(name)) {
> -                final String attr = entry.getValue();
> -                attrs.remove(key);
> -                return attr;
> -            }
> -            if (aliases != null) {
> -                for (String alias : aliases) {
> -                    if (key.equalsIgnoreCase(alias)) {
> -                        final String attr = entry.getValue();
> -                        attrs.remove(key);
> -                        return attr;
> -                    }
> -                }
> -            }
> -        }
> -        return null;
> -    }
> -
>      private void setParents() {
>           for (final Map.Entry<String, LoggerConfig> entry :
> loggers.entrySet()) {
>              final LoggerConfig logger = entry.getValue();
>
> Added:
> logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilder.java
> URL:
> http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilder.java?rev=1586456&view=auto
>
> ==============================================================================
> ---
> logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilder.java
> (added)
> +++
> logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilder.java
> Thu Apr 10 20:06:14 2014
> @@ -0,0 +1,337 @@
> +/*
> + * 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.plugins;
> +
> +import java.lang.annotation.Annotation;
> +import java.lang.reflect.Array;
> +import java.lang.reflect.Method;
> +import java.lang.reflect.Modifier;
> +import java.util.ArrayList;
> +import java.util.Arrays;
> +import java.util.Collection;
> +import java.util.HashSet;
> +import java.util.List;
> +import java.util.Map;
> +import org.apache.logging.log4j.Logger;
> +import org.apache.logging.log4j.core.LogEvent;
> +import org.apache.logging.log4j.core.config.Configuration;
> +import org.apache.logging.log4j.core.config.Node;
> +import org.apache.logging.log4j.status.StatusLogger;
> +
> +/**
> + * Builder class to instantiate and configure a Plugin object using a
> PluginFactory method.
> + *
> + * @param <T> type of Plugin class.
> + */
> +public class PluginBuilder<T> {
> +
> +    private static final Logger LOGGER = StatusLogger.getLogger();
> +
> +    private final PluginType<T> pluginType;
> +    private final Class<T> clazz;
> +
> +    private Configuration configuration;
> +    private Node node;
> +    private LogEvent event;
> +
> +    private Method factory;
> +    private Annotation[][] annotations;
> +    private Class<?>[] types;
> +    private List<Node> children;
> +    private Collection<Node> used;
> +
> +    /**
> +     * Constructs a PluginBuilder for a given PluginType.
> +     *
> +     * @param pluginType type of plugin to configure
> +     */
> +    public PluginBuilder(final PluginType<T> pluginType) {
> +        this.pluginType = pluginType;
> +        this.clazz = pluginType.getPluginClass();
> +    }
> +
> +    /**
> +     * Specifies which annotation denotes a plugin factory method. The
> method must be static.
> +     *
> +     * @param annotationType class of annotation marking the plugin
> factory.
> +     * @param <A>            type of annotation.
> +     * @return {@code this}
> +     * @throws NoSuchMethodException
> +     */
> +    public <A extends Annotation> PluginBuilder<T>
> withFactoryMethodAnnotatedBy(final Class<A> annotationType)
> +            throws NoSuchMethodException {
> +        for (final Method method : clazz.getMethods()) {
> +            if (method.isAnnotationPresent(annotationType) &&
> Modifier.isStatic(method.getModifiers())) {
> +                factory = method;
> +                LOGGER.trace("Using factory method {} on class {}",
> method.getName(), clazz.getName());
> +                return this;
> +            }
> +        }
> +        throw new NoSuchMethodException("No method annotated with " +
> annotationType.getName() + "was found in " + clazz.getName());
> +    }
> +
> +    /**
> +     * Specifies the Configuration to use for constructing the plugin
> instance.
> +     *
> +     * @param configuration the configuration to use.
> +     * @return {@code this}
> +     */
> +    public PluginBuilder<T> withConfiguration(final Configuration
> configuration) {
> +        this.configuration = configuration;
> +        return this;
> +    }
> +
> +    /**
> +     * Specifies the Node corresponding to the plugin object that will be
> created.
> +     *
> +     * @param node the plugin configuration node to use.
> +     * @return {@code this}
> +     */
> +    public PluginBuilder<T> withConfigurationNode(final Node node) {
> +        this.node = node;
> +        this.children = this.node.getChildren();
> +        this.used = new HashSet<Node>(this.children.size());
> +        return this;
> +    }
> +
> +    /**
> +     * Specifies the LogEvent that may be used to provide extra context
> for string substitutions.
> +     *
> +     * @param event the event to use for extra information.
> +     * @return {@code this}
> +     */
> +    public PluginBuilder<T> forLogEvent(final LogEvent event) {
> +        this.event = event;
> +        return this;
> +    }
> +
> +    /**
> +     * Builds the plugin object.
> +     *
> +     * @return the plugin object or {@code null} if there was a problem
> creating it.
> +     */
> +    public Object build() {
> +        init();
> +        try {
> +            return factory.invoke(null, generateParameters());
> +        } catch (final Exception e) {
> +            LOGGER.error("Unable to invoke method {} in class {} for
> element {}",
> +                    factory.getName(), clazz.getName(), node.getName(),
> e);
> +            return null;
> +        }
> +    }
> +
> +    private void init() {
> +        if (factory == null) {
> +            throw new IllegalStateException("No factory method was
> found.");
> +        }
> +        if (configuration == null) {
> +            throw new IllegalStateException("No Configuration object was
> set.");
> +        }
> +        if (node == null) {
> +            throw new IllegalStateException("No Node object was set.");
> +        }
> +        annotations = factory.getParameterAnnotations();
> +        types = factory.getParameterTypes();
> +    }
> +
> +    private Object[] generateParameters() {
> +        final StringBuilder sb = new StringBuilder();
> +        final Object[] args = new Object[annotations.length];
> +        for (int i = 0; i < annotations.length; i++) {
> +            final String[] aliases = extractPluginAliases(annotations[i]);
> +            for (Annotation a : annotations[i]) {
> +                if (a instanceof PluginAliases) {
> +                    continue; // already processed
> +                }
> +                sb.append(sb.length() == 0 ? "with params(" : ", ");
> +                if (a instanceof PluginNode) {
> +                    args[i] = node;
> +                    sb.append("Node=").append(node.getName());
> +                } else if (a instanceof PluginConfiguration) {
> +                    args[i] = configuration;
> +                    sb.append("Configuration");
> +                    if (configuration.getName() != null) {
> +
>  sb.append('(').append(configuration.getName()).append(')');
> +                    }
> +                } else if (a instanceof PluginValue) {
> +                    final String name = ((PluginValue) a).value();
> +                    final String v = node.getValue() != null ?
> node.getValue() : getAttrValue("value");
> +                    final String value =
> configuration.getStrSubstitutor().replace(event, v);
> +                    args[i] = value;
> +
>  sb.append(name).append("=\"").append(value).append('"');
> +                } else if (a instanceof PluginAttribute) {
> +                    final PluginAttribute attribute = (PluginAttribute) a;
> +                    final String name = attribute.value();
> +                    final String value =
> configuration.getStrSubstitutor().replace(event, getAttrValue(name,
> aliases));
> +                    args[i] = value;
> +
>  sb.append(name).append("=\"").append(value).append('"');
> +                } else if (a instanceof PluginElement) {
> +                    final PluginElement element = (PluginElement) a;
> +                    final String name = element.value();
> +                    if (types[i].isArray()) {
> +                        final Class<?> type = types[i].getComponentType();
> +                        sb.append(name).append("={");
> +                        final List<Object> values = new
> ArrayList<Object>();
> +                        boolean first = true;
> +                        for (final Node child : children) {
> +                            final PluginType<?> childType =
> child.getType();
> +                            if
> (name.equalsIgnoreCase(childType.getElementName()) ||
> +
>  type.isAssignableFrom(childType.getPluginClass())) {
> +                                if (!first) {
> +                                    sb.append(", ");
> +                                }
> +                                first = false;
> +                                used.add(child);
> +                                final Object o = child.getObject();
> +                                if (o == null) {
> +                                    LOGGER.error("Null object returned
> for {} in {}", child.getName(), node.getName());
> +                                    continue;
> +                                }
> +                                if (o.getClass().isArray()) {
> +                                    sb.append(Arrays.toString((Object[])
> o));
> +                                    args[i] = o;
> +                                    break;
> +                                }
> +                                sb.append(child.toString());
> +                                values.add(o);
> +                            }
> +                        }
> +                        sb.append('}');
> +                        if (args[i] != null) {
> +                            break;
> +                        }
> +                        if (!(values.isEmpty() ||
> type.isAssignableFrom(values.get(0).getClass()))) {
> +                            LOGGER.error(
> +                                    "Attempted to assign List containing
> class {} to array of type {} for attribute {}",
> +                                    values.get(0).getClass().getName(),
> type, name
> +                            );
> +                            break;
> +                        }
> +                        args[i] = collectionToArray(values, type);
> +                    } else {
> +                        final Node namedNode = findNamedNode(name,
> types[i], children);
> +                        if (namedNode == null) {
> +                            sb.append("null");
> +                        } else {
> +
>  sb.append(namedNode.getName()).append('(').append(namedNode.toString()).append(')');
> +                            used.add(namedNode);
> +                            args[i] = namedNode.getObject();
> +                        }
> +                    }
> +                }
> +            }
> +        }
> +        if (sb.length() > 0) {
> +            sb.append(')');
> +        }
> +        checkForRemainingAttributes();
> +        verifyNodeChildrenUsed();
> +        LOGGER.debug("Calling {} on class {} for element {} {}",
> factory.getName(), clazz.getName(), node.getName(), sb.toString());
> +        return args;
> +    }
> +
> +    private static String[] extractPluginAliases(final Annotation...
> parmTypes) {
> +        String[] aliases = null;
> +        for (final Annotation a : parmTypes) {
> +            if (a instanceof PluginAliases) {
> +                aliases = ((PluginAliases) a).value();
> +            }
> +        }
> +        return aliases;
> +    }
> +
> +    private String getAttrValue(final String name, final String...
> aliases) {
> +        final Map<String, String> attrs = node.getAttributes();
> +        for (final Map.Entry<String, String> entry : attrs.entrySet()) {
> +            final String key = entry.getKey();
> +            if (key.equalsIgnoreCase(name)) {
> +                final String attr = entry.getValue();
> +                attrs.remove(key);
> +                return attr;
> +            }
> +            if (aliases != null) {
> +                for (String alias : aliases) {
> +                    if (key.equalsIgnoreCase(alias)) {
> +                        final String attr = entry.getValue();
> +                        attrs.remove(key);
> +                        return attr;
> +                    }
> +                }
> +            }
> +        }
> +        return null;
> +    }
> +
> +    private static Object[] collectionToArray(final Collection<?>
> collection, final Class<?> type) {
> +        final Object[] array = (Object[]) Array.newInstance(type,
> collection.size());
> +        int i = 0;
> +        for (final Object obj : collection) {
> +            array[i] = obj;
> +            ++i;
> +        }
> +        return array;
> +    }
> +
> +    private static Node findNamedNode(final String name, final Class<?>
> type, final Iterable<Node> nodes) {
> +        for (final Node child : nodes) {
> +            final PluginType<?> childType = child.getType();
> +            if (name.equalsIgnoreCase(childType.getElementName()) ||
> +                    type.isAssignableFrom(childType.getPluginClass())) {
> +                return child;
> +            }
> +        }
> +        return null;
> +    }
> +
> +    private void checkForRemainingAttributes() {
> +        final Map<String, String> attrs = node.getAttributes();
> +        if (!attrs.isEmpty()) {
> +            final StringBuilder eb = new StringBuilder();
> +            for (final String key : attrs.keySet()) {
> +                if (eb.length() == 0) {
> +                    eb.append(node.getName());
> +                    eb.append(" contains ");
> +                    if (attrs.size() == 1) {
> +                        eb.append("an invalid element or attribute ");
> +                    } else {
> +                        eb.append("invalid attributes ");
> +                    }
> +                } else {
> +                    eb.append(", ");
> +                }
> +                eb.append('"');
> +                eb.append(key);
> +                eb.append('"');
> +
> +            }
> +            LOGGER.error(eb.toString());
> +        }
> +    }
> +
> +    private void verifyNodeChildrenUsed() {
> +        if (!(pluginType.isDeferChildren() || used.size() ==
> children.size())) {
> +            children.removeAll(used);
> +            for (final Node child : children) {
> +                final String nodeType = node.getType().getElementName();
> +                final String start = nodeType.equals(node.getName()) ?
> node.getName() : nodeType + ' ' + node.getName();
> +                LOGGER.error("{} has no parameter that matches element
> {}", start, child.getName());
> +            }
> +        }
> +    }
> +}
>
> Propchange:
> logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilder.java
>
> ------------------------------------------------------------------------------
>     svn:eol-style = native
>
>
>