CAMEL-10774: Generate other json schema for Camel artifacts which is not a component, dataformat or language
Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/490d49e0 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/490d49e0 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/490d49e0 Branch: refs/heads/master Commit: 490d49e001dec0e8fdcba7803f4a89d9c7f717fe Parents: 41200e2 Author: Claus Ibsen <davscl...@apache.org> Authored: Fri Feb 3 18:03:36 2017 +0100 Committer: Claus Ibsen <davscl...@apache.org> Committed: Fri Feb 3 20:31:28 2017 +0100 ---------------------------------------------------------------------- components/camel-eclipse/pom.xml | 16 +- .../camel/maven/packaging/PackageOtherMojo.java | 187 ++++++++++++++- .../maven/packaging/PrepareCatalogMojo.java | 235 ++++++++++++++++++- .../maven/packaging/PrepareComponentMojo.java | 2 +- .../camel/maven/packaging/StringHelper.java | 29 +++ 5 files changed, 454 insertions(+), 15 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/490d49e0/components/camel-eclipse/pom.xml ---------------------------------------------------------------------- diff --git a/components/camel-eclipse/pom.xml b/components/camel-eclipse/pom.xml index 85146da..4a86c18 100644 --- a/components/camel-eclipse/pom.xml +++ b/components/camel-eclipse/pom.xml @@ -30,12 +30,16 @@ <description>Camel Eclipse support</description> <properties> - <camel.osgi.export.pkg>org.apache.camel.component.eclipse.*</camel.osgi.export.pkg> - <camel.osgi.import.pkg> - !org.apache.camel.component.eclipse.*, - org.apache.camel.*;${camel.osgi.import.camel.version}, - org.eclipse.core.runtime;common=! - </camel.osgi.import.pkg> + <!-- use by camel-catalog --> + <firstVersion>2.3.0</firstVersion> + <label>tooling</label> + + <camel.osgi.export.pkg>org.apache.camel.component.eclipse.*</camel.osgi.export.pkg> + <camel.osgi.import.pkg> + !org.apache.camel.component.eclipse.*, + org.apache.camel.*;${camel.osgi.import.camel.version}, + org.eclipse.core.runtime;common=! + </camel.osgi.import.pkg> </properties> <dependencies> http://git-wip-us.apache.org/repos/asf/camel/blob/490d49e0/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageOtherMojo.java ---------------------------------------------------------------------- diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageOtherMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageOtherMojo.java index f061947..87e99ed 100644 --- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageOtherMojo.java +++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageOtherMojo.java @@ -33,6 +33,8 @@ import org.apache.maven.project.MavenProject; import org.apache.maven.project.MavenProjectHelper; import org.sonatype.plexus.build.incremental.BuildContext; +import static org.apache.camel.maven.packaging.StringHelper.camelDashToTitle; + /** * Analyses the Camel plugins in a project and generates extra descriptor information for easier auto-discovery in Camel. * @@ -57,6 +59,13 @@ public class PackageOtherMojo extends AbstractMojo { protected File otherOutDir; /** + * The output directory for generated languages file + * + * @parameter default-value="${project.build.directory}/classes" + */ + protected File schemaOutDir; + + /** * Maven ProjectHelper. * * @component @@ -81,11 +90,11 @@ public class PackageOtherMojo extends AbstractMojo { * @throws MojoFailureException something bad happened... */ public void execute() throws MojoExecutionException, MojoFailureException { - prepareOthers(getLog(), project, projectHelper, otherOutDir, buildContext); + prepareOthers(getLog(), project, projectHelper, otherOutDir, schemaOutDir, buildContext); } - public static void prepareOthers(Log log, MavenProject project, MavenProjectHelper projectHelper, - File otherOutDir, BuildContext buildContext) throws MojoExecutionException { + public static void prepareOthers(Log log, MavenProject project, MavenProjectHelper projectHelper, File otherOutDir, + File schemaOutDir, BuildContext buildContext) throws MojoExecutionException { // are there any components, data formats or languages? for (Resource r : project.getBuild().getResources()) { @@ -121,9 +130,57 @@ public class PackageOtherMojo extends AbstractMojo { return; } + String name = project.getArtifactId(); + // strip leading camel- + if (name.startsWith("camel-")) { + name = name.substring(6); + } + + try { + // create json model + OtherModel otherModel = new OtherModel(); + otherModel.setName(name); + otherModel.setGroupId(project.getGroupId()); + otherModel.setArtifactId(project.getArtifactId()); + otherModel.setVersion(project.getVersion()); + otherModel.setDescription(project.getDescription()); + if (project.getName() != null && project.getName().contains("(deprecated)")) { + otherModel.setDeprecated("true"); + } else { + otherModel.setDeprecated("false"); + } + otherModel.setFirstVersion(project.getProperties().getProperty("firstVersion")); + otherModel.setLabel(project.getProperties().getProperty("label")); + String title = project.getProperties().getProperty("title"); + if (title == null) { + title = camelDashToTitle(name); + } + otherModel.setTitle(title); + + log.debug("Model " + otherModel); + + // write this to the directory + File dir = schemaOutDir; + dir.mkdirs(); + + File out = new File(dir, name + ".json"); + OutputStream fos = buildContext.newFileOutputStream(out); + String json = createJsonSchema(otherModel); + fos.write(json.getBytes()); + fos.close(); + + buildContext.refresh(out); + + log.debug("Generated " + out + " containing JSon schema for " + name + " other"); + } catch (Exception e) { + throw new MojoExecutionException("Error loading language model from camel-core. Reason: " + e, e); + } + + // now create properties file File camelMetaDir = new File(otherOutDir, "META-INF/services/org/apache/camel/"); Properties properties = new Properties(); + properties.put("name", name); properties.put("groupId", project.getGroupId()); properties.put("artifactId", project.getArtifactId()); properties.put("version", project.getVersion()); @@ -167,4 +224,128 @@ public class PackageOtherMojo extends AbstractMojo { } } + private static String createJsonSchema(OtherModel otherModel) { + StringBuilder buffer = new StringBuilder("{"); + // language model + buffer.append("\n \"other\": {"); + buffer.append("\n \"name\": \"").append(otherModel.getName()).append("\","); + buffer.append("\n \"kind\": \"").append("other").append("\","); + if (otherModel.getTitle() != null) { + buffer.append("\n \"title\": \"").append(otherModel.getTitle()).append("\","); + } + if (otherModel.getDescription() != null) { + buffer.append("\n \"description\": \"").append(otherModel.getDescription()).append("\","); + } + buffer.append("\n \"deprecated\": \"").append(otherModel.getDeprecated()).append("\","); + if (otherModel.getFirstVersion() != null) { + buffer.append("\n \"firstVersion\": \"").append(otherModel.getFirstVersion()).append("\","); + } + if (otherModel.getLabel() != null) { + buffer.append("\n \"label\": \"").append(otherModel.getLabel()).append("\","); + } + buffer.append("\n \"groupId\": \"").append(otherModel.getGroupId()).append("\","); + buffer.append("\n \"artifactId\": \"").append(otherModel.getArtifactId()).append("\","); + buffer.append("\n \"version\": \"").append(otherModel.getVersion()).append("\""); + buffer.append("\n }"); + buffer.append("\n}"); + return buffer.toString(); + } + + private static class OtherModel { + private String name; + private String title; + private String description; + private String deprecated; + private String firstVersion; + private String label; + private String groupId; + private String artifactId; + private String version; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getDeprecated() { + return deprecated; + } + + public void setDeprecated(String deprecated) { + this.deprecated = deprecated; + } + + public String getFirstVersion() { + return firstVersion; + } + + public void setFirstVersion(String firstVersion) { + this.firstVersion = firstVersion; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public String getArtifactId() { + return artifactId; + } + + public void setArtifactId(String artifactId) { + this.artifactId = artifactId; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + @Override + public String toString() { + return "OtherModel[" + + "name='" + name + '\'' + + ", title='" + title + '\'' + + ", description='" + description + '\'' + + ", label='" + label + '\'' + + ", groupId='" + groupId + '\'' + + ", artifactId='" + artifactId + '\'' + + ", version='" + version + '\'' + + ']'; + } + } + } http://git-wip-us.apache.org/repos/asf/camel/blob/490d49e0/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCatalogMojo.java ---------------------------------------------------------------------- diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCatalogMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCatalogMojo.java index 62786ff..2728c57 100644 --- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCatalogMojo.java +++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCatalogMojo.java @@ -105,6 +105,13 @@ public class PrepareCatalogMojo extends AbstractMojo { protected File languagesOutDir; /** + * The output directory for others catalog + * + * @parameter default-value="${project.build.directory}/classes/org/apache/camel/catalog/others" + */ + protected File othersOutDir; + + /** * The output directory for documents catalog * * @parameter default-value="${project.build.directory}/classes/org/apache/camel/catalog/docs" @@ -194,7 +201,8 @@ public class PrepareCatalogMojo extends AbstractMojo { Set<String> components = executeComponents(); Set<String> dataformats = executeDataFormats(); Set<String> languages = executeLanguages(); - executeDocuments(components, dataformats, languages); + Set<String> others = executeOthers(); + executeDocuments(components, dataformats, languages, others); executeArchetypes(); executeXmlSchemas(); } @@ -763,6 +771,135 @@ public class PrepareCatalogMojo extends AbstractMojo { return answer; } + private Set<String> executeOthers() throws MojoFailureException { + getLog().info("Copying all Camel other json descriptors"); + + // lets use sorted set/maps + Set<File> jsonFiles = new TreeSet<File>(); + Set<File> duplicateJsonFiles = new TreeSet<File>(); + Set<File> otherFiles = new TreeSet<File>(); + Map<String, Set<String>> usedLabels = new TreeMap<String, Set<String>>(); + Set<File> missingFirstVersions = new TreeSet<File>(); + + // find all others from the components directory + if (componentsDir != null && componentsDir.isDirectory()) { + File[] others = componentsDir.listFiles(); + if (others != null) { + for (File dir : others) { + + // skip these special cases + // (camel-jetty is a placeholder, as camel-jetty9 is the actual component) + if ("camel-core-osgi".equals(dir.getName()) + || "camel-core-xml".equals(dir.getName()) + || "camel-http-common".equals(dir.getName()) + || "camel-jetty".equals(dir.getName()) + || "camel-jetty-common".equals(dir.getName()) + || "camel-linkedin".equals(dir.getName()) + || "camel-olingo2".equals(dir.getName()) + || "camel-salesforce".equals(dir.getName())) { + continue; + } + + if (dir.isDirectory() && !"target".equals(dir.getName())) { + File target = new File(dir, "target/classes"); + findOtherFilesRecursive(target, jsonFiles, otherFiles, new CamelOthersFileFilter()); + } + } + } + } + // nothing in camel-core + + getLog().info("Found " + otherFiles.size() + " other.properties files"); + getLog().info("Found " + jsonFiles.size() + " other json files"); + + // make sure to create out dir + othersOutDir.mkdirs(); + + for (File file : jsonFiles) { + File to = new File(othersOutDir, file.getName()); + if (to.exists()) { + duplicateJsonFiles.add(to); + getLog().warn("Duplicate other name detected: " + to); + } + try { + copyFile(file, to); + } catch (IOException e) { + throw new MojoFailureException("Cannot copy file from " + file + " -> " + to, e); + } + + // check if we have a label as we want the other to include labels + try { + String text = loadText(new FileInputStream(file)); + String name = asComponentName(file); + Matcher matcher = LABEL_PATTERN.matcher(text); + // grab the label, and remember it in the used labels + if (matcher.find()) { + String label = matcher.group(1); + String[] labels = label.split(","); + for (String s : labels) { + Set<String> others = usedLabels.get(s); + if (others == null) { + others = new TreeSet<String>(); + usedLabels.put(s, others); + } + others.add(name); + } + } + + // detect missing first version + List<Map<String, String>> rows = JSonSchemaHelper.parseJsonSchema("other", text, false); + String firstVersion = null; + for (Map<String, String> row : rows) { + if (row.get("firstVersion") != null) { + firstVersion = row.get("firstVersion"); + } + } + if (firstVersion == null) { + missingFirstVersions.add(file); + } + + } catch (IOException e) { + // ignore + } + } + + Set<String> answer = new LinkedHashSet<>(); + + File all = new File(othersOutDir, "../others.properties"); + try { + FileOutputStream fos = new FileOutputStream(all, false); + + String[] names = othersOutDir.list(); + List<String> others = new ArrayList<String>(); + // sort the names + for (String name : names) { + if (name.endsWith(".json")) { + // strip out .json from the name + String otherName = name.substring(0, name.length() - 5); + others.add(otherName); + } + } + + Collections.sort(others); + for (String name : others) { + fos.write(name.getBytes()); + fos.write("\n".getBytes()); + + // remember other name + answer.add(name); + } + + fos.close(); + + } catch (IOException e) { + throw new MojoFailureException("Error writing to file " + all); + } + + printOthersReport(jsonFiles, duplicateJsonFiles, usedLabels, missingFirstVersions); + + return answer; + } + protected void executeArchetypes() throws MojoExecutionException, MojoFailureException { getLog().info("Copying Archetype Catalog"); @@ -807,7 +944,7 @@ public class PrepareCatalogMojo extends AbstractMojo { } } - protected void executeDocuments(Set<String> components, Set<String> dataformats, Set<String> languages) throws MojoExecutionException, MojoFailureException { + protected void executeDocuments(Set<String> components, Set<String> dataformats, Set<String> languages, Set<String> others) throws MojoExecutionException, MojoFailureException { getLog().info("Copying all Camel documents (ascii docs)"); // lets use sorted set/maps @@ -928,11 +1065,11 @@ public class PrepareCatalogMojo extends AbstractMojo { printDocumentsReport(adocFiles, duplicateAdocFiles, missingAdocFiles); - // find out if we have documents for each component / dataformat / languages - printMissingDocumentsReport(docs, components, dataformats, languages); + // find out if we have documents for each component / dataformat / languages / others + printMissingDocumentsReport(docs, components, dataformats, languages, others); } - private void printMissingDocumentsReport(Set<String> docs, Set<String> components, Set<String> dataformats, Set<String> languages) { + private void printMissingDocumentsReport(Set<String> docs, Set<String> components, Set<String> dataformats, Set<String> languages, Set<String> others) { getLog().info(""); getLog().info("Camel missing documents report"); getLog().info(""); @@ -993,6 +1130,21 @@ public class PrepareCatalogMojo extends AbstractMojo { } missing.clear(); + for (String other : others) { + String name = other; + if (!docs.contains(name)) { + missing.add(name); + } + } + if (!missing.isEmpty()) { + getLog().info(""); + getLog().warn("\tMissing .adoc other documentation : " + missing.size()); + for (String name : missing) { + getLog().warn("\t\t" + name); + } + } + missing.clear(); + getLog().info(""); getLog().info("================================================================================"); } @@ -1175,6 +1327,43 @@ public class PrepareCatalogMojo extends AbstractMojo { getLog().info("================================================================================"); } + private void printOthersReport(Set<File> json, Set<File> duplicate, Map<String, Set<String>> usedLabels, Set<File> missingFirstVersions) { + getLog().info("================================================================================"); + getLog().info(""); + getLog().info("Camel other catalog report"); + getLog().info(""); + getLog().info("\tOthers found: " + json.size()); + for (File file : json) { + getLog().info("\t\t" + asComponentName(file)); + } + if (!duplicate.isEmpty()) { + getLog().info(""); + getLog().warn("\tDuplicate other detected: " + duplicate.size()); + for (File file : duplicate) { + getLog().warn("\t\t" + asComponentName(file)); + } + } + if (!usedLabels.isEmpty()) { + getLog().info(""); + getLog().info("\tUsed labels: " + usedLabels.size()); + for (Map.Entry<String, Set<String>> entry : usedLabels.entrySet()) { + getLog().info("\t\t" + entry.getKey() + ":"); + for (String name : entry.getValue()) { + getLog().info("\t\t\t" + name); + } + } + } + if (!missingFirstVersions.isEmpty()) { + getLog().info(""); + getLog().warn("\tOthers without firstVersion defined: " + missingFirstVersions.size()); + for (File name : missingFirstVersions) { + getLog().warn("\t\t" + name.getName()); + } + } + getLog().info(""); + getLog().info("================================================================================"); + } + private void printDocumentsReport(Set<File> docs, Set<File> duplicate, Set<File> missing) { getLog().info("================================================================================"); getLog().info(""); @@ -1268,6 +1457,25 @@ public class PrepareCatalogMojo extends AbstractMojo { } } + private void findOtherFilesRecursive(File dir, Set<File> found, Set<File> others, FileFilter filter) { + File[] files = dir.listFiles(filter); + if (files != null) { + for (File file : files) { + // skip files in root dirs as Camel does not store information there but others may do + boolean rootDir = "classes".equals(dir.getName()) || "META-INF".equals(dir.getName()); + boolean jsonFile = rootDir && file.isFile() && file.getName().endsWith(".json"); + boolean otherFile = !rootDir && file.isFile() && file.getName().equals("other.properties"); + if (jsonFile) { + found.add(file); + } else if (otherFile) { + others.add(file); + } else if (file.isDirectory()) { + findOtherFilesRecursive(file, found, others, filter); + } + } + } + } + private void findAsciiDocFilesRecursive(File dir, Set<File> found, FileFilter filter) { File[] files = dir.listFiles(filter); if (files != null) { @@ -1347,6 +1555,23 @@ public class PrepareCatalogMojo extends AbstractMojo { } } + private class CamelOthersFileFilter implements FileFilter { + + @Override + public boolean accept(File pathname) { + if (pathname.isFile() && pathname.getName().endsWith(".json")) { + // must be a language json file + try { + String json = loadText(new FileInputStream(pathname)); + return json != null && json.contains("\"kind\": \"other\""); + } catch (IOException e) { + // ignore + } + } + return pathname.isDirectory() || (pathname.isFile() && pathname.getName().equals("other.properties")); + } + } + private class CamelAsciiDocFileFilter implements FileFilter { @Override http://git-wip-us.apache.org/repos/asf/camel/blob/490d49e0/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareComponentMojo.java ---------------------------------------------------------------------- diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareComponentMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareComponentMojo.java index 7d5c8ed..5b99b9a 100644 --- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareComponentMojo.java +++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareComponentMojo.java @@ -116,7 +116,7 @@ public class PrepareComponentMojo extends AbstractMojo { prepareComponent(getLog(), project, projectHelper, componentOutDir, buildContext); prepareDataFormat(getLog(), project, projectHelper, dataFormatOutDir, schemaOutDir, buildContext); prepareLanguage(getLog(), project, projectHelper, languageOutDir, schemaOutDir, buildContext); - prepareOthers(getLog(), project, projectHelper, otherOutDir, buildContext); + prepareOthers(getLog(), project, projectHelper, otherOutDir, schemaOutDir, buildContext); } } http://git-wip-us.apache.org/repos/asf/camel/blob/490d49e0/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/StringHelper.java ---------------------------------------------------------------------- diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/StringHelper.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/StringHelper.java index e17f430..b65ada8 100644 --- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/StringHelper.java +++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/StringHelper.java @@ -58,4 +58,33 @@ public final class StringHelper { return sb.toString(); } + /** + * Converts the value to use title style instead of dash cased + */ + public static String camelDashToTitle(String value) { + StringBuilder sb = new StringBuilder(value.length()); + boolean dash = false; + + for (char c : value.toCharArray()) { + if ('-' == c) { + dash = true; + continue; + } + + if (dash) { + sb.append(' '); + sb.append(Character.toUpperCase(c)); + } else { + // upper case first + if (sb.length() == 0) { + sb.append(Character.toUpperCase(c)); + } else { + sb.append(c); + } + } + dash = false; + } + return sb.toString(); + } + }