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>