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