Author: mattsicker
Date: Mon May 26 22:07:46 2014
New Revision: 1597653

URL: http://svn.apache.org/r1597653
Log:
Update PluginBuilder to work with builder classes as well as factory 
methods.

  - Removed some unnecessary class fields and prefer passing through 
  method parameters.
  - Removed the strange withFactoryMethodAnnotatedBy method.
  - Renamed init() to verify() to better reflect updated usage.
  - Try to use builder first if one can be found. Fall back to 
  factory method otherwise (as usual).
  - Migrated logic from withFactoryMethodAnnotatedBy to 
  findFactoryMethod with hard-coded reference to PluginFactory.
  - Added createBuilder and injectFields methods for builder-style 
  plugin creation. (The Builder<T> interface turned out to be handy 
  here).
  - Renamed helper local variable to visitor (refactoring didn't 
  catch that when I renamed PluginHelper to PluginVisitor before 
  committing).

Modified:
    
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
    
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginBuilder.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=1597653&r1=1597652&r2=1597653&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
 Mon May 26 22:07:46 2014
@@ -38,7 +38,6 @@ 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.PluginFactory;
 import org.apache.logging.log4j.core.config.plugins.util.PluginBuilder;
 import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
 import org.apache.logging.log4j.core.config.plugins.util.PluginType;
@@ -685,7 +684,6 @@ public abstract class AbstractConfigurat
     */
     private <T> Object createPluginObject(final PluginType<T> type, final Node 
node, final LogEvent event)
     {
-        // TODO: add support for type conversion
         final Class<T> clazz = type.getPluginClass();
 
         if (Map.class.isAssignableFrom(clazz)) {
@@ -704,17 +702,11 @@ public abstract class AbstractConfigurat
             }
         }
 
-        try {
-            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 new PluginBuilder<T>(type)
+                .withConfiguration(this)
+                .withConfigurationNode(node)
+                .forLogEvent(event)
+                .build();
     }
 
     private static <T> Object createPluginMap(final Node node, final Class<T> 
clazz) throws InstantiationException, IllegalAccessException {

Modified: 
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginBuilder.java
URL: 
http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginBuilder.java?rev=1597653&r1=1597652&r2=1597653&view=diff
==============================================================================
--- 
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginBuilder.java
 (original)
+++ 
logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginBuilder.java
 Mon May 26 22:07:46 2014
@@ -18,16 +18,21 @@
 package org.apache.logging.log4j.core.config.plugins.util;
 
 import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.logging.log4j.Level;
 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.core.config.plugins.PluginAliases;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
 import org.apache.logging.log4j.core.config.plugins.visitors.PluginVisitor;
 import org.apache.logging.log4j.core.config.plugins.visitors.PluginVisitors;
 import org.apache.logging.log4j.core.util.Assert;
@@ -35,14 +40,13 @@ import org.apache.logging.log4j.core.uti
 import org.apache.logging.log4j.status.StatusLogger;
 
 /**
- * Builder class to instantiate and configure a Plugin object using a 
PluginFactory method.
+ * Builder class to instantiate and configure a Plugin object using a 
PluginFactory method or PluginBuilderFactory
+ * builder class.
  *
  * @param <T> type of Plugin class.
  */
 public class PluginBuilder<T> implements Builder<T> {
 
-    // TODO: field injection for builder factories annotated with 
@PluginBuilderFactory
-
     private static final Logger LOGGER = StatusLogger.getLogger();
 
     private final PluginType<T> pluginType;
@@ -52,10 +56,6 @@ public class PluginBuilder<T> implements
     private Node node;
     private LogEvent event;
 
-    private Method factory;
-    private Annotation[][] annotations;
-    private Class<?>[] types;
-
     /**
      * Constructs a PluginBuilder for a given PluginType.
      *
@@ -67,26 +67,6 @@ public class PluginBuilder<T> implements
     }
 
     /**
-     * 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.
@@ -126,27 +106,91 @@ public class PluginBuilder<T> implements
      */
     @Override
     public T build() {
-        init();
+        verify();
+        // first try to use a builder class if one is available
+        try {
+            final Builder<T> builder = createBuilder(this.clazz);
+            if (builder != null) {
+                injectFields(builder);
+                return builder.build();
+            }
+        } catch (final Exception e) {
+            LOGGER.catching(Level.DEBUG, e);
+            LOGGER.error("Unable to inject fields into builder class for 
plugin type {}, element {}.", this.clazz,
+                node.getName());
+        }
+        // or fall back to factory method if no builder class is available
         try {
+            final Method factory = findFactoryMethod(this.clazz);
+            final Object[] params = 
generateParameters(factory.getParameterTypes(), 
factory.getParameterAnnotations());
             @SuppressWarnings("unchecked")
-            final T plugin = (T) factory.invoke(null, generateParameters());
+            final T plugin = (T) factory.invoke(null, params);
             return plugin;
         } catch (final Exception e) {
-            LOGGER.error("Unable to invoke method {} in class {} for element 
{}",
-                    factory.getName(), clazz.getName(), node.getName(), e);
+            LOGGER.catching(Level.DEBUG, e);
+            LOGGER.error("Unable to invoke factory method in class {} for 
element {}.", this.clazz, this.node.getName());
             return null;
         }
     }
 
-    private void init() {
-        Assert.requireNonNull(factory, "No factory method was found.");
-        Assert.requireNonNull(configuration, "No Configuration object was 
set.");
-        Assert.requireNonNull(node, "No Node object was set.");
-        annotations = factory.getParameterAnnotations();
-        types = factory.getParameterTypes();
+    private void verify() {
+        Assert.requireNonNull(this.configuration, "No Configuration object was 
set.");
+        Assert.requireNonNull(this.node, "No Node object was set.");
+    }
+
+    private static <T> Builder<T> createBuilder(final Class<T> clazz)
+        throws InvocationTargetException, IllegalAccessException {
+        for (final Method method : clazz.getDeclaredMethods()) {
+            if (method.isAnnotationPresent(PluginBuilderFactory.class) &&
+                Modifier.isStatic(method.getModifiers())) {
+                @SuppressWarnings("unchecked")
+                final Builder<T> builder = (Builder<T>) method.invoke(null);
+                LOGGER.debug("Found builder factory method {}.{}.", clazz, 
method);
+                return builder;
+            }
+        }
+        LOGGER.debug("No compatible method annotated with {} found in class 
{}.", PluginBuilderFactory.class, clazz);
+        return null;
+    }
+
+    private void injectFields(final Builder<T> builder) throws 
IllegalAccessException {
+        final Field[] fields = builder.getClass().getDeclaredFields();
+        for (final Field field : fields) {
+            field.setAccessible(true);
+            final Annotation[] annotations = field.getDeclaredAnnotations();
+            final String[] aliases = extractPluginAliases(annotations);
+            for (final Annotation a : annotations) {
+                if (a instanceof PluginAliases) {
+                    continue; // already processed
+                }
+                final PluginVisitor<? extends Annotation> visitor = 
PluginVisitors.findVisitor(a.annotationType());
+                if (visitor != null) {
+                    final Object value = visitor.setAliases(aliases)
+                        .setAnnotation(a)
+                        .setConversionType(field.getType())
+                        .setStrSubstitutor(configuration.getStrSubstitutor())
+                        .visit(configuration, node, event);
+                    field.set(builder, value);
+                }
+            }
+        }
+        checkForRemainingAttributes();
+        verifyNodeChildrenUsed();
+    }
+
+    private static <T> Method findFactoryMethod(final Class<T> clazz) {
+        for (final Method method : clazz.getDeclaredMethods()) {
+            if (method.isAnnotationPresent(PluginFactory.class) &&
+                Modifier.isStatic(method.getModifiers())) {
+                LOGGER.debug("Found factory method {}.{}.", clazz, method);
+                return method;
+            }
+        }
+        LOGGER.debug("No compatible method annotated with {} found in class 
{}.", PluginFactory.class, clazz);
+        return null;
     }
 
-    private Object[] generateParameters() {
+    private Object[] generateParameters(final Class<?>[] types, final 
Annotation[][] annotations) {
         final Object[] args = new Object[annotations.length];
         for (int i = 0; i < annotations.length; i++) {
             final String[] aliases = extractPluginAliases(annotations[i]);
@@ -155,9 +199,9 @@ public class PluginBuilder<T> implements
                 if (a instanceof PluginAliases) {
                     continue; // already processed
                 }
-                final PluginVisitor<? extends Annotation> helper = 
PluginVisitors.findVisitor(a.annotationType());
-                if (helper != null) {
-                    args[i] = helper.setAliases(aliases)
+                final PluginVisitor<? extends Annotation> visitor = 
PluginVisitors.findVisitor(a.annotationType());
+                if (visitor != null) {
+                    args[i] = visitor.setAliases(aliases)
                         .setAnnotation(a)
                         .setConversionType(types[i])
                         .setStrSubstitutor(configuration.getStrSubstitutor())


Reply via email to