[LOG4J2-1508] Allow a Builder to sublcass another Builder.

Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/e3e37ccb
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/e3e37ccb
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/e3e37ccb

Branch: 
refs/heads/LOG4J2-1010&LOG4J2-1447-injectable-contextdata&better-datastructure
Commit: e3e37ccbd6ff8237cc693d32a1b687589b11d059
Parents: 685881a
Author: Gary Gregory <ggreg...@apache.org>
Authored: Wed Aug 10 13:50:13 2016 -0700
Committer: Gary Gregory <ggreg...@apache.org>
Committed: Wed Aug 10 13:50:13 2016 -0700

----------------------------------------------------------------------
 .../core/config/plugins/util/PluginBuilder.java |  6 +-
 .../logging/log4j/core/util/TypeUtil.java       | 21 ++++++
 .../AbstractPluginWithGenericBuilder.java       | 59 +++++++++++++++++
 .../PluginWithGenericSubclassFoo1Builder.java   | 55 ++++++++++++++++
 .../ValidatingPluginWithGenericBuilder.java     |  2 +-
 ...luginWithGenericSubclassFoo1BuilderTest.java | 68 ++++++++++++++++++++
 src/changes/changes.xml                         |  6 ++
 7 files changed, 214 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3e37ccb/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginBuilder.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginBuilder.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginBuilder.java
index 7ba9981..8da7356 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginBuilder.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginBuilder.java
@@ -23,6 +23,8 @@ import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 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.List;
 import java.util.Map;
@@ -156,8 +158,8 @@ public class PluginBuilder implements Builder<Object> {
     }
 
     private void injectFields(final Builder<?> builder) throws 
IllegalAccessException {
-        final Field[] fields = builder.getClass().getDeclaredFields();
-        AccessibleObject.setAccessible(fields, true);
+        final List<Field> fields = 
TypeUtil.getAllDeclaredFields(builder.getClass());
+        AccessibleObject.setAccessible(fields.toArray(new Field[] {}), true);
         final StringBuilder log = new StringBuilder();
         boolean invalid = false;
         for (final Field field : fields) {

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3e37ccb/log4j-core/src/main/java/org/apache/logging/log4j/core/util/TypeUtil.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/TypeUtil.java 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/TypeUtil.java
index 81a8de2..e8db1ed 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/TypeUtil.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/TypeUtil.java
@@ -16,10 +16,13 @@
  */
 package org.apache.logging.log4j.core.util;
 
+import java.lang.reflect.Field;
 import java.lang.reflect.GenericArrayType;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.lang.reflect.WildcardType;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -36,10 +39,28 @@ import java.util.Objects;
  * @since 2.1
  */
 public final class TypeUtil {
+    
     private TypeUtil() {
     }
 
     /**
+     * Gets all declared fields for the given class (including superclasses).
+     * 
+     * @param cls the class to examine
+     * @return all declared fields for the given class (including 
superclasses).
+     * @see Class#getDeclaredFields()
+     */
+    public static List<Field> getAllDeclaredFields(Class<?> cls) {
+        final List<Field> fields = new ArrayList<>();
+        while (cls != null) {
+            for (Field field : cls.getDeclaredFields()) {
+                fields.add(field);
+            }
+            cls = cls.getSuperclass();
+        }
+        return fields;
+    }
+    /**
      * Indicates if two {@link Type}s are assignment compatible.
      *
      * @param lhs the left hand side to check assignability to

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3e37ccb/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/AbstractPluginWithGenericBuilder.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/AbstractPluginWithGenericBuilder.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/AbstractPluginWithGenericBuilder.java
new file mode 100644
index 0000000..7b2a0f8
--- /dev/null
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/AbstractPluginWithGenericBuilder.java
@@ -0,0 +1,59 @@
+/*
+ * 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.validation;
+
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
+import 
org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
+
+/**
+ *
+ */
+public class AbstractPluginWithGenericBuilder {
+
+    public static abstract class Builder<B extends Builder<B>> {
+
+        @PluginBuilderAttribute
+        @Required(message = "The thing given by the builder is null")
+        private String thing;
+
+        @SuppressWarnings("unchecked")
+        public B asBuilder() {
+            return (B) this;
+        }
+
+        public String getThing() {
+            return thing;
+        }
+
+        public B withThing(final String name) {
+            this.thing = name;
+            return asBuilder();
+        }
+
+    }
+
+    private final String thing;
+
+    public AbstractPluginWithGenericBuilder(final String thing) {
+        super();
+        this.thing = thing;
+    }
+
+    public String getThing() {
+        return thing;
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3e37ccb/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/PluginWithGenericSubclassFoo1Builder.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/PluginWithGenericSubclassFoo1Builder.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/PluginWithGenericSubclassFoo1Builder.java
new file mode 100644
index 0000000..727b848
--- /dev/null
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/PluginWithGenericSubclassFoo1Builder.java
@@ -0,0 +1,55 @@
+package org.apache.logging.log4j.core.config.plugins.validation;
+
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
+import 
org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
+
+@Plugin(name = "PluginWithGenericSubclassFoo1Builder", category = "Test")
+public class PluginWithGenericSubclassFoo1Builder extends 
AbstractPluginWithGenericBuilder {
+
+    public static class Builder<B extends Builder<B>> extends 
AbstractPluginWithGenericBuilder.Builder<B>
+            implements 
org.apache.logging.log4j.core.util.Builder<PluginWithGenericSubclassFoo1Builder>
 {
+
+        @PluginBuilderFactory
+        public static <B extends Builder<B>> B newBuilder() {
+            return new Builder<B>().asBuilder();
+        }
+
+        @PluginBuilderAttribute
+        @Required(message = "The foo1 given by the builder is null")
+        private String foo1;
+
+        @Override
+        public PluginWithGenericSubclassFoo1Builder build() {
+            return new PluginWithGenericSubclassFoo1Builder(getThing(), 
getFoo1());
+        }
+
+        public String getFoo1() {
+            return foo1;
+        }
+
+        public B withFoo1(final String foo1) {
+            this.foo1 = foo1;
+            return asBuilder();
+        }
+
+    }
+    
+    @PluginBuilderFactory
+    public static <B extends Builder<B>> B newBuilder() {
+        return new Builder<B>().asBuilder();
+    }
+
+    private final String foo1;
+
+    public PluginWithGenericSubclassFoo1Builder(final String thing, final 
String foo1) {
+        super(thing);
+        this.foo1 = foo1;
+    }
+
+    public String getFoo1() {
+        return foo1;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3e37ccb/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/ValidatingPluginWithGenericBuilder.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/ValidatingPluginWithGenericBuilder.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/ValidatingPluginWithGenericBuilder.java
index 34297e2..5ca9b6e 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/ValidatingPluginWithGenericBuilder.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/ValidatingPluginWithGenericBuilder.java
@@ -63,7 +63,7 @@ public class ValidatingPluginWithGenericBuilder {
         }
 
         @SuppressWarnings("unchecked")
-        private B asBuilder() {
+        public B asBuilder() {
             return (B) this;
         }
 

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3e37ccb/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/validators/ValidatingPluginWithGenericSubclassFoo1BuilderTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/validators/ValidatingPluginWithGenericSubclassFoo1BuilderTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/validators/ValidatingPluginWithGenericSubclassFoo1BuilderTest.java
new file mode 100644
index 0000000..d6f3966
--- /dev/null
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/plugins/validation/validators/ValidatingPluginWithGenericSubclassFoo1BuilderTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.validation.validators;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import org.apache.logging.log4j.core.config.Node;
+import org.apache.logging.log4j.core.config.NullConfiguration;
+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;
+import 
org.apache.logging.log4j.core.config.plugins.validation.PluginWithGenericSubclassFoo1Builder;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ValidatingPluginWithGenericSubclassFoo1BuilderTest {
+
+    private PluginType<PluginWithGenericSubclassFoo1Builder> plugin;
+    private Node node;
+
+    @SuppressWarnings("unchecked")
+    @Before
+    public void setUp() throws Exception {
+        final PluginManager manager = new PluginManager("Test");
+        manager.collectPlugins();
+        plugin = (PluginType<PluginWithGenericSubclassFoo1Builder>) 
manager.getPluginType("PluginWithGenericSubclassFoo1Builder");
+        assertNotNull("Rebuild this module to make sure annotaion processing 
kicks in.", plugin);
+        node = new Node(null, "Validator", plugin);
+    }
+
+    @Test
+    public void testNullDefaultValue() throws Exception {
+        final PluginWithGenericSubclassFoo1Builder validatingPlugin = 
(PluginWithGenericSubclassFoo1Builder) new PluginBuilder(plugin)
+            .withConfiguration(new NullConfiguration())
+            .withConfigurationNode(node)
+            .build();
+        assertNull(validatingPlugin);
+    }
+
+    @Test
+    public void testNonNullValue() throws Exception {
+        node.getAttributes().put("thing", "thing1");
+        node.getAttributes().put("foo1", "foo1");
+        final PluginWithGenericSubclassFoo1Builder validatingPlugin = 
(PluginWithGenericSubclassFoo1Builder) new PluginBuilder(plugin)
+            .withConfiguration(new NullConfiguration())
+            .withConfigurationNode(node)
+            .build();
+        assertNotNull(validatingPlugin);
+        assertEquals("thing1", validatingPlugin.getThing());
+        assertEquals("foo1", validatingPlugin.getFoo1());
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e3e37ccb/src/changes/changes.xml
----------------------------------------------------------------------
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index ad03663..a4fac9e 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -75,6 +75,12 @@
       <action issue="LOG4J2-1505" dev="ggregory" type="add" due-to="Gary 
Gregory">
         Create a Builder for FileAppender.
       </action>
+      <action issue="LOG4J2-1507" dev="ggregory" type="add" due-to="Gary 
Gregory">
+        Allow Builders to be completely generic.
+      </action>
+      <action issue="LOG4J2-1508" dev="ggregory" type="add" due-to="Gary 
Gregory">
+        Allow a Builder to sublcass another Builder.
+      </action>
       <action issue="LOG4J2-1458" dev="ggregory" type="update" due-to="Gary 
Gregory">
         Update Jackson from 2.7.5 to 2.8.0.
       </action>

Reply via email to