You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2020/09/25 18:44:59 UTC
[camel] 02/16: CAMEL-15567: components - Generate source code for
creating endpoint uri via a map of properties. WIP
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch uri-assembler
in repository https://gitbox.apache.org/repos/asf/camel.git
commit 167118391f62f098edba9de1ba46c81c76e2aad4
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Sep 24 20:08:11 2020 +0200
CAMEL-15567: components - Generate source code for creating endpoint uri via a map of properties. WIP
---
.../component/EndpointUriAssemblerSupport.java | 94 +++++++++
.../packaging/EndpointUriAssemblerGenerator.java | 69 ++++++
.../maven/packaging/GenerateComponentMojo.java | 2 +
.../apache/camel/maven/packaging/GenerateMojo.java | 2 +
.../GenerateUriEndpointAssemblerMojo.java | 232 +++++++++++++++++++++
5 files changed, 399 insertions(+)
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/component/EndpointUriAssemblerSupport.java b/core/camel-support/src/main/java/org/apache/camel/support/component/EndpointUriAssemblerSupport.java
new file mode 100644
index 0000000..95ea8c1
--- /dev/null
+++ b/core/camel-support/src/main/java/org/apache/camel/support/component/EndpointUriAssemblerSupport.java
@@ -0,0 +1,94 @@
+/*
+ * 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.support.component;
+
+import java.util.List;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.NoSuchBeanException;
+import org.apache.camel.support.EndpointHelper;
+import org.apache.camel.util.TimeUtils;
+
+/**
+ * Base class used by Camel Package Maven Plugin when it generates source code for fast endpoint uri assembler via
+ * {@link org.apache.camel.spi.EndpointUriAssembler}.
+ */
+public abstract class EndpointUriAssemblerSupport {
+
+ /**
+ * Converts the property to the expected type
+ *
+ * @param camelContext the camel context
+ * @param type the expected type
+ * @param value the value
+ * @return the value converted to the expected type
+ */
+ public static <T> T property(CamelContext camelContext, Class<T> type, Object value) {
+ // if the type is not string based and the value is a bean reference, then we need to lookup
+ // the bean from the registry
+ if (value instanceof String && String.class != type) {
+ String text = value.toString();
+
+ if (EndpointHelper.isReferenceParameter(text)) {
+ Object obj;
+ // special for a list where we refer to beans which can be either a list or a single element
+ // so use Object.class as type
+ if (type == List.class) {
+ obj = EndpointHelper.resolveReferenceListParameter(camelContext, text, Object.class);
+ } else {
+ obj = EndpointHelper.resolveReferenceParameter(camelContext, text, type);
+ }
+ if (obj == null) {
+ // no bean found so throw an exception
+ throw new NoSuchBeanException(text, type.getName());
+ }
+ value = obj;
+ } else if (type == long.class || type == Long.class || type == int.class || type == Integer.class) {
+ Object obj = null;
+ // string to long/int then it may be a duration where we can convert the value to milli seconds
+ // it may be a time pattern, such as 5s for 5 seconds = 5000
+ try {
+ long num = TimeUtils.toMilliSeconds(text);
+ if (type == int.class || type == Integer.class) {
+ // need to cast to int
+ obj = (int) num;
+ } else {
+ obj = num;
+ }
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ if (obj != null) {
+ value = obj;
+ }
+ }
+ }
+
+ // special for boolean values with string values as we only want to accept "true" or "false"
+ if ((type == Boolean.class || type == boolean.class) && value instanceof String) {
+ String text = (String) value;
+ if (!text.equalsIgnoreCase("true") && !text.equalsIgnoreCase("false")) {
+ throw new IllegalArgumentException(
+ "Cannot convert the String value: " + value + " to type: " + type
+ + " as the value is not true or false");
+ }
+ }
+
+ return camelContext.getTypeConverter().convertTo(type, value);
+ }
+
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointUriAssemblerGenerator.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointUriAssemblerGenerator.java
new file mode 100644
index 0000000..c097e03
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointUriAssemblerGenerator.java
@@ -0,0 +1,69 @@
+/*
+ * 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.IOException;
+import java.io.Writer;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.stream.Collectors;
+
+import org.apache.camel.tooling.model.BaseOptionModel;
+
+public final class EndpointUriAssemblerGenerator {
+
+ private EndpointUriAssemblerGenerator() {
+ }
+
+ public static void generateEndpointUriAssembler(
+ String pn, String cn, String en,
+ String pfqn, String psn,
+ Collection<? extends BaseOptionModel> options, Writer w)
+ throws IOException {
+
+ w.write("/* " + AbstractGeneratorMojo.GENERATED_MSG + " */\n");
+ w.write("package " + pn + ";\n");
+ w.write("\n");
+ w.write("import java.net.URISyntaxException;\n");
+ w.write("import java.util.Map;\n");
+ w.write("\n");
+ w.write("import org.apache.camel.CamelContext;\n");
+ w.write("import org.apache.camel.spi.EndpointUriAssembler;\n");
+ w.write("import " + pfqn + ";\n");
+ w.write("\n");
+ w.write("/**\n");
+ w.write(" * " + AbstractGeneratorMojo.GENERATED_MSG + "\n");
+ w.write(" */\n");
+ w.write("@SuppressWarnings(\"unchecked\")\n");
+ w.write("public class " + cn + " extends " + psn
+ + " implements EndpointUriAssembler {\n");
+
+ // sort options A..Z so they always have same order
+ options = options.stream().sorted(Comparator.comparing(BaseOptionModel::getName)).collect(Collectors.toList());
+
+ // generate API that returns all the options
+ w.write("\n");
+ w.write(" @Override\n");
+ w.write(" public String buildUri(CamelContext camelContext, String scheme, Map<String, String> parameters) throws URISyntaxException {\n");
+ w.write(" return null;\n");
+ w.write(" }\n");
+
+ w.write("}\n");
+ w.write("\n");
+ }
+
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/GenerateComponentMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/GenerateComponentMojo.java
index c672a72..fc199c5 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/GenerateComponentMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/GenerateComponentMojo.java
@@ -44,6 +44,8 @@ public class GenerateComponentMojo extends AbstractGenerateMojo {
invoke(GenerateConfigurerMojo.class);
// generate-endpoint-schema
invoke(EndpointSchemaGeneratorMojo.class);
+ // generate uri-endpoint-assembler
+ invoke(GenerateUriEndpointAssemblerMojo.class);
// prepare-components
invoke(PrepareComponentMojo.class);
// validate-components
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/GenerateMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/GenerateMojo.java
index bb4ed04..de3a88d 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/GenerateMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/GenerateMojo.java
@@ -44,6 +44,8 @@ public class GenerateMojo extends AbstractGenerateMojo {
invoke(PackageModelMojo.class);
// generate-endpoint-schema
invoke(EndpointSchemaGeneratorMojo.class);
+ // generate uri-endpoint-assembler
+ invoke(GenerateUriEndpointAssemblerMojo.class);
// prepare-components
invoke(PrepareComponentMojo.class);
// prepare-main
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/GenerateUriEndpointAssemblerMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/GenerateUriEndpointAssemblerMojo.java
new file mode 100644
index 0000000..431f2d7
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/GenerateUriEndpointAssemblerMojo.java
@@ -0,0 +1,232 @@
+/*
+ * 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.io.IOError;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+import org.apache.camel.tooling.model.ComponentModel;
+import org.apache.camel.tooling.model.JsonMapper;
+import org.apache.camel.tooling.util.PackageHelper;
+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;
+
+import static org.apache.camel.tooling.util.PackageHelper.loadText;
+
+/**
+ * Abstract class for endpoint uri assembler generator.
+ */
+@Mojo(name = "generate-uri-endpoint-assembler", threadSafe = true, defaultPhase = LifecyclePhase.PROCESS_CLASSES,
+ requiresDependencyCollection = ResolutionScope.COMPILE,
+ requiresDependencyResolution = ResolutionScope.COMPILE)
+public class GenerateUriEndpointAssemblerMojo extends AbstractGeneratorMojo {
+
+ /**
+ * The project build directory
+ */
+ @Parameter(defaultValue = "${project.build.directory}")
+ protected File buildDir;
+
+ @Parameter(defaultValue = "${project.basedir}/src/generated/java")
+ protected File sourcesOutputDir;
+ @Parameter(defaultValue = "${project.basedir}/src/generated/resources")
+ protected File resourcesOutputDir;
+
+ public GenerateUriEndpointAssemblerMojo() {
+ }
+
+ @Override
+ public void execute() throws MojoExecutionException, MojoFailureException {
+ if ("pom".equals(project.getPackaging())) {
+ return;
+ }
+
+ buildDir = new File(project.getBuild().getDirectory());
+
+ if (sourcesOutputDir == null) {
+ sourcesOutputDir = new File(project.getBasedir(), "src/generated/java");
+ }
+ if (resourcesOutputDir == null) {
+ resourcesOutputDir = new File(project.getBasedir(), "src/generated/resources");
+ }
+
+ Map<File, Supplier<String>> files;
+ try {
+ files = Files
+ .find(buildDir.toPath(), Integer.MAX_VALUE,
+ (p, a) -> a.isRegularFile() && p.toFile().getName().endsWith(PackageHelper.JSON_SUFIX))
+ .collect(Collectors.toMap(Path::toFile, s -> cache(() -> loadJson(s.toFile()))));
+ } catch (IOException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+
+ executeComponent(files);
+ }
+
+ private void executeComponent(Map<File, Supplier<String>> jsonFiles) throws MojoExecutionException, MojoFailureException {
+ // find the component names
+ Set<String> componentNames = new TreeSet<>();
+ findComponentNames(buildDir, componentNames);
+
+ // create auto configuration for the components
+ if (!componentNames.isEmpty()) {
+ getLog().debug("Found " + componentNames.size() + " components");
+
+ List<ComponentModel> allModels = new LinkedList<>();
+ for (String componentName : componentNames) {
+ String json = loadComponentJson(jsonFiles, componentName);
+ if (json != null) {
+ ComponentModel model = JsonMapper.generateComponentModel(json);
+ allModels.add(model);
+ }
+ }
+
+ // Group the models by implementing classes
+ Map<String, List<ComponentModel>> grModels
+ = allModels.stream().collect(Collectors.groupingBy(ComponentModel::getJavaType));
+ for (String componentClass : grModels.keySet()) {
+ List<ComponentModel> compModels = grModels.get(componentClass);
+ for (ComponentModel model : compModels) {
+ // if more than one, we have a component class with multiple components aliases
+ try {
+ createEndpointUrlAssembler(model);
+ } catch (IOException e) {
+ throw new MojoExecutionException("Error generating source code", e);
+ }
+ }
+ }
+ }
+ }
+
+ protected void createEndpointUrlAssembler(ComponentModel model) throws IOException {
+ getLog().info("Generating endpoint-uri-assembler: " + model.getScheme());
+
+ String fqn = model.getJavaType();
+ generateEndpointUriAssembler(fqn, fqn, model.getEndpointOptions(), sourcesOutputDir);
+
+ int pos = fqn.lastIndexOf('.');
+ String pn = fqn.substring(0, pos);
+ String cn = fqn.substring(pos + 1) + "EndpointUriAssembler";
+ // remove component from name
+ cn = cn.replace("Component", "");
+ fqn = pn + "." + cn;
+
+ String pval = model.getScheme() + "-endpoint";
+ updateResource(resourcesOutputDir.toPath(),
+ "META-INF/services/org/apache/camel/assembler/" + pval,
+ "# " + GENERATED_MSG + NL + "class=" + fqn + NL);
+
+ // META-INF/services/org/apache/camel/configurer/
+ if (model.getAlternativeSchemes() != null) {
+ String[] schemes = model.getAlternativeSchemes().split(",");
+ for (String alt : schemes) {
+ pval = alt + "-endpoint";
+ updateResource(resourcesOutputDir.toPath(),
+ "META-INF/services/org/apache/camel/assembler/" + pval,
+ "# " + GENERATED_MSG + NL + "class=" + fqn + NL);
+ }
+ }
+ }
+
+ @Deprecated
+ private void generateEndpointUriAssembler(
+ String fqn, String targetFqn, List<ComponentModel.EndpointOptionModel> options, File outputDir)
+ throws IOException {
+
+ int pos = targetFqn.lastIndexOf('.');
+ String pn = targetFqn.substring(0, pos);
+ String cn = targetFqn.substring(pos + 1) + "EndpointUriAssembler";
+ // remove component from name
+ cn = cn.replace("Component", "");
+ String en = fqn;
+ String pfqn = fqn;
+ String psn = "org.apache.camel.support.component.EndpointUriAssemblerSupport";
+
+ StringWriter sw = new StringWriter();
+ EndpointUriAssemblerGenerator.generateEndpointUriAssembler(pn, cn, en, pfqn, psn, options, sw);
+
+ String source = sw.toString();
+
+ String fileName = pn.replace('.', '/') + "/" + cn + ".java";
+ outputDir.mkdirs();
+ boolean updated = updateResource(buildContext, outputDir.toPath().resolve(fileName), source);
+ if (updated) {
+ getLog().info("Updated " + fileName);
+ }
+ }
+
+ protected static String loadJson(File file) {
+ try {
+ return loadText(file);
+ } catch (IOException e) {
+ throw new IOError(e);
+ }
+ }
+
+ protected static String loadComponentJson(Map<File, Supplier<String>> jsonFiles, String componentName) {
+ return loadJsonOfType(jsonFiles, componentName, "component");
+ }
+
+ protected static String loadJsonOfType(Map<File, Supplier<String>> jsonFiles, String modelName, String type) {
+ for (Map.Entry<File, Supplier<String>> entry : jsonFiles.entrySet()) {
+ if (entry.getKey().getName().equals(modelName + ".json")) {
+ String json = entry.getValue().get();
+ if (json.contains("\"kind\": \"" + type + "\"")) {
+ return json;
+ }
+ }
+ }
+ return null;
+ }
+
+ protected void findComponentNames(File dir, Set<String> componentNames) {
+ File f = new File(dir, "classes/META-INF/services/org/apache/camel/component");
+
+ if (f.exists() && f.isDirectory()) {
+ File[] files = f.listFiles();
+ if (files != null) {
+ for (File file : files) {
+ // skip directories as there may be a sub .resolver
+ // directory
+ if (file.isDirectory()) {
+ continue;
+ }
+ String name = file.getName();
+ if (name.charAt(0) != '.') {
+ componentNames.add(name);
+ }
+ }
+ }
+ }
+ }
+
+}