This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch rest-dsl-parser in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/rest-dsl-parser by this push: new ef12cea CAMEL-12824: camel-route-parser - Add parser for rest-dsl ef12cea is described below commit ef12cea3b75a04c344e5d86e3ab12890163adde4 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Sat Sep 22 11:44:16 2018 +0200 CAMEL-12824: camel-route-parser - Add parser for rest-dsl --- .../org/apache/camel/parser/RestDslParser.java | 39 ++++- .../helper/CamelJavaRestDslParserHelper.java | 166 +++++++++++++++++++ .../camel/parser/model/RestServiceDetails.java | 180 +++++++++++++++++++++ .../apache/camel/parser/model/RestVerbDetails.java | 161 ++++++++++++++++++ .../camel/parser/java/MyRestDslRouteBuilder.java | 10 +- .../camel/parser/java/RoasterJavaRestDslTest.java | 25 ++- 6 files changed, 575 insertions(+), 6 deletions(-) diff --git a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RestDslParser.java b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RestDslParser.java index e73bbe9..6703b76 100644 --- a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RestDslParser.java +++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RestDslParser.java @@ -23,6 +23,7 @@ import java.util.List; import org.apache.camel.parser.helper.CamelJavaParserHelper; import org.apache.camel.parser.helper.CamelJavaRestDslParserHelper; import org.apache.camel.parser.model.RestConfigurationDetails; +import org.apache.camel.parser.model.RestServiceDetails; import org.jboss.forge.roaster.model.source.JavaClassSource; import org.jboss.forge.roaster.model.source.MethodSource; @@ -33,8 +34,6 @@ import org.jboss.forge.roaster.model.source.MethodSource; */ public final class RestDslParser { - // TODO: add support for rest services (eg rest().get() ...) - private RestDslParser() { } @@ -74,4 +73,40 @@ public final class RestDslParser { return list; } + /** + * Parses the java source class and build a rest service model of the discovered rest services in the java source class. + * + * @param clazz the java source class + * @param baseDir the base of the source code + * @param fullyQualifiedFileName the fully qualified source code file name + * @return a list of rest services + */ + public static List<RestServiceDetails> parseRestService(JavaClassSource clazz, String baseDir, String fullyQualifiedFileName, + boolean includeInlinedRouteBuilders) { + + List<MethodSource<JavaClassSource>> methods = new ArrayList<>(); + MethodSource<JavaClassSource> method = CamelJavaParserHelper.findConfigureMethod(clazz); + if (method != null) { + methods.add(method); + } + if (includeInlinedRouteBuilders) { + List<MethodSource<JavaClassSource>> inlinedMethods = CamelJavaParserHelper.findInlinedConfigureMethods(clazz); + if (!inlinedMethods.isEmpty()) { + methods.addAll(inlinedMethods); + } + } + + CamelJavaRestDslParserHelper parser = new CamelJavaRestDslParserHelper(); + List<RestServiceDetails> list = new ArrayList<>(); + for (MethodSource<JavaClassSource> configureMethod : methods) { + // there may be multiple route builder configure methods + List<RestServiceDetails> details = parser.parseRestService(clazz, baseDir, fullyQualifiedFileName, configureMethod); + list.addAll(details); + } + // we end up parsing bottom->up so reverse list + Collections.reverse(list); + + return list; + } + } diff --git a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelJavaRestDslParserHelper.java b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelJavaRestDslParserHelper.java index d111a84..e498248 100644 --- a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelJavaRestDslParserHelper.java +++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelJavaRestDslParserHelper.java @@ -20,9 +20,12 @@ import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.apache.camel.parser.model.RestConfigurationDetails; +import org.apache.camel.parser.model.RestServiceDetails; +import org.apache.camel.parser.model.RestVerbDetails; import org.apache.camel.parser.roaster.StatementFieldSource; import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.ASTNode; import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.AnonymousClassDeclaration; @@ -101,6 +104,55 @@ public final class CamelJavaRestDslParserHelper { return answer; } + public List<RestServiceDetails> parseRestService(JavaClassSource clazz, String baseDir, String fullyQualifiedFileName, + MethodSource<JavaClassSource> configureMethod) { + + List<RestServiceDetails> answer = new ArrayList<>(); + + if (configureMethod != null) { + MethodDeclaration md = (MethodDeclaration) configureMethod.getInternal(); + Block block = md.getBody(); + if (block != null) { + for (Object statement : md.getBody().statements()) { + // must be a method call expression + if (statement instanceof ExpressionStatement) { + ExpressionStatement es = (ExpressionStatement) statement; + Expression exp = es.getExpression(); + boolean valid = isRest(exp); + if (valid) { + RestServiceDetails node = new RestServiceDetails(); + answer.add(node); + + // include source code details + int pos = exp.getStartPosition(); + int line = findLineNumber(fullyQualifiedFileName, pos); + if (line > -1) { + node.setLineNumber("" + line); + } + pos = exp.getStartPosition() + exp.getLength(); + line = findLineNumber(fullyQualifiedFileName, pos); + if (line > -1) { + node.setLineNumberEnd("" + line); + } + node.setFileName(fullyQualifiedFileName); + node.setClassName(clazz.getQualifiedName()); + node.setMethodName(configureMethod.getName()); + + parseExpression(node, fullyQualifiedFileName, clazz, configureMethod, block, exp); + + // flip order of verbs as we parse bottom-up + if (node.getVerbs() != null) { + Collections.reverse(node.getVerbs()); + } + } + } + } + } + } + + return answer; + } + private boolean isRestConfiguration(Expression exp) { String rootMethodName = null; @@ -125,6 +177,30 @@ public final class CamelJavaRestDslParserHelper { return "restConfiguration".equals(rootMethodName); } + private boolean isRest(Expression exp) { + String rootMethodName = null; + + // find out if this is from a Camel route (eg from, route etc.) + Expression sub = exp; + while (sub instanceof MethodInvocation) { + sub = ((MethodInvocation) sub).getExpression(); + if (sub instanceof MethodInvocation) { + Expression parent = ((MethodInvocation) sub).getExpression(); + if (parent == null) { + break; + } + } + } + if (sub instanceof MethodInvocation) { + rootMethodName = ((MethodInvocation) sub).getName().getIdentifier(); + } else if (sub instanceof SimpleName) { + rootMethodName = ((SimpleName) sub).getIdentifier(); + } + + // must be from rest + return "rest".equals(rootMethodName); + } + private void parseExpression(RestConfigurationDetails node, String fullyQualifiedFileName, JavaClassSource clazz, MethodSource<JavaClassSource> configureMethod, Block block, Expression exp) { @@ -140,6 +216,21 @@ public final class CamelJavaRestDslParserHelper { } } + private void parseExpression(RestServiceDetails node, String fullyQualifiedFileName, + JavaClassSource clazz, MethodSource<JavaClassSource> configureMethod, Block block, + Expression exp) { + if (exp == null) { + return; + } + if (exp instanceof MethodInvocation) { + MethodInvocation mi = (MethodInvocation) exp; + doParseRestService(node, fullyQualifiedFileName, clazz, configureMethod, block, mi); + // if the method was called on another method, then recursive + exp = mi.getExpression(); + parseExpression(node, fullyQualifiedFileName, clazz, configureMethod, block, exp); + } + } + private void doParseRestConfiguration(RestConfigurationDetails node, String fullyQualifiedFileName, JavaClassSource clazz, MethodSource<JavaClassSource> configureMethod, Block block, MethodInvocation mi) { @@ -223,6 +314,81 @@ public final class CamelJavaRestDslParserHelper { } } + private void doParseRestService(RestServiceDetails node, String fullyQualifiedFileName, + JavaClassSource clazz, MethodSource<JavaClassSource> configureMethod, Block block, + MethodInvocation mi) { + + // end line number is the first node in the method chain we parse + if (node.getLineNumberEnd() == null) { + int pos = mi.getStartPosition() + mi.getLength(); + int line = findLineNumber(fullyQualifiedFileName, pos); + if (line > -1) { + node.setLineNumberEnd("" + line); + } + } + + String name = mi.getName().getIdentifier(); + if ("rest".equals(name)) { + node.setPath(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("delete".equals(name)) { + RestVerbDetails verb = new RestVerbDetails(); + node.addVerb(verb); + verb.setMethod("delete"); + verb.setUri(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("get".equals(name)) { + RestVerbDetails verb = new RestVerbDetails(); + node.addVerb(verb); + verb.setMethod("get"); + verb.setUri(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("head".equals(name)) { + RestVerbDetails verb = new RestVerbDetails(); + node.addVerb(verb); + verb.setMethod("head"); + verb.setUri(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("patch".equals(name)) { + RestVerbDetails verb = new RestVerbDetails(); + node.addVerb(verb); + verb.setMethod("patch"); + verb.setUri(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("post".equals(name)) { + RestVerbDetails verb = new RestVerbDetails(); + node.addVerb(verb); + verb.setMethod("post"); + verb.setUri(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("put".equals(name)) { + RestVerbDetails verb = new RestVerbDetails(); + node.addVerb(verb); + verb.setMethod("put"); + verb.setUri(extractValueFromFirstArgument(clazz, block, mi)); + } else { + if (isParentMethod(mi, "rest")) { + // okay its within the rest block, so then find out if its within one of the verbs + // TODO: implement me + } + } + } + + private static boolean isParentMethod(MethodInvocation mi, String parentName) { + String name = mi.getName().getIdentifier(); + if (parentName.equals(name)) { + return true; + } + + // find out if this is from a Camel route (eg from, route etc.) + Expression sub = mi; + while (sub instanceof MethodInvocation) { + sub = ((MethodInvocation) sub).getExpression(); + if (sub instanceof MethodInvocation) { + name = ((MethodInvocation) sub).getName().getIdentifier(); + if (parentName.equals(name)) { + return true; + } + } + } + + return false; + } + private static String extractValueFromFirstArgument(JavaClassSource clazz, Block block, MethodInvocation mi) { List args = mi.arguments(); if (args != null && args.size() > 0) { diff --git a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/RestServiceDetails.java b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/RestServiceDetails.java new file mode 100644 index 0000000..58d30bf --- /dev/null +++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/RestServiceDetails.java @@ -0,0 +1,180 @@ +/** + * 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.parser.model; + +import java.util.ArrayList; +import java.util.List; + +public class RestServiceDetails { + + // source code details + private String fileName; + private String lineNumber; + private String lineNumberEnd; + private int linePosition; + + // java source code details + private String className; + private String methodName; + + // camel service details + private String path; + private String tag; + private String consumes; + private String produces; + private String bindingMode; + private String skipBindingOnErrorCode; + private String clientRequestValidation; + private String enableCORS; + private String apiDocs; + private List<RestVerbDetails> verbs; + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public String getLineNumber() { + return lineNumber; + } + + public void setLineNumber(String lineNumber) { + this.lineNumber = lineNumber; + } + + public String getLineNumberEnd() { + return lineNumberEnd; + } + + public void setLineNumberEnd(String lineNumberEnd) { + this.lineNumberEnd = lineNumberEnd; + } + + public int getLinePosition() { + return linePosition; + } + + public void setLinePosition(int linePosition) { + this.linePosition = linePosition; + } + + public String getClassName() { + return className; + } + + public void setClassName(String className) { + this.className = className; + } + + public String getMethodName() { + return methodName; + } + + public void setMethodName(String methodName) { + this.methodName = methodName; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getTag() { + return tag; + } + + public void setTag(String tag) { + this.tag = tag; + } + + public String getConsumes() { + return consumes; + } + + public void setConsumes(String consumes) { + this.consumes = consumes; + } + + public String getProduces() { + return produces; + } + + public void setProduces(String produces) { + this.produces = produces; + } + + public String getBindingMode() { + return bindingMode; + } + + public void setBindingMode(String bindingMode) { + this.bindingMode = bindingMode; + } + + public String getSkipBindingOnErrorCode() { + return skipBindingOnErrorCode; + } + + public void setSkipBindingOnErrorCode(String skipBindingOnErrorCode) { + this.skipBindingOnErrorCode = skipBindingOnErrorCode; + } + + public String getClientRequestValidation() { + return clientRequestValidation; + } + + public void setClientRequestValidation(String clientRequestValidation) { + this.clientRequestValidation = clientRequestValidation; + } + + public String getEnableCORS() { + return enableCORS; + } + + public void setEnableCORS(String enableCORS) { + this.enableCORS = enableCORS; + } + + public String getApiDocs() { + return apiDocs; + } + + public void setApiDocs(String apiDocs) { + this.apiDocs = apiDocs; + } + + public List<RestVerbDetails> getVerbs() { + return verbs; + } + + public void setVerbs(List<RestVerbDetails> verbs) { + this.verbs = verbs; + } + + public void addVerb(RestVerbDetails verb) { + if (verbs == null) { + verbs = new ArrayList<>(); + } + verbs.add(verb); + } +} diff --git a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/RestVerbDetails.java b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/RestVerbDetails.java new file mode 100644 index 0000000..a3411f5 --- /dev/null +++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/RestVerbDetails.java @@ -0,0 +1,161 @@ +/** + * 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.parser.model; + +public class RestVerbDetails { + + // source code details + private String fileName; + private String lineNumber; + private String lineNumberEnd; + private int linePosition; + + // java source code details + private String className; + private String methodName; + + // camel verb details + private String method; + private String uri; + private String consumes; + private String produces; + private String bindingMode; + private String skipBindingOnErrorCode; + private String clientRequestValidation; + private String type; + private String outType; + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public String getLineNumber() { + return lineNumber; + } + + public void setLineNumber(String lineNumber) { + this.lineNumber = lineNumber; + } + + public String getLineNumberEnd() { + return lineNumberEnd; + } + + public void setLineNumberEnd(String lineNumberEnd) { + this.lineNumberEnd = lineNumberEnd; + } + + public int getLinePosition() { + return linePosition; + } + + public void setLinePosition(int linePosition) { + this.linePosition = linePosition; + } + + public String getClassName() { + return className; + } + + public void setClassName(String className) { + this.className = className; + } + + public String getMethodName() { + return methodName; + } + + public void setMethodName(String methodName) { + this.methodName = methodName; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public String getConsumes() { + return consumes; + } + + public void setConsumes(String consumes) { + this.consumes = consumes; + } + + public String getProduces() { + return produces; + } + + public void setProduces(String produces) { + this.produces = produces; + } + + public String getBindingMode() { + return bindingMode; + } + + public void setBindingMode(String bindingMode) { + this.bindingMode = bindingMode; + } + + public String getSkipBindingOnErrorCode() { + return skipBindingOnErrorCode; + } + + public void setSkipBindingOnErrorCode(String skipBindingOnErrorCode) { + this.skipBindingOnErrorCode = skipBindingOnErrorCode; + } + + public String getClientRequestValidation() { + return clientRequestValidation; + } + + public void setClientRequestValidation(String clientRequestValidation) { + this.clientRequestValidation = clientRequestValidation; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getOutType() { + return outType; + } + + public void setOutType(String outType) { + this.outType = outType; + } +} diff --git a/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/MyRestDslRouteBuilder.java b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/MyRestDslRouteBuilder.java index 1d914af..7250232 100644 --- a/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/MyRestDslRouteBuilder.java +++ b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/MyRestDslRouteBuilder.java @@ -40,8 +40,12 @@ public class MyRestDslRouteBuilder extends RouteBuilder { .corsHeaderProperty("key1", "value1") .corsHeaderProperty("key2", "value2"); - rest() - .get("/foo") - .to("log:foo"); + rest("/foo") + .get("{id}") + .description("get by id") + .to("log:id") + .post() + .description("post something") + .to("log:post"); } } diff --git a/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterJavaRestDslTest.java b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterJavaRestDslTest.java index dedf9ae..bf92949 100644 --- a/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterJavaRestDslTest.java +++ b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterJavaRestDslTest.java @@ -21,6 +21,7 @@ import java.util.List; import org.apache.camel.parser.RestDslParser; import org.apache.camel.parser.model.RestConfigurationDetails; +import org.apache.camel.parser.model.RestServiceDetails; import org.apache.camel.test.junit4.CamelTestSupport; import org.jboss.forge.roaster.Roaster; import org.jboss.forge.roaster.model.source.JavaClassSource; @@ -34,7 +35,7 @@ public class RoasterJavaRestDslTest extends CamelTestSupport { } @Test - public void parseTree() throws Exception { + public void parseRestConfiguration() throws Exception { JavaClassSource clazz = (JavaClassSource) Roaster.parse(new File("src/test/java/org/apache/camel/parser/java/MyRestDslRouteBuilder.java")); List<RestConfigurationDetails> list = RestDslParser.parseRestConfiguration(clazz, ".", @@ -68,4 +69,26 @@ public class RoasterJavaRestDslTest extends CamelTestSupport { assertEquals("value2", details.getCorsHeaders().get("key2")); } + @Test + public void parseRestService() throws Exception { + JavaClassSource clazz = (JavaClassSource) Roaster.parse(new File("src/test/java/org/apache/camel/parser/java/MyRestDslRouteBuilder.java")); + + List<RestServiceDetails> list = RestDslParser.parseRestService(clazz, ".", + "src/test/java/org/apache/camel/parser/java/MyRestDslRouteBuilder.java", true); + assertEquals(1, list.size()); + RestServiceDetails details = list.get(0); + assertEquals("43", details.getLineNumber()); + assertEquals("49", details.getLineNumberEnd()); + assertEquals("src/test/java/org/apache/camel/parser/java/MyRestDslRouteBuilder.java", details.getFileName()); + assertEquals("configure", details.getMethodName()); + assertEquals("org.apache.camel.parser.java.MyRestDslRouteBuilder", details.getClassName()); + + assertEquals("/foo", details.getPath()); + assertEquals(2, details.getVerbs().size()); + assertEquals("get", details.getVerbs().get(0).getMethod()); + assertEquals("{id}", details.getVerbs().get(0).getUri()); + assertEquals("post", details.getVerbs().get(1).getMethod()); + assertNull(details.getVerbs().get(1).getUri()); + } + }