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 <davscl...@apache.org> Authored: Wed Jan 18 14:27:14 2017 +0100 Committer: Claus Ibsen <davscl...@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 = """; + + 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>