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 2017/01/19 11:06:44 UTC

[04/17] camel git commit: CAMEL-10721: camel-connector

CAMEL-10721: camel-connector


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

Branch: refs/heads/master
Commit: c473d0b9627ff8b6a817c31092c55f86904a5137
Parents: c4ee179
Author: Claus Ibsen <da...@apache.org>
Authored: Wed Jan 18 14:27:14 2017 +0100
Committer: Claus Ibsen <da...@apache.org>
Committed: Thu Jan 19 11:00:00 2017 +0100

----------------------------------------------------------------------
 connectors/camel-connector-maven-plugin/pom.xml |  90 ++++
 .../maven/connector/CollectionStringBuffer.java |  57 +++
 .../camel/maven/connector/ConnectorMojo.java    | 501 +++++++++++++++++++
 .../camel/maven/connector/FileHelper.java       |  56 +++
 .../apache/camel/maven/connector/GitHelper.java | 123 +++++
 .../camel/maven/connector/JSonSchemaHelper.java | 356 +++++++++++++
 .../src/main/resources/META-INF/MANIFEST.MF     |   4 +
 connectors/pom.xml                              |   1 +
 parent/pom.xml                                  |   2 +
 9 files changed, 1190 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/c473d0b9/connectors/camel-connector-maven-plugin/pom.xml
----------------------------------------------------------------------
diff --git a/connectors/camel-connector-maven-plugin/pom.xml b/connectors/camel-connector-maven-plugin/pom.xml
new file mode 100644
index 0000000..6eaaf29
--- /dev/null
+++ b/connectors/camel-connector-maven-plugin/pom.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.camel</groupId>
+    <artifactId>connectors</artifactId>
+    <version>2.19.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>camel-connector-maven-plugin</artifactId>
+  <name>Camel :: Connector :: Camel Connector Maven Plugin</name>
+  <packaging>maven-plugin</packaging>
+
+  <dependencies>
+
+    <!-- we extend this existing maven plugin -->
+    <dependency>
+      <groupId>org.apache.maven.plugins</groupId>
+      <artifactId>maven-jar-plugin</artifactId>
+      <version>${maven-jar-plugin-version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.plugin-tools</groupId>
+      <artifactId>maven-plugin-annotations</artifactId>
+      <version>3.2</version>
+      <scope>provided</scope>
+    </dependency>
+
+    <!-- use jackson json parser -->
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-core</artifactId>
+      <version>${jackson2-version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-annotations</artifactId>
+      <version>${jackson2-version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-databind</artifactId>
+      <version>${jackson2-version}</version>
+    </dependency>
+
+    <!-- logging -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-simple</artifactId>
+      <version>${slf4j-version}</version>
+      <scope>runtime</scope>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <version>${maven-jar-plugin-version}</version>
+        <configuration>
+          <!-- must include our own dummy manifest -->
+          <archive>
+            <manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
+          </archive>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>

http://git-wip-us.apache.org/repos/asf/camel/blob/c473d0b9/connectors/camel-connector-maven-plugin/src/main/java/org/apache/camel/maven/connector/CollectionStringBuffer.java
----------------------------------------------------------------------
diff --git a/connectors/camel-connector-maven-plugin/src/main/java/org/apache/camel/maven/connector/CollectionStringBuffer.java b/connectors/camel-connector-maven-plugin/src/main/java/org/apache/camel/maven/connector/CollectionStringBuffer.java
new file mode 100644
index 0000000..bd11654
--- /dev/null
+++ b/connectors/camel-connector-maven-plugin/src/main/java/org/apache/camel/maven/connector/CollectionStringBuffer.java
@@ -0,0 +1,57 @@
+/**
+ * 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.connector;
+
+public class CollectionStringBuffer {
+    private final StringBuilder buffer = new StringBuilder();
+    private String separator;
+    private boolean first = true;
+
+    public CollectionStringBuffer() {
+        this(", ");
+    }
+
+    public CollectionStringBuffer(String separator) {
+        this.separator = separator;
+    }
+
+    @Override
+    public String toString() {
+        return buffer.toString();
+    }
+
+    public void append(Object value) {
+        if (first) {
+            first = false;
+        } else {
+            buffer.append(separator);
+        }
+        buffer.append(value);
+    }
+
+    public String getSeparator() {
+        return separator;
+    }
+
+    public void setSeparator(String separator) {
+        this.separator = separator;
+    }
+
+    public boolean isEmpty() {
+        return first;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/c473d0b9/connectors/camel-connector-maven-plugin/src/main/java/org/apache/camel/maven/connector/ConnectorMojo.java
----------------------------------------------------------------------
diff --git a/connectors/camel-connector-maven-plugin/src/main/java/org/apache/camel/maven/connector/ConnectorMojo.java b/connectors/camel-connector-maven-plugin/src/main/java/org/apache/camel/maven/connector/ConnectorMojo.java
new file mode 100644
index 0000000..4683db7
--- /dev/null
+++ b/connectors/camel-connector-maven-plugin/src/main/java/org/apache/camel/maven/connector/ConnectorMojo.java
@@ -0,0 +1,501 @@
+/**
+ * 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.connector;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.plugin.MojoExecutionException;
+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.apache.maven.plugin.jar.AbstractJarMojo;
+
+@Mojo(name = "jar", defaultPhase = LifecyclePhase.PREPARE_PACKAGE, requiresProject = true, threadSafe = true,
+        requiresDependencyResolution = ResolutionScope.RUNTIME)
+public class ConnectorMojo extends AbstractJarMojo {
+
+    /**
+     * Directory containing the classes and resource files that should be packaged into the JAR.
+     */
+    @Parameter(defaultValue = "${project.build.outputDirectory}", required = true)
+    private File classesDirectory;
+
+    @Override
+    protected File getClassesDirectory() {
+        return classesDirectory;
+    }
+
+    @Override
+    protected String getClassifier() {
+        return "camel-connector";
+    }
+
+    @Override
+    protected String getType() {
+        return "jar";
+    }
+
+    @Override
+    public File createArchive() throws MojoExecutionException {
+
+        String gitUrl;
+
+        // find the component dependency and get its .json file
+
+        File file = new File(classesDirectory, "camel-connector.json");
+        if (file.exists()) {
+
+            // we want to include the git url of the project
+            File gitFolder = GitHelper.findGitFolder();
+            try {
+                gitUrl = GitHelper.extractGitUrl(gitFolder);
+            } catch (IOException e) {
+                throw new MojoExecutionException("Cannot extract gitUrl due " + e.getMessage(), e);
+            }
+            if (gitUrl == null) {
+                getLog().warn("No .git directory found for connector");
+            }
+
+            try {
+
+                ObjectMapper mapper = new ObjectMapper();
+                Map dto = mapper.readValue(file, Map.class);
+
+                // embed girUrl in camel-connector.json file
+                if (gitUrl != null) {
+                    String existingGitUrl = (String) dto.get("gitUrl");
+                    if (existingGitUrl == null || !existingGitUrl.equals(gitUrl)) {
+                        dto.put("gitUrl", gitUrl);
+                        // update file
+                        mapper.writerWithDefaultPrettyPrinter().writeValue(file, dto);
+                        // update source file also
+                        File root = classesDirectory.getParentFile().getParentFile();
+                        file = new File(root, "src/main/resources/camel-connector.json");
+                        if (file.exists()) {
+                            getLog().info("Updating gitUrl to " + file);
+                            mapper.writerWithDefaultPrettyPrinter().writeValue(file, dto);
+                        }
+                    }
+                }
+
+                File schema = embedCamelComponentSchema(file);
+                if (schema != null) {
+                    String json = FileHelper.loadText(new FileInputStream(schema));
+
+                    List<Map<String, String>> rows = JSonSchemaHelper.parseJsonSchema("component", json, false);
+                    String header = buildComponentHeaderSchema(rows, dto, gitUrl);
+                    getLog().debug(header);
+
+                    rows = JSonSchemaHelper.parseJsonSchema("componentProperties", json, true);
+                    // we do not offer editing component properties (yet) so clear the rows
+                    rows.clear();
+                    String componentOptions = buildComponentOptionsSchema(rows, dto);
+                    getLog().debug(componentOptions);
+
+                    rows = JSonSchemaHelper.parseJsonSchema("properties", json, true);
+                    String endpointOptions = buildEndpointOptionsSchema(rows, dto);
+                    getLog().debug(endpointOptions);
+
+                    // generate the json file
+                    StringBuilder jsonSchema = new StringBuilder();
+                    jsonSchema.append("{\n");
+                    jsonSchema.append(header);
+                    jsonSchema.append(componentOptions);
+                    jsonSchema.append(endpointOptions);
+                    jsonSchema.append("}\n");
+
+                    String newJson = jsonSchema.toString();
+
+                    // parse ourselves
+                    rows = JSonSchemaHelper.parseJsonSchema("component", newJson, false);
+                    String newScheme = getOption(rows, "scheme");
+
+                    // write the json file to the target directory as if camel apt would do it
+                    String javaType = (String) dto.get("javaType");
+                    String dir = javaType.substring(0, javaType.lastIndexOf("."));
+                    dir = dir.replace('.', '/');
+                    File subDir = new File(classesDirectory, dir);
+                    String name = newScheme + ".json";
+                    File out = new File(subDir, name);
+
+                    FileOutputStream fos = new FileOutputStream(out, false);
+                    fos.write(newJson.getBytes());
+                    fos.close();
+                }
+
+                // build json schema for component that only has the selectable options
+            } catch (Exception e) {
+                throw new MojoExecutionException("Error in connector-maven-plugin", e);
+            }
+        }
+
+        return super.createArchive();
+    }
+
+    private String extractJavaType(String scheme) throws Exception {
+        File file = new File(classesDirectory, "META-INF/services/org/apache/camel/component/" + scheme);
+        if (file.exists()) {
+            List<String> lines = loadFile(file);
+            String fqn = extractClass(lines);
+            return fqn;
+        }
+        return null;
+    }
+
+    private String getOption(List<Map<String, String>> rows, String key) {
+        for (Map<String, String> row : rows) {
+            if (row.containsKey(key)) {
+                return row.get(key);
+            }
+        }
+        return null;
+    }
+
+    private String buildComponentOptionsSchema(List<Map<String, String>> rows, Map dto) throws JsonProcessingException {
+        ObjectMapper mapper = new ObjectMapper();
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("  \"componentProperties\": {\n");
+
+        for (int i = 0; i < rows.size(); i++) {
+            Map<String, String> row = rows.get(i);
+            String key = row.get("name");
+            row.remove("name");
+            String line = mapper.writeValueAsString(row);
+
+            sb.append("    \"" + key + "\": ");
+            sb.append(line);
+            if (i < rows.size() - 1) {
+                sb.append(",\n");
+            } else {
+                sb.append("\n");
+            }
+        }
+
+        sb.append("  },\n");
+        return sb.toString();
+    }
+
+    private String buildEndpointOptionsSchema(List<Map<String, String>> rows, Map dto) throws JsonProcessingException {
+        // find the endpoint options
+        List options = (List) dto.get("endpointOptions");
+        Map values = (Map) dto.get("endpointValues");
+
+        ObjectMapper mapper = new ObjectMapper();
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("  \"properties\": {\n");
+
+        boolean first = true;
+        for (int i = 0; i < rows.size(); i++) {
+            Map<String, String> row = rows.get(i);
+            String key = row.get("name");
+            row.remove("name");
+
+            // TODO: if no options should we include all by default instead?
+            if (options == null || !options.contains(key)) {
+                continue;
+            }
+
+            // do we have a new default value for this row?
+            if (values != null && values.containsKey(key)) {
+                String newDefaultValue = (String) values.get(key);
+                if (newDefaultValue != null) {
+                    row.put("defaultValue", newDefaultValue);
+                }
+            }
+
+            // we should build the json as one-line which is how Camel does it today
+            // which makes its internal json parser support loading our generated schema file
+            String line = mapper.writeValueAsString(row);
+
+            if (!first) {
+                sb.append(",\n");
+            }
+            sb.append("    \"" + key + "\": ");
+            sb.append(line);
+
+            first = false;
+        }
+        if (!first) {
+            sb.append("\n");
+        }
+
+        sb.append("  }\n");
+        return sb.toString();
+    }
+
+    private String buildComponentHeaderSchema(List<Map<String, String>> rows, Map dto, String gitUrl) throws Exception {
+        String baseScheme = (String) dto.get("baseScheme");
+        String source = (String) dto.get("source");
+        String title = (String) dto.get("name");
+        String scheme = camelCaseToDash(title);
+        String baseSyntax = getOption(rows, "syntax");
+        String syntax = baseSyntax.replaceFirst(baseScheme, scheme);
+
+        String description = (String) dto.get("description");
+        // dto has labels
+        String label = null;
+        List<String> labels = (List<String>) dto.get("labels");
+        if (labels != null) {
+            CollectionStringBuffer csb = new CollectionStringBuffer(",");
+            for (String s : labels) {
+                csb.append(s);
+            }
+            label = csb.toString();
+        }
+        String async = getOption(rows, "async");
+        String producerOnly = "To".equals(source) ? "true" : null;
+        String consumerOnly = "From".equals(source) ? "true" : null;
+        String lenientProperties = getOption(rows, "lenientProperties");
+        String javaType = extractJavaType(scheme);
+        String groupId = getProject().getGroupId();
+        String artifactId = getProject().getArtifactId();
+        String version = getProject().getVersion();
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("  \"component\": {\n");
+        sb.append("    \"girUrl\": \"" + nullSafe(gitUrl) + "\",\n");
+        sb.append("    \"kind\": \"component\",\n");
+        sb.append("    \"baseScheme\": \"" + nullSafe(baseScheme) + "\",\n");
+        sb.append("    \"scheme\": \"" + scheme + "\",\n");
+        sb.append("    \"syntax\": \"" + syntax + "\",\n");
+        sb.append("    \"title\": \"" + title + "\",\n");
+        if (description != null) {
+            sb.append("    \"description\": \"" + description + "\",\n");
+        }
+        if (label != null) {
+            sb.append("    \"label\": \"" + label + "\",\n");
+        }
+        sb.append("    \"deprecated\": \"false\",\n");
+        if (async != null) {
+            sb.append("    \"async\": \"" + async + "\",\n");
+        }
+        if (producerOnly != null) {
+            sb.append("    \"producerOnly\": \"" + producerOnly + "\",\n");
+        } else if (consumerOnly != null) {
+            sb.append("    \"consumerOnly\": \"" + consumerOnly + "\",\n");
+        }
+        if (lenientProperties != null) {
+            sb.append("    \"lenientProperties\": \"" + lenientProperties + "\",\n");
+        }
+        sb.append("    \"javaType\": \"" + javaType + "\",\n");
+        sb.append("    \"groupId\": \"" + groupId + "\",\n");
+        sb.append("    \"artifactId\": \"" + artifactId + "\",\n");
+        sb.append("    \"version\": \"" + version + "\"\n");
+        sb.append("  },\n");
+
+        return sb.toString();
+    }
+
+    private static String nullSafe(String text) {
+        return text != null ? text : "";
+    }
+
+    /**
+     * Finds and embeds the Camel component JSon schema file
+     */
+    private File embedCamelComponentSchema(File file) throws MojoExecutionException {
+        try {
+            List<String> json = loadFile(file);
+
+            String scheme = extractScheme(json);
+            String groupId = extractGroupId(json);
+            String artifactId = extractArtifactId(json);
+            String version = extractVersion(json); // version not in use
+
+            // find the artifact on the classpath that has the Camel component this connector is using
+            // then we want to grab its json schema file to embed in this JAR so we have all files together
+
+            if (scheme != null && groupId != null && artifactId != null) {
+                for (Object obj : getProject().getDependencyArtifacts()) {
+                    Artifact artifact = (Artifact) obj;
+                    if ("jar".equals(artifact.getType())) {
+                        if (groupId.equals(artifact.getGroupId()) && artifactId.equals(artifact.getArtifactId())) {
+                            // load the component file inside the file
+                            URL url = new URL("file:" + artifact.getFile());
+                            URLClassLoader child = new URLClassLoader(new URL[]{url}, this.getClass().getClassLoader());
+
+                            InputStream is = child.getResourceAsStream("META-INF/services/org/apache/camel/component/" + scheme);
+                            if (is != null) {
+                                List<String> lines = loadFile(is);
+                                String fqn = extractClass(lines);
+                                is.close();
+
+                                // only keep package
+                                String pck = fqn.substring(0, fqn.lastIndexOf("."));
+                                String name = pck.replace(".", "/") + "/" + scheme + ".json";
+
+                                is = child.getResourceAsStream(name);
+                                if (is != null) {
+                                    List<String> schema = loadFile(is);
+                                    is.close();
+
+                                    // write schema to file
+                                    File out = new File(classesDirectory, "camel-component-schema.json");
+                                    FileOutputStream fos = new FileOutputStream(out, false);
+                                    for (String line : schema) {
+                                        fos.write(line.getBytes());
+                                        fos.write("\n".getBytes());
+                                    }
+                                    fos.close();
+
+                                    getLog().info("Embedded camel-component-schema.json file for Camel component " + scheme);
+
+                                    return out;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+        } catch (Exception e) {
+            throw new MojoExecutionException("Cannot read file camel-connector.json", e);
+        }
+
+        return null;
+    }
+
+    private String extractClass(List<String> lines) {
+        for (String line : lines) {
+            line = line.trim();
+            if (line.startsWith("class=")) {
+                return line.substring(6);
+            }
+        }
+        return null;
+    }
+
+    private String extractScheme(List<String> json) {
+        for (String line : json) {
+            line = line.trim();
+            if (line.startsWith("\"baseScheme\":")) {
+                String answer = line.substring(14);
+                return answer.substring(0, answer.length() - 2);
+            }
+        }
+        return null;
+    }
+
+    private String extractGroupId(List<String> json) {
+        for (String line : json) {
+            line = line.trim();
+            if (line.startsWith("\"baseGroupId\":")) {
+                String answer = line.substring(15);
+                return answer.substring(0, answer.length() - 2);
+            }
+        }
+        return null;
+    }
+
+    private String extractArtifactId(List<String> json) {
+        for (String line : json) {
+            line = line.trim();
+            if (line.startsWith("\"baseArtifactId\":")) {
+                String answer = line.substring(18);
+                return answer.substring(0, answer.length() - 2);
+            }
+        }
+        return null;
+    }
+
+    private String extractVersion(List<String> json) {
+        for (String line : json) {
+            line = line.trim();
+            if (line.startsWith("\"baseVersion\":")) {
+                String answer = line.substring(15);
+                return answer.substring(0, answer.length() - 2);
+            }
+        }
+        return null;
+    }
+
+    private List<String> loadFile(File file) throws Exception {
+        List<String> lines = new ArrayList<>();
+        LineNumberReader reader = new LineNumberReader(new FileReader(file));
+
+        String line;
+        do {
+            line = reader.readLine();
+            if (line != null) {
+                lines.add(line);
+            }
+        } while (line != null);
+        reader.close();
+
+        return lines;
+    }
+
+    private List<String> loadFile(InputStream fis) throws Exception {
+        List<String> lines = new ArrayList<>();
+        LineNumberReader reader = new LineNumberReader(new InputStreamReader(fis));
+
+        String line;
+        do {
+            line = reader.readLine();
+            if (line != null) {
+                lines.add(line);
+            }
+        } while (line != null);
+        reader.close();
+
+        return lines;
+    }
+
+    public static String camelCaseToDash(String value) {
+        StringBuilder sb = new StringBuilder(value.length());
+        boolean dash = false;
+
+        for (char c : value.toCharArray()) {
+            // skip dash in start
+            if (sb.length() > 0 & Character.isUpperCase(c)) {
+                dash = true;
+            }
+            if (dash) {
+                sb.append('-');
+                sb.append(Character.toLowerCase(c));
+            } else {
+                // lower case first
+                if (sb.length() == 0) {
+                    sb.append(Character.toLowerCase(c));
+                } else {
+                    sb.append(c);
+                }
+            }
+            dash = false;
+        }
+        return sb.toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/c473d0b9/connectors/camel-connector-maven-plugin/src/main/java/org/apache/camel/maven/connector/FileHelper.java
----------------------------------------------------------------------
diff --git a/connectors/camel-connector-maven-plugin/src/main/java/org/apache/camel/maven/connector/FileHelper.java b/connectors/camel-connector-maven-plugin/src/main/java/org/apache/camel/maven/connector/FileHelper.java
new file mode 100644
index 0000000..caecd16
--- /dev/null
+++ b/connectors/camel-connector-maven-plugin/src/main/java/org/apache/camel/maven/connector/FileHelper.java
@@ -0,0 +1,56 @@
+/**
+ * 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.connector;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+
+public class FileHelper {
+
+    /**
+     * Loads the entire stream into memory as a String and returns it.
+     * <p/>
+     * <b>Notice:</b> This implementation appends a <tt>\n</tt> as line
+     * terminator at the of the text.
+     * <p/>
+     * Warning, don't use for crazy big streams :)
+     */
+    public static String loadText(InputStream in) throws IOException {
+        StringBuilder builder = new StringBuilder();
+        InputStreamReader isr = new InputStreamReader(in);
+        try {
+            BufferedReader reader = new LineNumberReader(isr);
+            while (true) {
+                String line = reader.readLine();
+                if (line != null) {
+                    builder.append(line);
+                    builder.append("\n");
+                } else {
+                    break;
+                }
+            }
+            return builder.toString();
+        } finally {
+            isr.close();
+            in.close();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/c473d0b9/connectors/camel-connector-maven-plugin/src/main/java/org/apache/camel/maven/connector/GitHelper.java
----------------------------------------------------------------------
diff --git a/connectors/camel-connector-maven-plugin/src/main/java/org/apache/camel/maven/connector/GitHelper.java b/connectors/camel-connector-maven-plugin/src/main/java/org/apache/camel/maven/connector/GitHelper.java
new file mode 100644
index 0000000..92c50ee
--- /dev/null
+++ b/connectors/camel-connector-maven-plugin/src/main/java/org/apache/camel/maven/connector/GitHelper.java
@@ -0,0 +1,123 @@
+/**
+ * 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.connector;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.HashMap;
+import java.util.Map;
+
+public class GitHelper {
+
+    public static File findGitFolder() {
+        File baseDir = new File("").getAbsoluteFile();
+        return findGitFolder(baseDir);
+    }
+
+    private static File findGitFolder(File basedir) {
+        File gitDir = new File(basedir, ".git");
+        if (gitDir.exists() && gitDir.isDirectory()) {
+            return gitDir;
+        }
+
+        File parent = basedir.getParentFile();
+        if (parent != null) {
+            return findGitFolder(parent);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the remote git URL for the given folder; looking for the .git/config file in the current directory or a parent directory
+     */
+    public static String extractGitUrl(File basedir) throws IOException {
+        if (basedir == null) {
+            return null;
+        }
+        if (basedir.exists() && basedir.isDirectory()) {
+            File gitConfig = new File(basedir, ".git/config");
+            if (gitConfig.isFile() && gitConfig.exists()) {
+                String text = FileHelper.loadText(new FileInputStream(gitConfig));
+                return extractGitUrl(text);
+            }
+        }
+        File parentFile = basedir.getParentFile();
+        if (parentFile != null) {
+            return extractGitUrl(parentFile);
+        }
+        return null;
+    }
+
+    /**
+     * Returns the remote git URL for the given git config file text lets extract the
+     */
+    private static String extractGitUrl(String configText) {
+        String remote = null;
+        String lastUrl = null;
+        String firstUrl = null;
+        BufferedReader reader = new BufferedReader(new StringReader(configText));
+        Map<String, String> remoteUrls = new HashMap<>();
+        while (true) {
+            String line = null;
+            try {
+                line = reader.readLine();
+            } catch (IOException e) {
+                // ignore should never happen!
+            }
+            if (line == null) {
+                break;
+            }
+            if (line.startsWith("[remote ")) {
+                String[] parts = line.split("\"");
+                if (parts.length > 1) {
+                    remote = parts[1];
+                }
+            } else if (line.startsWith("[")) {
+                remote = null;
+            } else if (remote != null && line.length() > 0 && Character.isWhitespace(line.charAt(0))) {
+                String trimmed = line.trim();
+                if (trimmed.startsWith("url ")) {
+                    String[] parts = trimmed.split("=", 2);
+                    if (parts.length > 1) {
+                        lastUrl = parts[1].trim();
+                        if (firstUrl == null) {
+                            firstUrl = lastUrl;
+                        }
+                        remoteUrls.put(remote, lastUrl);
+                    }
+                }
+
+            }
+        }
+        String answer = null;
+        if (remoteUrls.size() == 1) {
+            return lastUrl;
+        } else if (remoteUrls.size() > 1) {
+            answer = remoteUrls.get("origin");
+            if (answer == null) {
+                answer = firstUrl;
+            }
+        }
+        return answer;
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/c473d0b9/connectors/camel-connector-maven-plugin/src/main/java/org/apache/camel/maven/connector/JSonSchemaHelper.java
----------------------------------------------------------------------
diff --git a/connectors/camel-connector-maven-plugin/src/main/java/org/apache/camel/maven/connector/JSonSchemaHelper.java b/connectors/camel-connector-maven-plugin/src/main/java/org/apache/camel/maven/connector/JSonSchemaHelper.java
new file mode 100644
index 0000000..d42e5e3
--- /dev/null
+++ b/connectors/camel-connector-maven-plugin/src/main/java/org/apache/camel/maven/connector/JSonSchemaHelper.java
@@ -0,0 +1,356 @@
+/**
+ * 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.connector;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class JSonSchemaHelper {
+
+    private static final Pattern PATTERN = Pattern.compile("\"(.+?)\"|\\[(.+)\\]");
+    private static final String QUOT = "&quot;";
+
+    private JSonSchemaHelper() {
+    }
+
+    /**
+     * Parses the json schema to split it into a list or rows, where each row contains key value pairs with the metadata
+     *
+     * @param group the group to parse from such as <tt>component</tt>, <tt>componentProperties</tt>, or <tt>properties</tt>.
+     * @param json the json
+     * @return a list of all the rows, where each row is a set of key value pairs with metadata
+     */
+    public static List<Map<String, String>> parseJsonSchema(String group, String json, boolean parseProperties) {
+        List<Map<String, String>> answer = new ArrayList<Map<String, String>>();
+        if (json == null) {
+            return answer;
+        }
+
+        boolean found = false;
+
+        // parse line by line
+        String[] lines = json.split("\n");
+        for (String line : lines) {
+            // we need to find the group first
+            if (!found) {
+                String s = line.trim();
+                found = s.startsWith("\"" + group + "\":") && s.endsWith("{");
+                continue;
+            }
+
+            // we should stop when we end the group
+            if (line.equals("  },") || line.equals("  }")) {
+                break;
+            }
+
+            // need to safe encode \" so we can parse the line
+            line = line.replaceAll("\"\\\\\"\"", '"' + QUOT + '"');
+
+            Map<String, String> row = new LinkedHashMap<String, String>();
+            Matcher matcher = PATTERN.matcher(line);
+
+            String key;
+            if (parseProperties) {
+                // when parsing properties the first key is given as name, so the first parsed token is the value of the name
+                key = "name";
+            } else {
+                key = null;
+            }
+            while (matcher.find()) {
+                if (key == null) {
+                    key = matcher.group(1);
+                } else {
+                    String value = matcher.group(1);
+                    if (value == null) {
+                        value = matcher.group(2);
+                        // its an enum so strip out " and trim spaces after comma
+                        value = value.replaceAll("\"", "");
+                        value = value.replaceAll(", ", ",");
+                    }
+                    if (value != null) {
+                        value = value.trim();
+                        // decode
+                        value = value.replaceAll(QUOT, "\"");
+                        value = decodeJson(value);
+                    }
+                    row.put(key, value);
+                    // reset
+                    key = null;
+                }
+            }
+            if (!row.isEmpty()) {
+                answer.add(row);
+            }
+        }
+
+        return answer;
+    }
+
+    private static String decodeJson(String value) {
+        // json encodes a \ as \\ so we need to decode from \\ back to \
+        if ("\\\\".equals(value)) {
+            value = "\\";
+        }
+        return value;
+    }
+
+    public static boolean isComponentLenientProperties(List<Map<String, String>> rows) {
+        for (Map<String, String> row : rows) {
+            if (row.containsKey("lenientProperties")) {
+                return "true".equals(row.get("lenientProperties"));
+            }
+        }
+        return false;
+    }
+
+    public static boolean isPropertyRequired(List<Map<String, String>> rows, String name) {
+        for (Map<String, String> row : rows) {
+            boolean required = false;
+            boolean found = false;
+            if (row.containsKey("name")) {
+                found = name.equals(row.get("name"));
+            }
+            if (row.containsKey("required")) {
+                required = "true".equals(row.get("required"));
+            }
+            if (found) {
+                return required;
+            }
+        }
+        return false;
+    }
+
+    public static String getPropertyKind(List<Map<String, String>> rows, String name) {
+        for (Map<String, String> row : rows) {
+            String kind = null;
+            boolean found = false;
+            if (row.containsKey("name")) {
+                found = name.equals(row.get("name"));
+            }
+            if (row.containsKey("kind")) {
+                kind = row.get("kind");
+            }
+            if (found) {
+                return kind;
+            }
+        }
+        return null;
+    }
+
+    public static boolean isPropertyBoolean(List<Map<String, String>> rows, String name) {
+        for (Map<String, String> row : rows) {
+            String type = null;
+            boolean found = false;
+            if (row.containsKey("name")) {
+                found = name.equals(row.get("name"));
+            }
+            if (row.containsKey("type")) {
+                type = row.get("type");
+            }
+            if (found) {
+                return "boolean".equals(type);
+            }
+        }
+        return false;
+    }
+
+    public static boolean isPropertyInteger(List<Map<String, String>> rows, String name) {
+        for (Map<String, String> row : rows) {
+            String type = null;
+            boolean found = false;
+            if (row.containsKey("name")) {
+                found = name.equals(row.get("name"));
+            }
+            if (row.containsKey("type")) {
+                type = row.get("type");
+            }
+            if (found) {
+                return "integer".equals(type);
+            }
+        }
+        return false;
+    }
+
+    public static boolean isPropertyNumber(List<Map<String, String>> rows, String name) {
+        for (Map<String, String> row : rows) {
+            String type = null;
+            boolean found = false;
+            if (row.containsKey("name")) {
+                found = name.equals(row.get("name"));
+            }
+            if (row.containsKey("type")) {
+                type = row.get("type");
+            }
+            if (found) {
+                return "number".equals(type);
+            }
+        }
+        return false;
+    }
+
+    public static boolean isPropertyObject(List<Map<String, String>> rows, String name) {
+        for (Map<String, String> row : rows) {
+            String type = null;
+            boolean found = false;
+            if (row.containsKey("name")) {
+                found = name.equals(row.get("name"));
+            }
+            if (row.containsKey("type")) {
+                type = row.get("type");
+            }
+            if (found) {
+                return "object".equals(type);
+            }
+        }
+        return false;
+    }
+
+    public static String getPropertyDefaultValue(List<Map<String, String>> rows, String name) {
+        for (Map<String, String> row : rows) {
+            String defaultValue = null;
+            boolean found = false;
+            if (row.containsKey("name")) {
+                found = name.equals(row.get("name"));
+            }
+            if (row.containsKey("defaultValue")) {
+                defaultValue = row.get("defaultValue");
+            }
+            if (found) {
+                return defaultValue;
+            }
+        }
+        return null;
+    }
+
+    public static String stripOptionalPrefixFromName(List<Map<String, String>> rows, String name) {
+        for (Map<String, String> row : rows) {
+            String optionalPrefix = null;
+            boolean found = false;
+            if (row.containsKey("optionalPrefix")) {
+                optionalPrefix = row.get("optionalPrefix");
+            }
+            if (row.containsKey("name")) {
+                if (optionalPrefix != null && name.startsWith(optionalPrefix)) {
+                    name = name.substring(optionalPrefix.length());
+                    // try again
+                    return stripOptionalPrefixFromName(rows, name);
+                } else {
+                    found = name.equals(row.get("name"));
+                }
+            }
+            if (found) {
+                return name;
+            }
+        }
+        return name;
+    }
+
+    public static String getPropertyEnum(List<Map<String, String>> rows, String name) {
+        for (Map<String, String> row : rows) {
+            String enums = null;
+            boolean found = false;
+            if (row.containsKey("name")) {
+                found = name.equals(row.get("name"));
+            }
+            if (row.containsKey("enum")) {
+                enums = row.get("enum");
+            }
+            if (found) {
+                return enums;
+            }
+        }
+        return null;
+    }
+
+    public static String getPropertyPrefix(List<Map<String, String>> rows, String name) {
+        for (Map<String, String> row : rows) {
+            String prefix = null;
+            boolean found = false;
+            if (row.containsKey("name")) {
+                found = name.equals(row.get("name"));
+            }
+            if (row.containsKey("prefix")) {
+                prefix = row.get("prefix");
+            }
+            if (found) {
+                return prefix;
+            }
+        }
+        return null;
+    }
+
+    public static boolean isPropertyMultiValue(List<Map<String, String>> rows, String name) {
+        for (Map<String, String> row : rows) {
+            boolean multiValue = false;
+            boolean found = false;
+            if (row.containsKey("name")) {
+                found = name.equals(row.get("name"));
+            }
+            if (row.containsKey("multiValue")) {
+                multiValue = "true".equals(row.get("multiValue"));
+            }
+            if (found) {
+                return multiValue;
+            }
+        }
+        return false;
+    }
+
+    public static String getPropertyNameFromNameWithPrefix(List<Map<String, String>> rows, String name) {
+        for (Map<String, String> row : rows) {
+            String propertyName = null;
+            boolean found = false;
+            if (row.containsKey("name")) {
+                propertyName = row.get("name");
+            }
+            if (row.containsKey("prefix")) {
+                String preifx = row.get("prefix");
+                found = name.startsWith(preifx);
+            }
+            if (found) {
+                return propertyName;
+            }
+        }
+        return null;
+    }
+
+    public static Map<String, String> getRow(List<Map<String, String>> rows, String key) {
+        for (Map<String, String> row : rows) {
+            if (key.equals(row.get("name"))) {
+                return row;
+            }
+        }
+        return null;
+    }
+
+    public static Set<String> getNames(List<Map<String, String>> rows) {
+        Set<String> answer = new LinkedHashSet<String>();
+        for (Map<String, String> row : rows) {
+            if (row.containsKey("name")) {
+                answer.add(row.get("name"));
+            }
+        }
+        return answer;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/c473d0b9/connectors/camel-connector-maven-plugin/src/main/resources/META-INF/MANIFEST.MF
----------------------------------------------------------------------
diff --git a/connectors/camel-connector-maven-plugin/src/main/resources/META-INF/MANIFEST.MF b/connectors/camel-connector-maven-plugin/src/main/resources/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..8428f49
--- /dev/null
+++ b/connectors/camel-connector-maven-plugin/src/main/resources/META-INF/MANIFEST.MF
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+Implementation-Title: Camel :: Connector :: Camel Connector Maven Plugin
+Implementation-Vendor: The Apache Software Foundation
+Implementation-Vendor-Id: org.apache.camel

http://git-wip-us.apache.org/repos/asf/camel/blob/c473d0b9/connectors/pom.xml
----------------------------------------------------------------------
diff --git a/connectors/pom.xml b/connectors/pom.xml
index 43e285d..acb3aef 100644
--- a/connectors/pom.xml
+++ b/connectors/pom.xml
@@ -33,6 +33,7 @@
 
   <modules>
     <module>camel-connector</module>
+    <module>camel-connector-maven-plugin</module>
   </modules>
 
 </project>

http://git-wip-us.apache.org/repos/asf/camel/blob/c473d0b9/parent/pom.xml
----------------------------------------------------------------------
diff --git a/parent/pom.xml b/parent/pom.xml
index 180a989..15c3330 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -425,6 +425,8 @@
     <maven-jar-plugin-version>2.6</maven-jar-plugin-version>
     <maven-javadoc-plugin-version>2.9.1</maven-javadoc-plugin-version>
     <maven-jboss-as-maven-plugin-version>7.7.Final</maven-jboss-as-maven-plugin-version>
+    <!-- plugin-plugin 3.5 does not work -->
+    <maven-plugin-plugin-version>3.4</maven-plugin-plugin-version>
     <maven-remote-resources-plugin-version>1.5</maven-remote-resources-plugin-version>
     <!-- resources plugin needed by Camel maven archetypes -->
     <maven-resources-plugin-version>3.0.1</maven-resources-plugin-version>