You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4j-dev@logging.apache.org by Ralph Goers <ra...@dslextreme.com> on 2014/04/12 09:01:39 UTC

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

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 <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>