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