This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel.git
commit daa273617c278361d95451bc78b24c746dca7b69 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Mon Aug 17 13:01:58 2020 +0200 CAMEL-15394: PropertyBindingSupport should use configurer to set array/list/map on root objects without reflection. WIP --- core/camel-main/pom.xml | 19 ++ .../java/org/apache/camel/main/MySecondBar.java | 34 +++ .../apache/camel/main/MySecondBarConfigurer.java | 45 +++ .../java/org/apache/camel/main/MySecondFoo.java | 35 +++ .../apache/camel/main/MySecondFooConfigurer.java | 45 +++ .../main/PropertyBindingSupportRootArrayTest.java | 64 ++++ ...yBindingSupportRootArrayWithConfigurerTest.java | 65 ++++ .../org/apache/camel/configurer/MySecondBar | 2 + .../org/apache/camel/configurer/MySecondFoo | 2 + ...jo.java => AbstractGenerateConfigurerMojo.java} | 65 ++-- .../maven/packaging/GenerateConfigurerMojo.java | 335 +-------------------- .../packaging/GenerateTestConfigurerMojo.java | 77 +++++ 12 files changed, 417 insertions(+), 371 deletions(-) diff --git a/core/camel-main/pom.xml b/core/camel-main/pom.xml index c8b8802..7edbe4f 100644 --- a/core/camel-main/pom.xml +++ b/core/camel-main/pom.xml @@ -140,6 +140,25 @@ </execution> </executions> </plugin> + <plugin> + <!-- we need to generate additional configurer classes --> + <groupId>org.apache.camel</groupId> + <artifactId>camel-package-maven-plugin</artifactId> + <executions> + <execution> + <id>generate-test-configurer</id> + <phase>process-test-classes</phase> + <goals> + <goal>generate-test-configurer</goal> + </goals> + <!-- only do these test classes --> + <configuration> + <discoverClasses>false</discoverClasses> + <classes>org.apache.camel.main.MySecondFoo,org.apache.camel.main.MySecondBar</classes> + </configuration> + </execution> + </executions> + </plugin> </plugins> </build> diff --git a/core/camel-main/src/test/java/org/apache/camel/main/MySecondBar.java b/core/camel-main/src/test/java/org/apache/camel/main/MySecondBar.java new file mode 100644 index 0000000..5110b07 --- /dev/null +++ b/core/camel-main/src/test/java/org/apache/camel/main/MySecondBar.java @@ -0,0 +1,34 @@ +/* + * 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.camel.main; + +import java.util.List; + +import org.apache.camel.spi.Configurer; + +@Configurer +public class MySecondBar { + private List<String> names; + + public List<String> getNames() { + return names; + } + + public void setNames(List<String> names) { + this.names = names; + } +} diff --git a/core/camel-main/src/test/java/org/apache/camel/main/MySecondBarConfigurer.java b/core/camel-main/src/test/java/org/apache/camel/main/MySecondBarConfigurer.java new file mode 100644 index 0000000..d31f486 --- /dev/null +++ b/core/camel-main/src/test/java/org/apache/camel/main/MySecondBarConfigurer.java @@ -0,0 +1,45 @@ +/* Generated by camel build tools - do NOT edit this file! */ +package org.apache.camel.main; + +import java.util.Map; + +import org.apache.camel.CamelContext; +import org.apache.camel.spi.GeneratedPropertyConfigurer; +import org.apache.camel.spi.PropertyConfigurerGetter; +import org.apache.camel.util.CaseInsensitiveMap; +import org.apache.camel.main.MySecondBar; + +/** + * Generated by camel build tools - do NOT edit this file! + */ +@SuppressWarnings("unchecked") +public class MySecondBarConfigurer extends org.apache.camel.support.component.PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter { + + @Override + public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) { + org.apache.camel.main.MySecondBar target = (org.apache.camel.main.MySecondBar) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "names": + case "Names": target.setNames(property(camelContext, java.util.List.class, value)); return true; + default: return false; + } + } + + @Override + public Map<String, Object> getAllOptions(Object target) { + Map<String, Object> answer = new CaseInsensitiveMap(); + answer.put("Names", java.util.List.class); + return answer; + } + + @Override + public Object getOptionValue(Object obj, String name, boolean ignoreCase) { + org.apache.camel.main.MySecondBar target = (org.apache.camel.main.MySecondBar) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "names": + case "Names": return target.getNames(); + default: return null; + } + } +} + diff --git a/core/camel-main/src/test/java/org/apache/camel/main/MySecondFoo.java b/core/camel-main/src/test/java/org/apache/camel/main/MySecondFoo.java new file mode 100644 index 0000000..f3442a7 --- /dev/null +++ b/core/camel-main/src/test/java/org/apache/camel/main/MySecondFoo.java @@ -0,0 +1,35 @@ +/* + * 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.camel.main; + +import java.util.List; + +import org.apache.camel.spi.Configurer; + +@Configurer +public class MySecondFoo { + + private List<MySecondBar> bars; + + public List<MySecondBar> getBars() { + return bars; + } + + public void setBars(List<MySecondBar> bars) { + this.bars = bars; + } +} diff --git a/core/camel-main/src/test/java/org/apache/camel/main/MySecondFooConfigurer.java b/core/camel-main/src/test/java/org/apache/camel/main/MySecondFooConfigurer.java new file mode 100644 index 0000000..5044c1f --- /dev/null +++ b/core/camel-main/src/test/java/org/apache/camel/main/MySecondFooConfigurer.java @@ -0,0 +1,45 @@ +/* Generated by camel build tools - do NOT edit this file! */ +package org.apache.camel.main; + +import java.util.Map; + +import org.apache.camel.CamelContext; +import org.apache.camel.spi.GeneratedPropertyConfigurer; +import org.apache.camel.spi.PropertyConfigurerGetter; +import org.apache.camel.util.CaseInsensitiveMap; +import org.apache.camel.main.MySecondFoo; + +/** + * Generated by camel build tools - do NOT edit this file! + */ +@SuppressWarnings("unchecked") +public class MySecondFooConfigurer extends org.apache.camel.support.component.PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter { + + @Override + public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) { + org.apache.camel.main.MySecondFoo target = (org.apache.camel.main.MySecondFoo) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "bars": + case "Bars": target.setBars(property(camelContext, java.util.List.class, value)); return true; + default: return false; + } + } + + @Override + public Map<String, Object> getAllOptions(Object target) { + Map<String, Object> answer = new CaseInsensitiveMap(); + answer.put("Bars", java.util.List.class); + return answer; + } + + @Override + public Object getOptionValue(Object obj, String name, boolean ignoreCase) { + org.apache.camel.main.MySecondFoo target = (org.apache.camel.main.MySecondFoo) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "bars": + case "Bars": return target.getBars(); + default: return null; + } + } +} + diff --git a/core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportRootArrayTest.java b/core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportRootArrayTest.java new file mode 100644 index 0000000..a26cb63 --- /dev/null +++ b/core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportRootArrayTest.java @@ -0,0 +1,64 @@ +/* + * 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.camel.main; + +import org.apache.camel.CamelContext; +import org.apache.camel.ExtendedCamelContext; +import org.apache.camel.impl.DefaultCamelContext; +import org.apache.camel.spi.BeanIntrospection; +import org.apache.camel.support.PropertyBindingSupport; +import org.junit.jupiter.api.Test; + + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Unit test for PropertyBindingSupport + */ +public class PropertyBindingSupportRootArrayTest { + + @Test + public void testRootArray() throws Exception { + CamelContext context = new DefaultCamelContext(); + + BeanIntrospection bi = context.adapt(ExtendedCamelContext.class).getBeanIntrospection(); + bi.setExtendedStatistics(true); + + context.start(); + + MySecondFoo target = new MySecondFoo(); + + PropertyBindingSupport.build() + .withCamelContext(context) + .withTarget(target) + .withProperty("bars[0]", "#class:" + MySecondBar.class.getName()) + .withProperty("bars[0].names[0]", "a") + .withProperty("bars[0].names[1]", "b") + .withRemoveParameters(false).bind(); + + assertEquals(1, target.getBars().size()); + assertEquals(2, target.getBars().get(0).getNames().size()); + assertEquals("a", target.getBars().get(0).getNames().get(0)); + assertEquals("b", target.getBars().get(0).getNames().get(1)); + + // no configurer so reflection is in use + assertEquals(3, bi.getInvokedCounter()); + + context.stop(); + } + +} diff --git a/core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportRootArrayWithConfigurerTest.java b/core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportRootArrayWithConfigurerTest.java new file mode 100644 index 0000000..2748e1a --- /dev/null +++ b/core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportRootArrayWithConfigurerTest.java @@ -0,0 +1,65 @@ +/* + * 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.camel.main; + +import org.apache.camel.CamelContext; +import org.apache.camel.ExtendedCamelContext; +import org.apache.camel.impl.DefaultCamelContext; +import org.apache.camel.spi.BeanIntrospection; +import org.apache.camel.support.PropertyBindingSupport; +import org.junit.jupiter.api.Test; + + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Unit test for PropertyBindingSupport + */ +public class PropertyBindingSupportRootArrayWithConfigurerTest { + + @Test + public void testRootArray() throws Exception { + CamelContext context = new DefaultCamelContext(); + + BeanIntrospection bi = context.adapt(ExtendedCamelContext.class).getBeanIntrospection(); + bi.setExtendedStatistics(true); + + context.start(); + + MySecondFoo target = new MySecondFoo(); + + PropertyBindingSupport.build() + .withCamelContext(context) + .withTarget(target) + .withProperty("bars[0]", "#class:" + MySecondBar.class.getName()) + .withProperty("bars[0].names[0]", "a") + .withProperty("bars[0].names[1]", "b") + .withConfigurer(new MySecondFooConfigurer()) + .withRemoveParameters(false).bind(); + + assertEquals(1, target.getBars().size()); + assertEquals(2, target.getBars().get(0).getNames().size()); + assertEquals("a", target.getBars().get(0).getNames().get(0)); + assertEquals("b", target.getBars().get(0).getNames().get(1)); + + // TODO: CAMEL-15394 + // assertEquals(0, bi.getInvokedCounter()); + + context.stop(); + } + +} diff --git a/core/camel-main/src/test/resources/META-INF/services/org/apache/camel/configurer/MySecondBar b/core/camel-main/src/test/resources/META-INF/services/org/apache/camel/configurer/MySecondBar new file mode 100644 index 0000000..e223b95 --- /dev/null +++ b/core/camel-main/src/test/resources/META-INF/services/org/apache/camel/configurer/MySecondBar @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.main.MySecondBarConfigurer diff --git a/core/camel-main/src/test/resources/META-INF/services/org/apache/camel/configurer/MySecondFoo b/core/camel-main/src/test/resources/META-INF/services/org/apache/camel/configurer/MySecondFoo new file mode 100644 index 0000000..8b49b25 --- /dev/null +++ b/core/camel-main/src/test/resources/META-INF/services/org/apache/camel/configurer/MySecondFoo @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.main.MySecondFooConfigurer diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/GenerateConfigurerMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/AbstractGenerateConfigurerMojo.java similarity index 87% copy from tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/GenerateConfigurerMojo.java copy to tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/AbstractGenerateConfigurerMojo.java index 1abb926..12fc5a5 100644 --- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/GenerateConfigurerMojo.java +++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/AbstractGenerateConfigurerMojo.java @@ -50,10 +50,7 @@ import org.apache.maven.model.Exclusion; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Component; -import org.apache.maven.plugins.annotations.LifecyclePhase; -import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.plugins.annotations.ResolutionScope; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationTarget; import org.jboss.jandex.AnnotationValue; @@ -62,44 +59,24 @@ import org.jboss.jandex.DotName; import org.jboss.jandex.Index; import org.jboss.jandex.IndexReader; + import static org.apache.camel.tooling.util.ReflectionHelper.doWithMethods; import static org.apache.camel.tooling.util.Strings.between; /** - * Generate configurer classes from @Configuer annotated classes. + * Abstract class for configurer generator. */ -@Mojo(name = "generate-configurer", threadSafe = true, defaultPhase = LifecyclePhase.PROCESS_CLASSES, - requiresDependencyCollection = ResolutionScope.COMPILE, - requiresDependencyResolution = ResolutionScope.COMPILE) -// must include runtime dependencies to generate configurer source -public class GenerateConfigurerMojo extends AbstractGeneratorMojo { +public abstract class AbstractGenerateConfigurerMojo extends AbstractGeneratorMojo { public static final DotName CONFIGURER = DotName.createSimple("org.apache.camel.spi.Configurer"); /** - * The output directory for generated java source code - */ - @Parameter(defaultValue = "${project.basedir}/src/generated/java") - protected File sourcesOutputDir; - - @Parameter(defaultValue = "${project.basedir}/src/generated/resources") - protected File resourcesOutputDir; - - /** * Whether to discover configurer classes from classpath by scanning for @Configurer annotations. * This requires using jandex-maven-plugin. */ @Parameter(defaultValue = "true") protected boolean discoverClasses = true; - /** - * To generate configurer for these classes. - * The syntax is either <tt>fqn</tt> or </tt>fqn=targetFqn</tt>. - * This allows to map source class to target class to generate the source code using a different classname. - */ - @Parameter - protected List<String> classes; - @Component private ArtifactFactory artifactFactory; @@ -127,11 +104,10 @@ public class GenerateConfigurerMojo extends AbstractGeneratorMojo { } } - public GenerateConfigurerMojo() { + public AbstractGenerateConfigurerMojo() { } - @Override - public void execute() throws MojoExecutionException, MojoFailureException { + protected void doExecute(File sourcesOutputDir, File resourcesOutputDir, List<String> classes, boolean testClasspathOnly) throws MojoExecutionException, MojoFailureException { if ("pom".equals(project.getPackaging())) { return; } @@ -145,7 +121,7 @@ public class GenerateConfigurerMojo extends AbstractGeneratorMojo { List<URL> urls = new ArrayList<>(); // need to include project compile dependencies (code similar to camel-maven-plugin) - addRelevantProjectDependenciesToClasspath(urls); + addRelevantProjectDependenciesToClasspath(urls, testClasspathOnly); projectClassLoader = DynamicClassLoader.createDynamicClassLoaderFromUrls(urls); Set<String> set = new LinkedHashSet<>(); @@ -179,7 +155,6 @@ public class GenerateConfigurerMojo extends AbstractGeneratorMojo { if (getLog().isDebugEnabled()) { getLog().debug("Generating configuers for the following classes: " + set); } - for (String fqn : set) { try { String targetFqn = fqn; @@ -189,8 +164,8 @@ public class GenerateConfigurerMojo extends AbstractGeneratorMojo { fqn = fqn.substring(0, pos); } List<Option> options = processClass(fqn); - generateConfigurer(fqn, targetFqn, options); - generateMetaInfConfigurer(targetFqn); + generateConfigurer(fqn, targetFqn, options, sourcesOutputDir); + generateMetaInfConfigurer(targetFqn, resourcesOutputDir); } catch (Exception e) { throw new MojoExecutionException("Error processing class: " + fqn, e); } @@ -201,16 +176,22 @@ public class GenerateConfigurerMojo extends AbstractGeneratorMojo { * Add any relevant project dependencies to the classpath. Takes * includeProjectDependencies into consideration. * - * @param path classpath of {@link java.net.URL} objects + * @param path classpath of {@link URL} objects * @throws MojoExecutionException */ - private void addRelevantProjectDependenciesToClasspath(List<URL> path) throws MojoExecutionException { + private void addRelevantProjectDependenciesToClasspath(List<URL> path, boolean testClasspathOnly) throws MojoExecutionException { try { getLog().debug("Project Dependencies will be included."); - URL mainClasses = new File(project.getBuild().getOutputDirectory()).toURI().toURL(); - getLog().debug("Adding to classpath : " + mainClasses); - path.add(mainClasses); + if (testClasspathOnly) { + URL testClasses = new File(project.getBuild().getTestOutputDirectory()).toURI().toURL(); + getLog().debug("Adding to classpath : " + testClasses); + path.add(testClasses); + } else { + URL mainClasses = new File(project.getBuild().getOutputDirectory()).toURI().toURL(); + getLog().debug("Adding to classpath : " + mainClasses); + path.add(mainClasses); + } Set<Artifact> dependencies = project.getArtifacts(); @@ -357,7 +338,7 @@ public class GenerateConfigurerMojo extends AbstractGeneratorMojo { return true; } - private void generateConfigurer(String fqn, String targetFqn, List<Option> options) throws IOException { + private void generateConfigurer(String fqn, String targetFqn, List<Option> options, File outputDir) throws IOException { int pos = targetFqn.lastIndexOf('.'); String pn = targetFqn.substring(0, pos); String cn = targetFqn.substring(pos + 1) + "Configurer"; @@ -372,14 +353,14 @@ public class GenerateConfigurerMojo extends AbstractGeneratorMojo { String source = sw.toString(); String fileName = pn.replace('.', '/') + "/" + cn + ".java"; - sourcesOutputDir.mkdirs(); - boolean updated = updateResource(sourcesOutputDir.toPath(), fileName, source); + outputDir.mkdirs(); + boolean updated = updateResource(outputDir.toPath(), fileName, source); if (updated) { getLog().info("Updated " + fileName); } } - private void generateMetaInfConfigurer(String name) { + private void generateMetaInfConfigurer(String name, File resourcesOutputDir) { int pos = name.lastIndexOf('.'); String pn = name.substring(0, pos); String en = name.substring(pos + 1); diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/GenerateConfigurerMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/GenerateConfigurerMojo.java index 1abb926..52e8be5 100644 --- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/GenerateConfigurerMojo.java +++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/GenerateConfigurerMojo.java @@ -17,64 +17,23 @@ package org.apache.camel.maven.packaging; import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.StringWriter; -import java.io.Writer; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; import java.util.List; -import java.util.Set; -import org.apache.camel.tooling.model.BaseOptionModel; -import org.apache.camel.tooling.util.ReflectionHelper; -import org.apache.maven.artifact.Artifact; -import org.apache.maven.artifact.factory.ArtifactFactory; -import org.apache.maven.artifact.resolver.filter.ArtifactFilter; -import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter; -import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; -import org.apache.maven.artifact.versioning.VersionRange; -import org.apache.maven.model.Dependency; -import org.apache.maven.model.Exclusion; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; -import org.jboss.jandex.AnnotationInstance; -import org.jboss.jandex.AnnotationTarget; -import org.jboss.jandex.AnnotationValue; -import org.jboss.jandex.ClassInfo; -import org.jboss.jandex.DotName; -import org.jboss.jandex.Index; -import org.jboss.jandex.IndexReader; - -import static org.apache.camel.tooling.util.ReflectionHelper.doWithMethods; -import static org.apache.camel.tooling.util.Strings.between; /** - * Generate configurer classes from @Configuer annotated classes. + * Generate configurer classes from @Configuer annotated classes for main sources. */ @Mojo(name = "generate-configurer", threadSafe = true, defaultPhase = LifecyclePhase.PROCESS_CLASSES, requiresDependencyCollection = ResolutionScope.COMPILE, requiresDependencyResolution = ResolutionScope.COMPILE) // must include runtime dependencies to generate configurer source -public class GenerateConfigurerMojo extends AbstractGeneratorMojo { - - public static final DotName CONFIGURER = DotName.createSimple("org.apache.camel.spi.Configurer"); +public class GenerateConfigurerMojo extends AbstractGenerateConfigurerMojo { /** * The output directory for generated java source code @@ -82,15 +41,11 @@ public class GenerateConfigurerMojo extends AbstractGeneratorMojo { @Parameter(defaultValue = "${project.basedir}/src/generated/java") protected File sourcesOutputDir; - @Parameter(defaultValue = "${project.basedir}/src/generated/resources") - protected File resourcesOutputDir; - /** - * Whether to discover configurer classes from classpath by scanning for @Configurer annotations. - * This requires using jandex-maven-plugin. + * The output directory for generated resource source code */ - @Parameter(defaultValue = "true") - protected boolean discoverClasses = true; + @Parameter(defaultValue = "${project.basedir}/src/generated/resources") + protected File resourcesOutputDir; /** * To generate configurer for these classes. @@ -100,33 +55,6 @@ public class GenerateConfigurerMojo extends AbstractGeneratorMojo { @Parameter protected List<String> classes; - @Component - private ArtifactFactory artifactFactory; - - private DynamicClassLoader projectClassLoader; - - private static class Option extends BaseOptionModel { - - public Option(String name, Class type, String getter) { - // we just use name, type - setName(name); - if (byte[].class == type) { - // special for byte array - setJavaType("byte[]"); - } else if (long[].class == type) { - // special for long array - setJavaType("long[]"); - } else if (type.isArray()) { - // special for array - String arrType = between(type.getName(), "[L", ";") + "[]"; - setJavaType(arrType); - } else { - setJavaType(type.getName()); - } - setGetterMethod(getter); - } - } - public GenerateConfigurerMojo() { } @@ -143,258 +71,7 @@ public class GenerateConfigurerMojo extends AbstractGeneratorMojo { resourcesOutputDir = new File(project.getBasedir(), "src/generated/resources"); } - List<URL> urls = new ArrayList<>(); - // need to include project compile dependencies (code similar to camel-maven-plugin) - addRelevantProjectDependenciesToClasspath(urls); - projectClassLoader = DynamicClassLoader.createDynamicClassLoaderFromUrls(urls); - - Set<String> set = new LinkedHashSet<>(); - - if (discoverClasses) { - Path output = Paths.get(project.getBuild().getOutputDirectory()); - Index index; - try (InputStream is = Files.newInputStream(output.resolve("META-INF/jandex.idx"))) { - index = new IndexReader(is).read(); - } catch (IOException e) { - throw new MojoExecutionException("IOException: " + e.getMessage(), e); - } - - // discover all classes annotated with @Configurer - List<AnnotationInstance> annotations = index.getAnnotations(CONFIGURER); - annotations.stream() - .filter(annotation -> annotation.target().kind() == AnnotationTarget.Kind.CLASS) - .filter(annotation -> annotation.target().asClass().nestingType() == ClassInfo.NestingType.TOP_LEVEL) - .filter(annotation -> asBooleanDefaultTrue(annotation, "generateConfigurer")) - .forEach(annotation -> { - String currentClass = annotation.target().asClass().name().toString(); - set.add(currentClass); - }); - } - - // additional classes - if (classes != null && !classes.isEmpty()) { - set.addAll(classes); - } - - if (getLog().isDebugEnabled()) { - getLog().debug("Generating configuers for the following classes: " + set); - } - - for (String fqn : set) { - try { - String targetFqn = fqn; - int pos = fqn.indexOf('='); - if (pos != -1) { - targetFqn = fqn.substring(pos + 1); - fqn = fqn.substring(0, pos); - } - List<Option> options = processClass(fqn); - generateConfigurer(fqn, targetFqn, options); - generateMetaInfConfigurer(targetFqn); - } catch (Exception e) { - throw new MojoExecutionException("Error processing class: " + fqn, e); - } - } - } - - /** - * Add any relevant project dependencies to the classpath. Takes - * includeProjectDependencies into consideration. - * - * @param path classpath of {@link java.net.URL} objects - * @throws MojoExecutionException - */ - private void addRelevantProjectDependenciesToClasspath(List<URL> path) throws MojoExecutionException { - try { - getLog().debug("Project Dependencies will be included."); - - URL mainClasses = new File(project.getBuild().getOutputDirectory()).toURI().toURL(); - getLog().debug("Adding to classpath : " + mainClasses); - path.add(mainClasses); - - Set<Artifact> dependencies = project.getArtifacts(); - - // system scope dependencies are not returned by maven 2.0. See - // MEXEC-17 - dependencies.addAll(getAllNonTestScopedDependencies()); - - Iterator<Artifact> iter = dependencies.iterator(); - while (iter.hasNext()) { - Artifact classPathElement = iter.next(); - getLog().debug("Adding project dependency artifact: " + classPathElement.getArtifactId() - + " to classpath"); - File file = classPathElement.getFile(); - if (file != null) { - path.add(file.toURI().toURL()); - } - } - - } catch (MalformedURLException e) { - throw new MojoExecutionException("Error during setting up classpath", e); - } - } - - private Collection<Artifact> getAllNonTestScopedDependencies() throws MojoExecutionException { - List<Artifact> answer = new ArrayList<>(); - - for (Artifact artifact : getAllDependencies()) { - - // do not add test artifacts - if (!artifact.getScope().equals(Artifact.SCOPE_TEST)) { - answer.add(artifact); - } - } - return answer; - } - - // generic method to retrieve all the transitive dependencies - private Collection<Artifact> getAllDependencies() throws MojoExecutionException { - List<Artifact> artifacts = new ArrayList<>(); - - for (Iterator<?> dependencies = project.getDependencies().iterator(); dependencies.hasNext();) { - Dependency dependency = (Dependency)dependencies.next(); - - String groupId = dependency.getGroupId(); - String artifactId = dependency.getArtifactId(); - - VersionRange versionRange; - try { - versionRange = VersionRange.createFromVersionSpec(dependency.getVersion()); - } catch (InvalidVersionSpecificationException e) { - throw new MojoExecutionException("unable to parse version", e); - } - - String type = dependency.getType(); - if (type == null) { - type = "jar"; - } - String classifier = dependency.getClassifier(); - boolean optional = dependency.isOptional(); - String scope = dependency.getScope(); - if (scope == null) { - scope = Artifact.SCOPE_COMPILE; - } - - if (this.artifactFactory != null) { - Artifact art = this.artifactFactory.createDependencyArtifact(groupId, artifactId, versionRange, - type, classifier, scope, null, optional); - - if (scope.equalsIgnoreCase(Artifact.SCOPE_SYSTEM)) { - art.setFile(new File(dependency.getSystemPath())); - } - - List<String> exclusions = new ArrayList<>(); - for (Exclusion exclusion : dependency.getExclusions()) { - exclusions.add(exclusion.getGroupId() + ":" + exclusion.getArtifactId()); - } - - ArtifactFilter newFilter = new ExcludesArtifactFilter(exclusions); - - art.setDependencyFilter(newFilter); - - artifacts.add(art); - } - } - - return artifacts; - } - - private List<Option> processClass(String fqn) throws ClassNotFoundException { - List<Option> answer = new ArrayList<>(); - // filter out duplicates by using a names set that has already added - Set<String> names = new HashSet<>(); - - Class clazz = projectClassLoader.loadClass(fqn); - // find all public setters - doWithMethods(clazz, m -> { - boolean setter = m.getName().length() >= 4 && m.getName().startsWith("set") && Character.isUpperCase(m.getName().charAt(3)); - setter &= Modifier.isPublic(m.getModifiers()) && m.getParameterCount() == 1; - setter &= filterSetter(m); - if (setter) { - String getter = "get" + Character.toUpperCase(m.getName().charAt(3)) + m.getName().substring(4); - Class type = m.getParameterTypes()[0]; - if (boolean.class == type || Boolean.class == type) { - try { - String isGetter = "is" + getter.substring(3); - clazz.getMethod(isGetter, null); - getter = isGetter; - } catch (Exception e) { - // ignore as its then assumed to be get - } - } - String t = Character.toUpperCase(m.getName().charAt(3)) + m.getName().substring(3 + 1); - if (names.add(t)) { - answer.add(new Option(t, type, getter)); - } else { - boolean replace = false; - // try to find out what the real type is of the correspondent field so we chose among the clash - Field field = ReflectionHelper.findField(clazz, Character.toLowerCase(t.charAt(0)) + t.substring(1)); - if (field != null && field.getType().equals(type)) { - // this is the correct type for the new option - replace = true; - } - if (replace) { - answer.removeIf(o -> o.getName().equals(t)); - answer.add(new Option(t, type, getter)); - } - } - } - }); - - return answer; - } - - private boolean filterSetter(Method setter) { - // special for some - if ("setBindingMode".equals(setter.getName())) { - // we only want the string setter - return setter.getParameterTypes()[0] == String.class; - } else if ("setHostNameResolver".equals(setter.getName())) { - // we only want the string setter - return setter.getParameterTypes()[0] == String.class; - } - - return true; - } - - private void generateConfigurer(String fqn, String targetFqn, List<Option> options) throws IOException { - int pos = targetFqn.lastIndexOf('.'); - String pn = targetFqn.substring(0, pos); - String cn = targetFqn.substring(pos + 1) + "Configurer"; - String en = fqn; - String pfqn = fqn; - String psn = "org.apache.camel.support.component.PropertyConfigurerSupport"; - - StringWriter sw = new StringWriter(); - PropertyConfigurerGenerator.generatePropertyConfigurer(pn, cn, en, pfqn, psn, - false, false, options, sw); - - String source = sw.toString(); - - String fileName = pn.replace('.', '/') + "/" + cn + ".java"; - sourcesOutputDir.mkdirs(); - boolean updated = updateResource(sourcesOutputDir.toPath(), fileName, source); - if (updated) { - getLog().info("Updated " + fileName); - } - } - - private void generateMetaInfConfigurer(String name) { - int pos = name.lastIndexOf('.'); - String pn = name.substring(0, pos); - String en = name.substring(pos + 1); - try (Writer w = new StringWriter()) { - w.append("# " + GENERATED_MSG + "\n"); - w.append("class=").append(pn).append(".").append(en).append("Configurer").append("\n"); - updateResource(resourcesOutputDir.toPath(), "META-INF/services/org/apache/camel/configurer/" + en, w.toString()); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private static boolean asBooleanDefaultTrue(AnnotationInstance ai, String name) { - AnnotationValue av = ai.value(name); - return av == null || av.asBoolean(); + doExecute(sourcesOutputDir, resourcesOutputDir, classes, false); } } diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/GenerateTestConfigurerMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/GenerateTestConfigurerMojo.java new file mode 100644 index 0000000..f423189 --- /dev/null +++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/GenerateTestConfigurerMojo.java @@ -0,0 +1,77 @@ +/* + * 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.camel.maven.packaging; + +import java.io.File; +import java.util.List; + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; + +/** + * Generate configurer classes from @Configuer annotated classes for test sources. + */ +@Mojo(name = "generate-test-configurer", threadSafe = true, defaultPhase = LifecyclePhase.PROCESS_CLASSES, + requiresDependencyCollection = ResolutionScope.COMPILE, + requiresDependencyResolution = ResolutionScope.COMPILE) +// must include runtime dependencies to generate configurer source +public class GenerateTestConfigurerMojo extends AbstractGenerateConfigurerMojo { + + /** + * The output directory for generated test java source code + */ + @Parameter(defaultValue = "${project.basedir}/src/test/java") + protected File sourcesOutputDir; + + /** + * The output directory for generated test resource source code + */ + @Parameter(defaultValue = "${project.basedir}/src/test/resources") + protected File resourcesOutputDir; + + /** + * To generate configurer for these test classes. + * The syntax is either <tt>fqn</tt> or </tt>fqn=targetFqn</tt>. + * This allows to map source class to target class to generate the source code using a different classname. + */ + @Parameter + protected List<String> classes; + + public GenerateTestConfigurerMojo() { + } + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + if ("pom".equals(project.getPackaging())) { + return; + } + + if (sourcesOutputDir == null) { + sourcesOutputDir = new File(project.getBasedir(), "src/test/java"); + } + if (resourcesOutputDir == null) { + resourcesOutputDir = new File(project.getBasedir(), "src/test/resources"); + } + + doExecute(sourcesOutputDir, resourcesOutputDir, classes, true); + } + +}