CAMEL-7999: Add component details to json
Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/7e0d92c6 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/7e0d92c6 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/7e0d92c6 Branch: refs/heads/master Commit: 7e0d92c64b631397026c25118c88de842c0e9ab5 Parents: fc3a01c Author: Claus Ibsen <davscl...@apache.org> Authored: Sun Nov 9 15:51:11 2014 +0100 Committer: Claus Ibsen <davscl...@apache.org> Committed: Sun Nov 9 16:09:10 2014 +0100 ---------------------------------------------------------------------- .../apache/camel/impl/DefaultCamelContext.java | 2 +- .../camel/management/mbean/ManagedEndpoint.java | 2 +- .../org/apache/camel/util/JsonSchemaHelper.java | 19 ++- .../tools/apt/EndpointAnnotationProcessor.java | 160 +++++++++++++++++-- .../org/apache/camel/tools/apt/IOHelper.java | 82 ++++++++++ 5 files changed, 246 insertions(+), 19 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/7e0d92c6/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java b/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java index 01cdefb..addcc63 100644 --- a/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java +++ b/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java @@ -1151,7 +1151,7 @@ public class DefaultCamelContext extends ServiceSupport implements ModelCamelCon return null; } - List<Map<String, String>> rows = JsonSchemaHelper.parseJsonSchema(json); + List<Map<String, String>> rows = JsonSchemaHelper.parseJsonSchema("properties", json); // selected rows to use for answer Map<String, String[]> selected = new LinkedHashMap<String, String[]>(); http://git-wip-us.apache.org/repos/asf/camel/blob/7e0d92c6/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedEndpoint.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedEndpoint.java b/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedEndpoint.java index e6ae3e3..7e55502 100644 --- a/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedEndpoint.java +++ b/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedEndpoint.java @@ -87,7 +87,7 @@ public class ManagedEndpoint implements ManagedInstance, ManagedEndpointMBean { public TabularData explain(boolean allOptions) { try { String json = endpoint.getCamelContext().explainEndpointJson(getEndpointUri(), allOptions); - List<Map<String, String>> rows = JsonSchemaHelper.parseJsonSchema(json); + List<Map<String, String>> rows = JsonSchemaHelper.parseJsonSchema("properties", json); TabularData answer = new TabularDataSupport(CamelOpenMBeanTypes.explainEndpointTabularType()); http://git-wip-us.apache.org/repos/asf/camel/blob/7e0d92c6/camel-core/src/main/java/org/apache/camel/util/JsonSchemaHelper.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/util/JsonSchemaHelper.java b/camel-core/src/main/java/org/apache/camel/util/JsonSchemaHelper.java index 8690939..4233a14 100644 --- a/camel-core/src/main/java/org/apache/camel/util/JsonSchemaHelper.java +++ b/camel-core/src/main/java/org/apache/camel/util/JsonSchemaHelper.java @@ -111,20 +111,31 @@ public final class 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 json) { + public static List<Map<String, String>> parseJsonSchema(String group, String json) { List<Map<String, String>> answer = new ArrayList<Map<String, String>>(); if (json == null) { return answer; } + boolean found = false; + // parse line by line - // skip first 2 lines as they are leading String[] lines = json.split("\n"); - for (int i = 2; i < lines.length; i++) { - String line = lines[i]; + for (String line : lines) { + // we need to find the group first + if (!found) { + found = line.startsWith(" \"" + group + "\":"); + continue; + } + + // we should stop when we end the group + if (line.equals(" },") || line.equals(" }")) { + break; + } Map<String, String> row = new LinkedHashMap<String, String>(); Matcher matcher = PATTERN.matcher(line); http://git-wip-us.apache.org/repos/asf/camel/blob/7e0d92c6/tooling/apt/src/main/java/org/apache/camel/tools/apt/EndpointAnnotationProcessor.java ---------------------------------------------------------------------- diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/EndpointAnnotationProcessor.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/EndpointAnnotationProcessor.java index ca85271..2149bed 100644 --- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/EndpointAnnotationProcessor.java +++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/EndpointAnnotationProcessor.java @@ -19,12 +19,15 @@ package org.apache.camel.tools.apt; import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.io.InputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; import java.net.URI; +import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.Filer; @@ -49,6 +52,8 @@ import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; import org.apache.camel.spi.UriParams; +import static org.apache.camel.tools.apt.IOHelper.loadText; +import static org.apache.camel.tools.apt.JsonSchemaHelper.sanitizeDescription; import static org.apache.camel.tools.apt.Strings.canonicalClassName; import static org.apache.camel.tools.apt.Strings.isNullOrEmpty; @@ -144,27 +149,36 @@ public class EndpointAnnotationProcessor extends AbstractProcessor { } protected void writeJSonSchemeDocumentation(PrintWriter writer, RoundEnvironment roundEnv, TypeElement classElement, UriEndpoint uriEndpoint) { + // gather component information String scheme = uriEndpoint.scheme(); - - String classDoc = processingEnv.getElementUtils().getDocComment(classElement); - if (!isNullOrEmpty(classDoc)) { - classDoc = JsonSchemaHelper.sanitizeDescription(classDoc); - } - - // TODO: include component meta-data such as scheme name, java class, mvn coordinate etc. - // also component summary, eg grab <description> from mvn pom.xml + ComponentModel componentModel = findComponentProperties(roundEnv, scheme); Set<EndpointOption> endpointOptions = new LinkedHashSet<>(); findClassProperties(roundEnv, endpointOptions, classElement, ""); if (!endpointOptions.isEmpty()) { - String json = createParameterJsonSchema(endpointOptions); + String json = createParameterJsonSchema(componentModel, endpointOptions); writer.println(json); } } - - public String createParameterJsonSchema(Set<EndpointOption> options) { - StringBuilder buffer = new StringBuilder("{\n \"properties\": {"); + public String createParameterJsonSchema(ComponentModel componentModel, Set<EndpointOption> options) { + StringBuilder buffer = new StringBuilder("{"); + // component model + buffer.append("\n \"component\": {"); + buffer.append("\n \"scheme\": \"" + componentModel.getScheme() + "\","); + buffer.append("\n \"description\": \"" + sanitizeDescription(componentModel.getDescription()) + "\","); + buffer.append("\n \"javaType\": \"" + componentModel.getJavaType() + "\","); + buffer.append("\n \"groupId\": \"" + componentModel.getGroupId() + "\","); + buffer.append("\n \"artifactId\": \"" + componentModel.getArtifactId() + "\","); + buffer.append("\n \"version\": \"" + componentModel.getVersionId() + "\""); + buffer.append("\n },"); + + // and empty component properties as placeholder for future improvement + buffer.append("\n \"componentProperties\": {"); + buffer.append("\n },"); + + // endpoint properties was named properties at first, and hence we stick with that naming to be compatible + buffer.append("\n \"properties\": {"); boolean first = true; for (EndpointOption entry : options) { if (first) { @@ -175,7 +189,9 @@ public class EndpointAnnotationProcessor extends AbstractProcessor { buffer.append("\n "); buffer.append(JsonSchemaHelper.toJson(entry.getName(), entry.getType(), entry.getDefaultValue(), entry.getDocumentationWithNotes(), entry.isEnumType(), entry.getEnums())); } - buffer.append("\n }\n}\n"); + buffer.append("\n }"); + + buffer.append("\n}\n"); return buffer.toString(); } @@ -212,6 +228,28 @@ public class EndpointAnnotationProcessor extends AbstractProcessor { } } + protected ComponentModel findComponentProperties(RoundEnvironment roundEnv, String scheme) { + ComponentModel model = new ComponentModel(scheme); + + String data = loadResource("META-INF/services/org/apache/camel/component", scheme); + if (data != null) { + Map<String, String> map = parseAsMap(data); + model.setJavaType(map.get("class")); + } + + data = loadResource("META-INF/services/org/apache/camel", "component.properties"); + if (data != null) { + Map<String, String> map = parseAsMap(data); + // now we have a lot more data, so we need to load it as key/value + model.setDescription(map.get("projectDescription")); + model.setGroupId(map.get("groupId")); + model.setArtifactId(map.get("artifactId")); + model.setVersionId(map.get("version")); + } + + return model; + } + protected void findClassProperties(RoundEnvironment roundEnv, Set<EndpointOption> endpointOptions, TypeElement classElement, String prefix) { Elements elementUtils = processingEnv.getElementUtils(); while (true) { @@ -361,6 +399,44 @@ public class EndpointAnnotationProcessor extends AbstractProcessor { } } + protected String loadResource(String packageName, String fileName) { + Filer filer = processingEnv.getFiler(); + + FileObject resource; + try { + resource = filer.getResource(StandardLocation.CLASS_OUTPUT, "", packageName + "/" + fileName); + } catch (Throwable e) { + return "Crap" + e.getMessage(); + } + + if (resource == null) { + return null; + } + + try { + InputStream is = resource.openInputStream(); + return loadText(is, true); + } catch (Exception e) { + warning("Could not load file"); + } + + return null; + } + + protected Map<String, String> parseAsMap(String data) { + Map<String, String> answer = new HashMap<String, String>(); + String[] lines = data.split("\n"); + for (String line : lines) { + int idx = line.indexOf('='); + String key = line.substring(0, idx); + String value = line.substring(idx + 1); + // remove ending line break for the values + value = value.trim().replaceAll("\n", ""); + answer.put(key.trim(), value); + } + return answer; + } + protected void log(String message) { processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, message); } @@ -382,6 +458,64 @@ public class EndpointAnnotationProcessor extends AbstractProcessor { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, buffer.toString()); } + private static final class ComponentModel { + + private String scheme; + private String javaType; + private String description; + private String groupId; + private String artifactId; + private String versionId; + + private ComponentModel(String scheme) { + this.scheme = scheme; + } + + public String getScheme() { + return scheme; + } + + public String getJavaType() { + return javaType; + } + + public void setJavaType(String javaType) { + this.javaType = javaType; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + 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 getVersionId() { + return versionId; + } + + public void setVersionId(String versionId) { + this.versionId = versionId; + } + } + private static final class EndpointOption { private String name; http://git-wip-us.apache.org/repos/asf/camel/blob/7e0d92c6/tooling/apt/src/main/java/org/apache/camel/tools/apt/IOHelper.java ---------------------------------------------------------------------- diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/IOHelper.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/IOHelper.java new file mode 100644 index 0000000..e6c764a --- /dev/null +++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/IOHelper.java @@ -0,0 +1,82 @@ +/** + * 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.tools.apt; + +import java.io.BufferedReader; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +class IOHelper { + + private IOHelper() { + } + + /** + * 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, boolean skipCommentOrEmptyLines) throws IOException { + StringBuilder builder = new StringBuilder(); + InputStreamReader isr = new InputStreamReader(in); + try { + BufferedReader reader = new BufferedReader(isr); + while (true) { + String line = reader.readLine(); + if (line != null) { + + boolean empty = Strings.isNullOrEmpty(line); + boolean comment = line.trim().startsWith("#"); + if (skipCommentOrEmptyLines && (empty || comment)) { + continue; + } + + builder.append(line); + builder.append("\n"); + } else { + break; + } + } + return builder.toString(); + } finally { + close(isr, in); + } + } + + /** + * Closes the given resources if they are available. + * + * @param closeables the objects to close + */ + public static void close(Closeable... closeables) { + for (Closeable closeable : closeables) { + try { + closeable.close(); + } catch (IOException e) { + // ignore + } + } + } + + + +}