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 2019/06/19 12:23:32 UTC
[camel] 03/09: CAMEL-13663: camel-main-maven-plugin to generte
spring-boot tooling metadata to fool Java editors to have code completions
for Camel Main application.properties files.
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 0d9a90b1a61cba6762119ca463af43f779ed3f21
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed Jun 19 08:04:22 2019 +0200
CAMEL-13663: camel-main-maven-plugin to generte spring-boot tooling metadata to fool Java editors to have code completions for Camel Main application.properties files.
---
.../src/main/docs/camel-main-maven-plugin.adoc | 57 ++++++++
.../org/apache/camel/maven/AbstractMainMojo.java | 86 ++++++++++-
.../java/org/apache/camel/maven/AutowireMojo.java | 69 +--------
.../apache/camel/maven/SpringBootToolingMojo.java | 162 +++++++++++++++++++++
4 files changed, 304 insertions(+), 70 deletions(-)
diff --git a/catalog/camel-main-maven-plugin/src/main/docs/camel-main-maven-plugin.adoc b/catalog/camel-main-maven-plugin/src/main/docs/camel-main-maven-plugin.adoc
index d826227..e8d1364 100644
--- a/catalog/camel-main-maven-plugin/src/main/docs/camel-main-maven-plugin.adoc
+++ b/catalog/camel-main-maven-plugin/src/main/docs/camel-main-maven-plugin.adoc
@@ -3,6 +3,8 @@
The Camel Main Maven Plugin supports the following goals
- camel-main:autowire - To pre-scan your project and prepare autowiring by classpath scanning
+ - camel-main:spring-boot-tooling - To pre-scan your project and builds spring boot tooling metafiles
+ which fools tools to offer code completion for editing properties files.
== camel:autowire
@@ -157,3 +159,58 @@ The maven plugin *autowire* goal supports the following options which can be con
You can find more details and a working example at `examples/camel-example-main-artemis`.
+
+== camel:spring-boot-tooling
+
+To pre-scan your project and builds spring boot tooling metafiles
+which fools tools to offer code completion for editing properties files.
+
+----
+mvn camel-main:spring-boot-tooling
+----
+
+This will generate a Spring Boot tooling metadata file in `src/main/resouces/META-INF/spring-configuration-metadata.json`
+which contains all the options from Camel Main and the components from the classpath.
+
+For example if you have camel-jms on the classpath, then Java tools that has support for Spring Boot,
+can offer code completions when you edit `application.properties` file:
+
+----
+camel.component.jms.CURSOR HERE
+----
+
+Just press ctrl + space at the _CURSOR HERE_ location and you will get all the options you
+can configure on the JMS component.
+
+
+You can also enable the plugin to automatic run as part of the build to catch these errors.
+
+[source,xml]
+----
+<plugin>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-main-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>process-classes</phase>
+ <goals>
+ <goal>spring-boot-tooling</goal>
+ </goals>
+ </execution>
+ </executions>
+</plugin>
+----
+
+The phase determines when the plugin runs. In the sample above the phase is `process-classes` which runs after
+the compilation of the main source code.
+
+=== Options
+
+The maven plugin *spring-boot-tooling* goal supports the following options which can be configured from the command line (use `-D` syntax), or defined in the `pom.xml` file in the `<configuration>` tag.
+
+|===
+| Parameter | Default Value | Description
+| logClasspath | false | Whether to log the classpath when starting
+| downloadVersion | true | Whether to allow downloading Camel catalog version from the internet.
+ This is needed if the project * uses a different Camel version than this plugin is using by default.
+|===
diff --git a/catalog/camel-main-maven-plugin/src/main/java/org/apache/camel/maven/AbstractMainMojo.java b/catalog/camel-main-maven-plugin/src/main/java/org/apache/camel/maven/AbstractMainMojo.java
index f1de650..0d3e20f 100644
--- a/catalog/camel-main-maven-plugin/src/main/java/org/apache/camel/maven/AbstractMainMojo.java
+++ b/catalog/camel-main-maven-plugin/src/main/java/org/apache/camel/maven/AbstractMainMojo.java
@@ -1,13 +1,13 @@
-/**
+/*
* 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
- * <p>
- * http://www.apache.org/licenses/LICENSE-2.0
- * <p>
+ *
+ * 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.
@@ -32,6 +32,9 @@ import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
+import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.catalog.DefaultCamelCatalog;
+import org.apache.camel.catalog.maven.MavenVersionManager;
import org.apache.camel.util.IOHelper;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.repository.ArtifactRepository;
@@ -45,6 +48,10 @@ import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.repository.RepositorySystem;
import org.codehaus.mojo.exec.AbstractExecMojo;
+import org.reflections.Reflections;
+import org.reflections.scanners.SubTypesScanner;
+import org.reflections.util.ClasspathHelper;
+import org.reflections.util.ConfigurationBuilder;
/**
* Base class for maven goals.
@@ -66,6 +73,12 @@ public abstract class AbstractMainMojo extends AbstractExecMojo {
protected transient ClassLoader classLoader;
+ protected transient CamelCatalog catalog;
+
+ protected transient Reflections reflections;
+
+ protected transient Set<String> camelComponentsOnClasspath;
+
@Component
private RepositorySystem repositorySystem;
@@ -78,6 +91,62 @@ public abstract class AbstractMainMojo extends AbstractExecMojo {
@Parameter(property = "project.remoteArtifactRepositories")
private List remoteRepositories;
+ @Override
+ public void execute() throws MojoExecutionException, MojoFailureException {
+ catalog = new DefaultCamelCatalog();
+ // add activemq as known component
+ catalog.addComponent("activemq", "org.apache.activemq.camel.component.ActiveMQComponent");
+ // enable loading other catalog versions dynamically
+ catalog.setVersionManager(new MavenVersionManager());
+ // enable caching
+ catalog.enableCache();
+
+ String detectedVersion = findCamelVersion(project);
+ if (detectedVersion != null) {
+ getLog().info("Detected Camel version used in project: " + detectedVersion);
+ }
+
+ if (downloadVersion) {
+ String catalogVersion = catalog.getCatalogVersion();
+ String version = findCamelVersion(project);
+ if (version != null && !version.equals(catalogVersion)) {
+ // the project uses a different Camel version so attempt to load it
+ getLog().info("Downloading Camel version: " + version);
+ boolean loaded = catalog.loadVersion(version);
+ if (!loaded) {
+ getLog().warn("Error downloading Camel version: " + version);
+ }
+ }
+ }
+
+ if (catalog.getLoadedVersion() != null) {
+ getLog().info("Pre-scanning using downloaded Camel version: " + catalog.getLoadedVersion());
+ } else {
+ getLog().info("Pre-scanning using Camel version: " + catalog.getCatalogVersion());
+ }
+
+ // find all Camel components on classpath and check in the camel-catalog for all component options
+ // then check each option if its a complex type and an interface
+ // and if so scan class-path and find the single class implementing this interface
+ // write this to META-INF/services/org/apache/camel/autowire.properties
+
+ // find all Camel components on classpath
+ camelComponentsOnClasspath = resolveCamelComponentsFromClasspath();
+ if (camelComponentsOnClasspath.isEmpty()) {
+ getLog().warn("No Camel components discovered in classpath");
+ return;
+ } else {
+ getLog().info("Discovered " + camelComponentsOnClasspath.size() + " Camel components from classpath: " + camelComponentsOnClasspath);
+ }
+
+ // build index of classes on classpath
+ getLog().debug("Indexing classes on classpath");
+ reflections = new Reflections(new ConfigurationBuilder()
+ .addUrls(ClasspathHelper.forClassLoader(classLoader))
+ .addClassLoader(classLoader)
+ .setScanners(new SubTypesScanner()));
+ }
+
protected static String findCamelVersion(MavenProject project) {
Dependency candidate = null;
@@ -194,4 +263,13 @@ public abstract class AbstractMainMojo extends AbstractExecMojo {
return artifacts;
}
+
+ protected String safeJavaType(String javaType) {
+ int pos = javaType.indexOf('<');
+ if (pos > 0) {
+ return javaType.substring(0, pos);
+ }
+ return javaType;
+ }
+
}
diff --git a/catalog/camel-main-maven-plugin/src/main/java/org/apache/camel/maven/AutowireMojo.java b/catalog/camel-main-maven-plugin/src/main/java/org/apache/camel/maven/AutowireMojo.java
index 1e5c955..63f0c61 100644
--- a/catalog/camel-main-maven-plugin/src/main/java/org/apache/camel/maven/AutowireMojo.java
+++ b/catalog/camel-main-maven-plugin/src/main/java/org/apache/camel/maven/AutowireMojo.java
@@ -30,9 +30,7 @@ import java.util.Set;
import java.util.stream.Collectors;
import org.apache.camel.catalog.CamelCatalog;
-import org.apache.camel.catalog.DefaultCamelCatalog;
import org.apache.camel.catalog.JSonSchemaHelper;
-import org.apache.camel.catalog.maven.MavenVersionManager;
import org.apache.camel.support.PatternHelper;
import org.apache.camel.util.IOHelper;
import org.apache.camel.util.OrderedProperties;
@@ -44,9 +42,6 @@ import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.reflections.Reflections;
-import org.reflections.scanners.SubTypesScanner;
-import org.reflections.util.ClasspathHelper;
-import org.reflections.util.ConfigurationBuilder;
/**
* Pre scans your project and prepare autowiring by classpath scanning
@@ -96,58 +91,8 @@ public class AutowireMojo extends AbstractMainMojo {
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
- CamelCatalog catalog = new DefaultCamelCatalog();
- // add activemq as known component
- catalog.addComponent("activemq", "org.apache.activemq.camel.component.ActiveMQComponent");
- // enable loading other catalog versions dynamically
- catalog.setVersionManager(new MavenVersionManager());
- // enable caching
- catalog.enableCache();
-
- String detectedVersion = findCamelVersion(project);
- if (detectedVersion != null) {
- getLog().info("Detected Camel version used in project: " + detectedVersion);
- }
-
- if (downloadVersion) {
- String catalogVersion = catalog.getCatalogVersion();
- String version = findCamelVersion(project);
- if (version != null && !version.equals(catalogVersion)) {
- // the project uses a different Camel version so attempt to load it
- getLog().info("Downloading Camel version: " + version);
- boolean loaded = catalog.loadVersion(version);
- if (!loaded) {
- getLog().warn("Error downloading Camel version: " + version);
- }
- }
- }
-
- if (catalog.getLoadedVersion() != null) {
- getLog().info("Pre-scanning using downloaded Camel version: " + catalog.getLoadedVersion());
- } else {
- getLog().info("Pre-scanning using Camel version: " + catalog.getCatalogVersion());
- }
-
- // find all Camel components on classpath and check in the camel-catalog for all component options
- // then check each option if its a complex type and an interface
- // and if so scan class-path and find the single class implementing this interface
- // write this to META-INF/services/org/apache/camel/autowire.properties
-
- // find all Camel components on classpath
- Set<String> components = resolveCamelComponentsFromClasspath();
- if (components.isEmpty()) {
- getLog().warn("No Camel components discovered in classpath");
- return;
- } else {
- getLog().info("Discovered " + components.size() + " Camel components from classpath: " + components);
- }
-
- // build index of classes on classpath
- getLog().debug("Indexing classes on classpath");
- Reflections reflections = new Reflections(new ConfigurationBuilder()
- .addUrls(ClasspathHelper.forClassLoader(classLoader))
- .addClassLoader(classLoader)
- .setScanners(new SubTypesScanner()));
+ // perform common tasks
+ super.execute();
// load default mappings
Properties mappingProperties = loadDefaultMappings();
@@ -170,7 +115,7 @@ public class AutowireMojo extends AbstractMainMojo {
}
// find the autowire via classpath scanning
- List<String> autowires = findAutowireComponentOptionsByClasspath(catalog, components, reflections, mappingProperties);
+ List<String> autowires = findAutowireComponentOptionsByClasspath(catalog, camelComponentsOnClasspath, reflections, mappingProperties);
if (!autowires.isEmpty()) {
outFolder.mkdirs();
@@ -348,12 +293,4 @@ public class AutowireMojo extends AbstractMainMojo {
return !clazz.getName().startsWith("org.apache.camel");
}
- protected String safeJavaType(String javaType) {
- int pos = javaType.indexOf('<');
- if (pos > 0) {
- return javaType.substring(0, pos);
- }
- return javaType;
- }
-
}
diff --git a/catalog/camel-main-maven-plugin/src/main/java/org/apache/camel/maven/SpringBootToolingMojo.java b/catalog/camel-main-maven-plugin/src/main/java/org/apache/camel/maven/SpringBootToolingMojo.java
new file mode 100644
index 0000000..a249e2d
--- /dev/null
+++ b/catalog/camel-main-maven-plugin/src/main/java/org/apache/camel/maven/SpringBootToolingMojo.java
@@ -0,0 +1,162 @@
+/*
+ * 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;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.camel.catalog.JSonSchemaHelper;
+import org.apache.camel.util.IOHelper;
+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;
+
+/**
+ * Pre scans your project and builds spring boot tooling metafiles which fools tools to
+ * offer code completion for editing properties files.
+ */
+@Mojo(name = "spring-boot-tooling", defaultPhase = LifecyclePhase.PROCESS_CLASSES, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE)
+public class SpringBootToolingMojo extends AbstractMainMojo {
+
+ /**
+ * The output directory for generated spring boot tooling file
+ */
+ @Parameter(readonly = true, defaultValue = "${project.build.directory}/../src/main/resources/META-INF/")
+ protected File outFolder;
+
+ @Override
+ public void execute() throws MojoExecutionException, MojoFailureException {
+ // perform common tasks
+ super.execute();
+
+ // TODO: generate for Camel Main configuration which can be a bit more complex than components from classpath
+ List<String[]> data = new ArrayList<>();
+
+ for (String componentName : camelComponentsOnClasspath) {
+ String json = catalog.componentJSonSchema(componentName);
+ if (json == null) {
+ getLog().debug("Cannot find component JSon metadata for component: " + componentName);
+ continue;
+ }
+
+
+ List<Map<String, String>> rows = JSonSchemaHelper.parseJsonSchema("componentProperties", json, true);
+ Set<String> names = JSonSchemaHelper.getNames(rows);
+ for (String name : names) {
+ Map<String, String> row = JSonSchemaHelper.getRow(rows, name);
+ String javaType = springBootJavaType(safeJavaType(row.get("javaType")));
+ String desc = row.get("description");
+ String defaultValue = row.get("defaultValue");
+ // we want to use dash in the name
+ String dash = camelCaseToDash(name);
+ String key = "camel.component." + componentName + "." + dash;
+ data.add(new String[]{key, javaType, desc, defaultValue});
+ }
+ }
+
+ if (!data.isEmpty()) {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("{\n");
+ sb.append(" \"properties\": [\n");
+ for (int i = 0; i < data.size(); i++) {
+ String[] row = data.get(i);
+ String name = row[0];
+ String javaType = row[1];
+ String desc = row[2];
+ String defaultValue = row[3];
+ sb.append(" {\n");
+ sb.append(" \"name\": \"" + name + "\",\n");
+ sb.append(" \"type\": \"" + javaType + "\",\n");
+ sb.append(" \"description\": \"" + desc + "\"");
+ if (defaultValue != null) {
+ sb.append(",\n");
+ if (springBootDefaultValueQuotes(javaType)) {
+ sb.append(" \"defaultValue\": \"" + defaultValue + "\"\n");
+ } else {
+ sb.append(" \"defaultValue\": " + defaultValue + "\n");
+ }
+ } else {
+ sb.append("\n");
+ }
+ if (i < data.size() - 1) {
+ sb.append(" },\n");
+ } else {
+ sb.append(" }\n");
+ }
+ }
+ sb.append(" ]\n");
+ sb.append("}\n");
+
+ outFolder.mkdirs();
+ File file = new File(outFolder, "spring-configuration-metadata.json");
+ try {
+ FileOutputStream fos = new FileOutputStream(file, false);
+ fos.write(sb.toString().getBytes());
+ IOHelper.close(fos);
+ getLog().info("Created file: " + file);
+ } catch (Throwable e) {
+ throw new MojoFailureException("Cannot write to file " + file + " due " + e.getMessage(), e);
+ }
+ }
+ }
+
+ private static String camelCaseToDash(String name) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < name.length(); i++) {
+ char ch = name.charAt(i);
+ if (Character.isUpperCase(ch)) {
+ sb.append("-");
+ sb.append(Character.toLowerCase(ch));
+ } else {
+ sb.append(ch);
+ }
+ }
+ return sb.toString();
+ }
+
+ private static String springBootJavaType(String javaType) {
+ if ("boolean".equalsIgnoreCase(javaType)) {
+ return "java.lang.Boolean";
+ } else if ("int".equalsIgnoreCase(javaType)) {
+ return "java.lang.Integer";
+ } else if ("long".equalsIgnoreCase(javaType)) {
+ return "java.lang.Long";
+ } else if ("string".equalsIgnoreCase(javaType)) {
+ return "java.lang.String";
+ }
+ return javaType;
+ }
+
+ private static boolean springBootDefaultValueQuotes(String javaType) {
+ if ("java.lang.Boolean".equalsIgnoreCase(javaType)) {
+ return false;
+ } else if ("java.lang.Integer".equalsIgnoreCase(javaType)) {
+ return false;
+ } else if ("java.lang.Long".equalsIgnoreCase(javaType)) {
+ return false;
+ }
+ return true;
+ }
+}