This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/master by this push: new aff6c95 Rest dsl parser (#2529) aff6c95 is described below commit aff6c95ad4968f7394726583c5601f38a0ac8eda Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Sun Sep 23 11:57:15 2018 +0200 Rest dsl parser (#2529) CAMEL-12824: camel-route-parser - Add parser for rest-dsl --- .../org/apache/camel/parser/RestDslParser.java | 112 ++++ .../org/apache/camel/parser/XmlRestDslParser.java | 89 +++ .../helper/CamelJavaRestDslParserHelper.java | 678 +++++++++++++++++++++ .../parser/helper/CamelJavaTreeParserHelper.java | 6 +- .../parser/helper/CamelXmlRestDslParserHelper.java | 279 +++++++++ .../parser/model/RestConfigurationDetails.java | 371 +++++++++++ .../camel/parser/model/RestServiceDetails.java | 189 ++++++ .../apache/camel/parser/model/RestVerbDetails.java | 197 ++++++ .../camel/parser/java/MyRestDslRouteBuilder.java | 51 ++ .../camel/parser/java/RoasterJavaDslTest.java | 2 + .../camel/parser/java/RoasterJavaRestDslTest.java | 103 ++++ .../apache/camel/parser/xml/XmlRestDslTest.java | 111 ++++ .../org/apache/camel/parser/xml/myrest.xml | 51 ++ 13 files changed, 2238 insertions(+), 1 deletion(-) 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 new file mode 100644 index 0000000..6703b76 --- /dev/null +++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RestDslParser.java @@ -0,0 +1,112 @@ +/** + * 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; + +import java.util.ArrayList; +import java.util.Collections; +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; + +/** + * A Camel parser that parses Camel Java Rest DSL source code. + * <p/> + * This implementation is higher level details, and uses the lower level parser {@link CamelJavaRestDslParserHelper}. + */ +public final class RestDslParser { + + private RestDslParser() { + } + + /** + * Parses the java source class and build a rest configuration model of the discovered rest configurations 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 configurations (often there is only one) + */ + public static List<RestConfigurationDetails> parseRestConfiguration(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<RestConfigurationDetails> list = new ArrayList<>(); + for (MethodSource<JavaClassSource> configureMethod : methods) { + // there may be multiple route builder configure methods + List<RestConfigurationDetails> details = parser.parseRestConfiguration(clazz, baseDir, fullyQualifiedFileName, configureMethod); + list.addAll(details); + } + // we end up parsing bottom->up so reverse list + Collections.reverse(list); + + 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/XmlRestDslParser.java b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/XmlRestDslParser.java new file mode 100644 index 0000000..e28a295 --- /dev/null +++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/XmlRestDslParser.java @@ -0,0 +1,89 @@ +/** + * 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; + +import java.io.InputStream; +import java.util.Collections; +import java.util.List; + +import org.w3c.dom.Document; + +import org.apache.camel.parser.helper.CamelXmlRestDslParserHelper; +import org.apache.camel.parser.helper.XmlLineNumberParser; +import org.apache.camel.parser.model.RestConfigurationDetails; +import org.apache.camel.parser.model.RestServiceDetails; + +/** + * A Camel XML parser that parses Camel XML Rest DSL source code. + * <p/> + * This implementation is higher level details, and uses the lower level parser {@link CamelXmlRestDslParserHelper}. + */ +public final class XmlRestDslParser { + + private XmlRestDslParser() { + } + + /** + * Parses the XML file and build a rest configuration model of the discovered rest configurations in the XML source file. + * + * @param xml the xml file as input stream + * @param baseDir the base of the source code + * @param fullyQualifiedFileName the fully qualified source code file name + * @return a list of rest configurations (often there is only one) + */ + public static List<RestConfigurationDetails> parseRestConfiguration(InputStream xml, String baseDir, String fullyQualifiedFileName) { + // try parse it as dom + Document dom = null; + try { + dom = XmlLineNumberParser.parseXml(xml); + } catch (Exception e) { + // ignore as the xml file may not be valid at this point + } + if (dom != null) { + CamelXmlRestDslParserHelper parser = new CamelXmlRestDslParserHelper(); + return parser.parseRestConfiguration(dom, baseDir, fullyQualifiedFileName); + } + + return Collections.EMPTY_LIST; + } + + /** + * Parses the java source class and build a rest service model of the discovered rest services in the java source class. + * + * @param xml the xml file as input stream + * @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(InputStream xml, String baseDir, String fullyQualifiedFileName) { + + // try parse it as dom + Document dom = null; + try { + dom = XmlLineNumberParser.parseXml(xml); + } catch (Exception e) { + // ignore as the xml file may not be valid at this point + } + if (dom != null) { + CamelXmlRestDslParserHelper parser = new CamelXmlRestDslParserHelper(); + return parser.parseRestService(dom, baseDir, fullyQualifiedFileName); + } + + return Collections.EMPTY_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 new file mode 100644 index 0000000..8c3018f --- /dev/null +++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelJavaRestDslParserHelper.java @@ -0,0 +1,678 @@ +/** + * 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.helper; + +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; +import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.Block; +import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.BooleanLiteral; +import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.Expression; +import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.ExpressionStatement; +import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.FieldDeclaration; +import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.InfixExpression; +import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.MemberValuePair; +import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.MethodDeclaration; +import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.MethodInvocation; +import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.NormalAnnotation; +import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.NumberLiteral; +import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.ParenthesizedExpression; +import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.QualifiedName; +import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.SimpleName; +import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.SingleMemberAnnotation; +import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.StringLiteral; +import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.Type; +import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.VariableDeclarationFragment; +import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.VariableDeclarationStatement; +import org.jboss.forge.roaster.model.Annotation; +import org.jboss.forge.roaster.model.source.FieldSource; +import org.jboss.forge.roaster.model.source.JavaClassSource; +import org.jboss.forge.roaster.model.source.MethodSource; + +/** + * A Camel Java Rest DSL parser that only depends on the Roaster API. + * <p/> + * This implement is used for parsing the Camel routes and build a tree structure of the Rest DSL services. + */ +public final class CamelJavaRestDslParserHelper { + + public List<RestConfigurationDetails> parseRestConfiguration(JavaClassSource clazz, String baseDir, String fullyQualifiedFileName, + MethodSource<JavaClassSource> configureMethod) { + + List<RestConfigurationDetails> 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 = isRestConfiguration(exp); + if (valid) { + RestConfigurationDetails node = new RestConfigurationDetails(); + 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); + } + } + } + } + } + + 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, null, 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; + + // 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 configuration + 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) { + if (exp == null) { + return; + } + if (exp instanceof MethodInvocation) { + MethodInvocation mi = (MethodInvocation) exp; + doParseRestConfiguration(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 parseExpression(RestServiceDetails node, RestVerbDetails verb, String fullyQualifiedFileName, + JavaClassSource clazz, MethodSource<JavaClassSource> configureMethod, Block block, + Expression exp) { + if (exp == null) { + // this rest service is not complete, if there is any details on verb then they are actually general + // for this rest service and we should pass the details to it + if (verb != null) { + node.setConsumes(verb.getConsumes()); + node.setProduces(verb.getProduces()); + node.setSkipBindingOnErrorCode(verb.getSkipBindingOnErrorCode()); + node.setClientRequestValidation(verb.getClientRequestValidation()); + node.setApiDocs(verb.getApiDocs()); + node.setDescription(verb.getDescription()); + } + return; + } + if (exp instanceof MethodInvocation) { + MethodInvocation mi = (MethodInvocation) exp; + verb = doParseRestService(node, verb, fullyQualifiedFileName, clazz, configureMethod, block, mi); + // if the method was called on another method, then recursive + exp = mi.getExpression(); + parseExpression(node, verb, fullyQualifiedFileName, clazz, configureMethod, block, exp); + } + } + + private void doParseRestConfiguration(RestConfigurationDetails 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 ("component".equals(name)) { + node.setComponent(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("apiComponent".equals(name)) { + node.setApiComponent(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("producerComponent".equals(name)) { + node.setProducerComponent(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("scheme".equals(name)) { + node.setScheme(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("host".equals(name)) { + node.setHost(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("apiHost".equals(name)) { + node.setApiHost(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("port".equals(name)) { + node.setPort(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("producerApiDoc".equals(name)) { + node.setProducerApiDoc(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("contextPath".equals(name)) { + node.setContextPath(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("apiContextPath".equals(name)) { + node.setApiContextPath(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("apiContextRouteId".equals(name)) { + node.setApiContextRouteId(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("apiContextIdPattern".equals(name)) { + node.setApiContextIdPattern(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("apiContextListening".equals(name)) { + node.setApiContextListening(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("apiVendorExtension".equals(name)) { + node.setApiVendorExtension(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("hostNameResolver".equals(name)) { + node.setHostNameResolver(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("bindingMode".equals(name)) { + node.setBindingMode(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("skipBindingOnErrorCode".equals(name)) { + node.setSkipBindingOnErrorCode(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("clientRequestValidation".equals(name)) { + node.setClientRequestValidation(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("enableCORS".equals(name)) { + node.setEnableCORS(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("jsonDataFormat".equals(name)) { + node.setJsonDataFormat(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("xmlDataFormat".equals(name)) { + node.setXmlDataFormat(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("componentProperty".equals(name)) { + String key = extractValueFromFirstArgument(clazz, block, mi); + String value = extractValueFromSecondArgument(clazz, block, mi); + node.addComponentProperty(key, value); + } else if ("endpointProperty".equals(name)) { + String key = extractValueFromFirstArgument(clazz, block, mi); + String value = extractValueFromSecondArgument(clazz, block, mi); + node.addEndpointProperty(key, value); + } else if ("consumerProperty".equals(name)) { + String key = extractValueFromFirstArgument(clazz, block, mi); + String value = extractValueFromSecondArgument(clazz, block, mi); + node.addConsumerProperty(key, value); + } else if ("dataFormatProperty".equals(name)) { + String key = extractValueFromFirstArgument(clazz, block, mi); + String value = extractValueFromSecondArgument(clazz, block, mi); + node.addDataFormatProperty(key, value); + } else if ("apiProperty".equals(name)) { + String key = extractValueFromFirstArgument(clazz, block, mi); + String value = extractValueFromSecondArgument(clazz, block, mi); + node.addApiProperty(key, value); + } else if ("corsHeaderProperty".equals(name)) { + String key = extractValueFromFirstArgument(clazz, block, mi); + String value = extractValueFromSecondArgument(clazz, block, mi); + node.addCorsHeader(key, value); + } + } + + private RestVerbDetails doParseRestService(RestServiceDetails node, RestVerbDetails verb, 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 (isParentMethod(mi, "rest")) { + verb = doParseRestVerb(node, verb, clazz, configureMethod, block, mi); + } + return verb; + } + + private RestVerbDetails doParseRestVerb(RestServiceDetails node, RestVerbDetails verb, + JavaClassSource clazz, MethodSource<JavaClassSource> configureMethod, Block block, + MethodInvocation mi) { + if (verb == null) { + verb = new RestVerbDetails(); + } + + String name = mi.getName().getIdentifier(); + if ("description".equals(name)) { + verb.setDescription(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("bindingMode".equals(name)) { + verb.setBindingMode(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("skipBindingOnErrorcode".equals(name)) { + verb.setSkipBindingOnErrorCode(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("clientRequestValidation".equals(name)) { + verb.setClientRequestValidation(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("consumes".equals(name)) { + verb.setConsumes(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("produces".equals(name)) { + verb.setProduces(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("type".equals(name)) { + verb.setType(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("outType".equals(name)) { + verb.setOutType(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("apiDocs".equals(name)) { + verb.setApiDocs(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("to".equals(name)) { + verb.setTo(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("toD".equals(name)) { + verb.setToD(extractValueFromFirstArgument(clazz, block, mi)); + } else if ("tag".equals(name)) { + // tag is only available on the node + node.setTag(extractValueFromFirstArgument(clazz, block, mi)); + } + + if ("delete".equals(name)) { + node.addVerb(verb); + verb.setMethod("delete"); + verb.setUri(extractValueFromFirstArgument(clazz, block, mi)); + verb = null; // reset as this verb is not complete + } else if ("get".equals(name)) { + node.addVerb(verb); + verb.setMethod("get"); + verb.setUri(extractValueFromFirstArgument(clazz, block, mi)); + verb = null; // reset as this verb is not complete + } else if ("head".equals(name)) { + node.addVerb(verb); + verb.setMethod("head"); + verb.setUri(extractValueFromFirstArgument(clazz, block, mi)); + verb = null; // reset as this verb is not complete + } else if ("patch".equals(name)) { + node.addVerb(verb); + verb.setMethod("patch"); + verb.setUri(extractValueFromFirstArgument(clazz, block, mi)); + verb = null; // reset as this verb is not complete + } else if ("post".equals(name)) { + node.addVerb(verb); + verb.setMethod("post"); + verb.setUri(extractValueFromFirstArgument(clazz, block, mi)); + verb = null; // reset as this verb is not complete + } else if ("put".equals(name)) { + node.addVerb(verb); + verb.setMethod("put"); + verb.setUri(extractValueFromFirstArgument(clazz, block, mi)); + verb = null; // reset as this verb is not complete + } + + return verb; + } + + 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) { + Expression exp = (Expression) args.get(0); + return getLiteralValue(clazz, block, exp); + } + return null; + } + + private static String extractValueFromSecondArgument(JavaClassSource clazz, Block block, MethodInvocation mi) { + List args = mi.arguments(); + if (args != null && args.size() > 1) { + Expression exp = (Expression) args.get(1); + return getLiteralValue(clazz, block, exp); + } + return null; + } + + @SuppressWarnings("unchecked") + private static FieldSource<JavaClassSource> getField(JavaClassSource clazz, Block block, SimpleName ref) { + String fieldName = ref.getIdentifier(); + if (fieldName != null) { + // find field in class + FieldSource field = clazz != null ? clazz.getField(fieldName) : null; + if (field == null) { + field = findFieldInBlock(clazz, block, fieldName); + } + return field; + } + return null; + } + + @SuppressWarnings("unchecked") + private static FieldSource<JavaClassSource> findFieldInBlock(JavaClassSource clazz, Block block, String fieldName) { + for (Object statement : block.statements()) { + // try local statements first in the block + if (statement instanceof VariableDeclarationStatement) { + final Type type = ((VariableDeclarationStatement) statement).getType(); + for (Object obj : ((VariableDeclarationStatement) statement).fragments()) { + if (obj instanceof VariableDeclarationFragment) { + VariableDeclarationFragment fragment = (VariableDeclarationFragment) obj; + SimpleName name = fragment.getName(); + if (name != null && fieldName.equals(name.getIdentifier())) { + return new StatementFieldSource(clazz, fragment, type); + } + } + } + } + + // okay the field may be burried inside an anonymous inner class as a field declaration + // outside the configure method, so lets go back to the parent and see what we can find + ASTNode node = block.getParent(); + if (node instanceof MethodDeclaration) { + node = node.getParent(); + } + if (node instanceof AnonymousClassDeclaration) { + List declarations = ((AnonymousClassDeclaration) node).bodyDeclarations(); + for (Object dec : declarations) { + if (dec instanceof FieldDeclaration) { + FieldDeclaration fd = (FieldDeclaration) dec; + final Type type = fd.getType(); + for (Object obj : fd.fragments()) { + if (obj instanceof VariableDeclarationFragment) { + VariableDeclarationFragment fragment = (VariableDeclarationFragment) obj; + SimpleName name = fragment.getName(); + if (name != null && fieldName.equals(name.getIdentifier())) { + return new StatementFieldSource(clazz, fragment, type); + } + } + } + } + } + } + } + return null; + } + + public static String getLiteralValue(JavaClassSource clazz, Block block, Expression expression) { + // unwrap parenthesis + if (expression instanceof ParenthesizedExpression) { + expression = ((ParenthesizedExpression) expression).getExpression(); + } + + if (expression instanceof StringLiteral) { + return ((StringLiteral) expression).getLiteralValue(); + } else if (expression instanceof BooleanLiteral) { + return "" + ((BooleanLiteral) expression).booleanValue(); + } else if (expression instanceof NumberLiteral) { + return ((NumberLiteral) expression).getToken(); + } + + // if it a method invocation then add a dummy value assuming the method invocation will return a valid response + if (expression instanceof MethodInvocation) { + String name = ((MethodInvocation) expression).getName().getIdentifier(); + return "{{" + name + "}}"; + } + + // if its a qualified name, then its an enum where we should grab the simple name + if (expression instanceof QualifiedName) { + QualifiedName qn = (QualifiedName) expression; + return qn.getName().getIdentifier(); + } + + if (expression instanceof SimpleName) { + FieldSource<JavaClassSource> field = getField(clazz, block, (SimpleName) expression); + if (field != null) { + // is the field annotated with a Camel endpoint + if (field.getAnnotations() != null) { + for (Annotation ann : field.getAnnotations()) { + boolean valid = "org.apache.camel.EndpointInject".equals(ann.getQualifiedName()) || "org.apache.camel.cdi.Uri".equals(ann.getQualifiedName()); + if (valid) { + Expression exp = (Expression) ann.getInternal(); + if (exp instanceof SingleMemberAnnotation) { + exp = ((SingleMemberAnnotation) exp).getValue(); + } else if (exp instanceof NormalAnnotation) { + List values = ((NormalAnnotation) exp).values(); + for (Object value : values) { + MemberValuePair pair = (MemberValuePair) value; + if ("uri".equals(pair.getName().toString())) { + exp = pair.getValue(); + break; + } + } + } + if (exp != null) { + return getLiteralValue(clazz, block, exp); + } + } + } + } + // is the field an org.apache.camel.Endpoint type? + if ("Endpoint".equals(field.getType().getSimpleName())) { + // then grab the uri from the first argument + VariableDeclarationFragment vdf = (VariableDeclarationFragment) field.getInternal(); + expression = vdf.getInitializer(); + if (expression instanceof MethodInvocation) { + MethodInvocation mi = (MethodInvocation) expression; + List args = mi.arguments(); + if (args != null && args.size() > 0) { + // the first argument has the endpoint uri + expression = (Expression) args.get(0); + return getLiteralValue(clazz, block, expression); + } + } + } else { + // no annotations so try its initializer + VariableDeclarationFragment vdf = (VariableDeclarationFragment) field.getInternal(); + expression = vdf.getInitializer(); + if (expression == null) { + // its a field which has no initializer, then add a dummy value assuming the field will be initialized at runtime + return "{{" + field.getName() + "}}"; + } else { + return getLiteralValue(clazz, block, expression); + } + } + } else { + // we could not find the field in this class/method, so its maybe from some other super class, so insert a dummy value + final String fieldName = ((SimpleName) expression).getIdentifier(); + return "{{" + fieldName + "}}"; + } + } else if (expression instanceof InfixExpression) { + String answer = null; + // is it a string that is concat together? + InfixExpression ie = (InfixExpression) expression; + if (InfixExpression.Operator.PLUS.equals(ie.getOperator())) { + + String val1 = getLiteralValue(clazz, block, ie.getLeftOperand()); + String val2 = getLiteralValue(clazz, block, ie.getRightOperand()); + + // if numeric then we plus the values, otherwise we string concat + boolean numeric = isNumericOperator(clazz, block, ie.getLeftOperand()) && isNumericOperator(clazz, block, ie.getRightOperand()); + if (numeric) { + Long num1 = val1 != null ? Long.valueOf(val1) : 0; + Long num2 = val2 != null ? Long.valueOf(val2) : 0; + answer = "" + (num1 + num2); + } else { + answer = (val1 != null ? val1 : "") + (val2 != null ? val2 : ""); + } + + if (!answer.isEmpty()) { + // include extended when we concat on 2 or more lines + List extended = ie.extendedOperands(); + if (extended != null) { + for (Object ext : extended) { + String val3 = getLiteralValue(clazz, block, (Expression) ext); + if (numeric) { + Long num3 = val3 != null ? Long.valueOf(val3) : 0; + Long num = Long.valueOf(answer); + answer = "" + (num + num3); + } else { + answer += val3 != null ? val3 : ""; + } + } + } + } + } + return answer; + } + + return null; + } + + private static boolean isNumericOperator(JavaClassSource clazz, Block block, Expression expression) { + if (expression instanceof NumberLiteral) { + return true; + } else if (expression instanceof SimpleName) { + FieldSource field = getField(clazz, block, (SimpleName) expression); + if (field != null) { + return field.getType().isType("int") || field.getType().isType("long") + || field.getType().isType("Integer") || field.getType().isType("Long"); + } + } + return false; + } + + private static int findLineNumber(String fullyQualifiedFileName, int position) { + int lines = 0; + + try { + int current = 0; + try (BufferedReader br = new BufferedReader(new FileReader(new File(fullyQualifiedFileName)))) { + String line; + while ((line = br.readLine()) != null) { + lines++; + current += line.length() + 1; // add 1 for line feed + if (current >= position) { + return lines; + } + } + } + } catch (Exception e) { + // ignore + return -1; + } + + return lines; + } + +} diff --git a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelJavaTreeParserHelper.java b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelJavaTreeParserHelper.java index d48ce8e..d987c10 100644 --- a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelJavaTreeParserHelper.java +++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelJavaTreeParserHelper.java @@ -265,8 +265,12 @@ public final class CamelJavaTreeParserHelper { if (line > -1) { newNode.setLineNumber("" + line); } + pos = mi.getName().getStartPosition() + mi.getName().getLength(); + line = findLineNumber(fullyQualifiedFileName, pos); + if (line > -1) { + newNode.setLineNumberEnd("" + line); + } newNode.setFileName(fullyQualifiedFileName); - newNode.setClassName(clazz.getQualifiedName()); newNode.setMethodName(configureMethod.getName()); diff --git a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelXmlRestDslParserHelper.java b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelXmlRestDslParserHelper.java new file mode 100644 index 0000000..bb04cab --- /dev/null +++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelXmlRestDslParserHelper.java @@ -0,0 +1,279 @@ +/** + * 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.helper; + +import java.util.ArrayList; +import java.util.List; + +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import org.apache.camel.parser.model.RestConfigurationDetails; +import org.apache.camel.parser.model.RestServiceDetails; +import org.apache.camel.parser.model.RestVerbDetails; + +public final class CamelXmlRestDslParserHelper { + + public List<RestConfigurationDetails> parseRestConfiguration(Node xmlNode, String baseDir, String fullyQualifiedFileName) { + + List<RestConfigurationDetails> answer = new ArrayList<>(); + + RestConfigurationDetails detail = new RestConfigurationDetails(); + detail.setFileName(fullyQualifiedFileName); + walkXmlTree(xmlNode, detail); + answer.add(detail); + + return answer; + } + + public List<RestServiceDetails> parseRestService(Node xmlNode, String baseDir, String fullyQualifiedFileName) { + + List<RestServiceDetails> answer = new ArrayList<>(); + + RestServiceDetails detail = new RestServiceDetails(); + detail.setFileName(fullyQualifiedFileName); + walkXmlTree(xmlNode, detail); + answer.add(detail); + + return answer; + } + + private void walkXmlTree(Node xmlNode, RestConfigurationDetails detail) { + if ("restConfiguration".equals(xmlNode.getNodeName())) { + String lineNumber = (String) xmlNode.getUserData(XmlLineNumberParser.LINE_NUMBER); + String lineNumberEnd = (String) xmlNode.getUserData(XmlLineNumberParser.LINE_NUMBER_END); + detail.setLineNumber(lineNumber); + detail.setLineNumberEnd(lineNumberEnd); + + NamedNodeMap map = xmlNode.getAttributes(); + detail.setComponent(extractAttribute(map, "component")); + detail.setApiComponent(extractAttribute(map, "apiComponent")); + detail.setProducerComponent(extractAttribute(map, "producerComponent")); + detail.setScheme(extractAttribute(map, "scheme")); + detail.setHost(extractAttribute(map, "host")); + detail.setApiHost(extractAttribute(map, "apiHost")); + detail.setPort(extractAttribute(map, "port")); + detail.setProducerApiDoc(extractAttribute(map, "producerApiDoc")); + detail.setContextPath(extractAttribute(map, "contextPath")); + detail.setApiContextPath(extractAttribute(map, "apiContextPath")); + detail.setApiContextRouteId(extractAttribute(map, "apiContextRouteId")); + detail.setApiContextIdPattern(extractAttribute(map, "apiContextIdPattern")); + detail.setApiContextListening(extractAttribute(map, "apiContextListening")); + detail.setApiVendorExtension(extractAttribute(map, "apiVendorExtension")); + detail.setHostNameResolver(extractAttribute(map, "hostNameResolver")); + detail.setBindingMode(extractAttribute(map, "bindingMode")); + detail.setSkipBindingOnErrorCode(extractAttribute(map, "skipBindingOnErrorCode")); + detail.setClientRequestValidation(extractAttribute(map, "clientRequestValidation")); + detail.setEnableCORS(extractAttribute(map, "enableCORS")); + detail.setJsonDataFormat(extractAttribute(map, "jsonDataFormat")); + detail.setXmlDataFormat(extractAttribute(map, "xmlDataFormat")); + } + + if ("componentProperty".equals(xmlNode.getNodeName()) + && (xmlNode.getParentNode() != null && "restConfiguration".equals(xmlNode.getParentNode().getNodeName()))) { + NamedNodeMap map = xmlNode.getAttributes(); + String key = extractAttribute(map, "key"); + String value = extractAttribute(map, "value"); + if (key != null && value != null) { + detail.addComponentProperty(key, value); + } + } else if ("endpointProperty".equals(xmlNode.getNodeName()) + && (xmlNode.getParentNode() != null && "restConfiguration".equals(xmlNode.getParentNode().getNodeName()))) { + NamedNodeMap map = xmlNode.getAttributes(); + String key = extractAttribute(map, "key"); + String value = extractAttribute(map, "value"); + if (key != null && value != null) { + detail.addEndpointProperty(key, value); + } + } else if ("consumerProperty".equals(xmlNode.getNodeName()) + && (xmlNode.getParentNode() != null && "restConfiguration".equals(xmlNode.getParentNode().getNodeName()))) { + NamedNodeMap map = xmlNode.getAttributes(); + String key = extractAttribute(map, "key"); + String value = extractAttribute(map, "value"); + if (key != null && value != null) { + detail.addConsumerProperty(key, value); + } + } else if ("dataFormatProperty".equals(xmlNode.getNodeName()) + && (xmlNode.getParentNode() != null && "restConfiguration".equals(xmlNode.getParentNode().getNodeName()))) { + NamedNodeMap map = xmlNode.getAttributes(); + String key = extractAttribute(map, "key"); + String value = extractAttribute(map, "value"); + if (key != null && value != null) { + detail.addDataFormatProperty(key, value); + } + } else if ("apiProperty".equals(xmlNode.getNodeName()) + && (xmlNode.getParentNode() != null && "restConfiguration".equals(xmlNode.getParentNode().getNodeName()))) { + NamedNodeMap map = xmlNode.getAttributes(); + String key = extractAttribute(map, "key"); + String value = extractAttribute(map, "value"); + if (key != null && value != null) { + detail.addApiProperty(key, value); + } + } else if ("corsHeaders".equals(xmlNode.getNodeName()) + && (xmlNode.getParentNode() != null && "restConfiguration".equals(xmlNode.getParentNode().getNodeName()))) { + NamedNodeMap map = xmlNode.getAttributes(); + String key = extractAttribute(map, "key"); + String value = extractAttribute(map, "value"); + if (key != null && value != null) { + detail.addCorsHeader(key, value); + } + } + + // walk the rest of the children + NodeList children = xmlNode.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Node child = children.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + walkXmlTree(child, detail); + } + } + } + + private void walkXmlTree(Node xmlNode, RestServiceDetails detail) { + if ("rest".equals(xmlNode.getNodeName())) { + String lineNumber = (String) xmlNode.getUserData(XmlLineNumberParser.LINE_NUMBER); + String lineNumberEnd = (String) xmlNode.getUserData(XmlLineNumberParser.LINE_NUMBER_END); + detail.setLineNumber(lineNumber); + detail.setLineNumberEnd(lineNumberEnd); + extractAttributes(xmlNode, detail); + } + + if (isParentRest(xmlNode)) { + if ("delete".equals(xmlNode.getNodeName())) { + RestVerbDetails verb = new RestVerbDetails(); + verb.setMethod("delete"); + detail.addVerb(verb); + extractAttributes(xmlNode, verb); + } else if ("get".equals(xmlNode.getNodeName())) { + RestVerbDetails verb = new RestVerbDetails(); + verb.setMethod("get"); + detail.addVerb(verb); + extractAttributes(xmlNode, verb); + } else if ("head".equals(xmlNode.getNodeName())) { + RestVerbDetails verb = new RestVerbDetails(); + verb.setMethod("head"); + detail.addVerb(verb); + extractAttributes(xmlNode, verb); + } else if ("patch".equals(xmlNode.getNodeName())) { + RestVerbDetails verb = new RestVerbDetails(); + verb.setMethod("patch"); + detail.addVerb(verb); + extractAttributes(xmlNode, verb); + } else if ("post".equals(xmlNode.getNodeName())) { + RestVerbDetails verb = new RestVerbDetails(); + verb.setMethod("post"); + detail.addVerb(verb); + extractAttributes(xmlNode, verb); + } else if ("put".equals(xmlNode.getNodeName())) { + RestVerbDetails verb = new RestVerbDetails(); + verb.setMethod("put"); + detail.addVerb(verb); + extractAttributes(xmlNode, verb); + } + + if ("description".equals(xmlNode.getNodeName())) { + String value = xmlNode.getTextContent(); + RestVerbDetails verb = getLastVerb(detail); + if (verb != null) { + verb.setDescription(value); + } else { + detail.setDescription(value); + } + } else if ("to".equals(xmlNode.getNodeName())) { + NamedNodeMap map = xmlNode.getAttributes(); + String uri = extractAttribute(map, "uri"); + RestVerbDetails verb = getLastVerb(detail); + if (verb != null) { + verb.setTo(uri); + } + } else if ("toD".equals(xmlNode.getNodeName())) { + NamedNodeMap map = xmlNode.getAttributes(); + String uri = extractAttribute(map, "uri"); + RestVerbDetails verb = getLastVerb(detail); + if (verb != null) { + verb.setToD(uri); + } + } + } + + // walk the rest of the children + NodeList children = xmlNode.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Node child = children.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + walkXmlTree(child, detail); + } + } + } + + private static void extractAttributes(Node xmlNode, RestServiceDetails detail) { + NamedNodeMap map = xmlNode.getAttributes(); + detail.setConsumes(extractAttribute(map, "consumes")); + detail.setProduces(extractAttribute(map, "produces")); + detail.setApiDocs(extractAttribute(map, "apiDocs")); + detail.setBindingMode(extractAttribute(map, "bindingMode")); + detail.setClientRequestValidation(extractAttribute(map, "clientRequestValidation")); + detail.setEnableCORS(extractAttribute(map, "enableCORS")); + detail.setPath(extractAttribute(map, "path")); + detail.setSkipBindingOnErrorCode(extractAttribute(map, "skipBindingOnErrorCode")); + detail.setTag(extractAttribute(map, "tag")); + } + + private static void extractAttributes(Node xmlNode, RestVerbDetails detail) { + NamedNodeMap map = xmlNode.getAttributes(); + detail.setUri(extractAttribute(map, "uri")); + detail.setConsumes(extractAttribute(map, "consumes")); + detail.setProduces(extractAttribute(map, "produces")); + detail.setApiDocs(extractAttribute(map, "apiDocs")); + detail.setBindingMode(extractAttribute(map, "bindingMode")); + detail.setClientRequestValidation(extractAttribute(map, "clientRequestValidation")); + detail.setSkipBindingOnErrorCode(extractAttribute(map, "skipBindingOnErrorCode")); + detail.setType(extractAttribute(map, "type")); + detail.setOutType(extractAttribute(map, "outType")); + } + + private static RestVerbDetails getLastVerb(RestServiceDetails detail) { + if (detail.getVerbs() == null) { + return null; + } + return detail.getVerbs().get(detail.getVerbs().size() - 1); + } + + private static boolean isParentRest(Node node) { + if (node == null) { + return false; + } + String name = node.getNodeName(); + if ("rest".equals(name)) { + return true; + } else { + return isParentRest(node.getParentNode()); + } + } + + private static String extractAttribute(NamedNodeMap map, String name) { + if (map != null) { + Node attr = map.getNamedItem(name); + if (attr != null) { + return attr.getTextContent(); + } + } + return null; + } + +} diff --git a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/RestConfigurationDetails.java b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/RestConfigurationDetails.java new file mode 100644 index 0000000..b1db413 --- /dev/null +++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/RestConfigurationDetails.java @@ -0,0 +1,371 @@ +/** + * 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.LinkedHashMap; +import java.util.Map; + +public class RestConfigurationDetails { + + // 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 rest configuration details + private String component; + private String apiComponent; + private String producerComponent; + private String scheme; + private String host; + private String apiHost; + private String port; + private String producerApiDoc; + private String contextPath; + private String apiContextPath; + private String apiContextRouteId; + private String apiContextIdPattern; + private String apiContextListening; + private String apiVendorExtension; + private String hostNameResolver; + private String bindingMode; + private String skipBindingOnErrorCode; + private String clientRequestValidation; + private String enableCORS; + private String jsonDataFormat; + private String xmlDataFormat; + private Map<String, String> componentProperties; + private Map<String, String> endpointProperties; + private Map<String, String> consumerProperties; + private Map<String, String> dataFormatProperties; + private Map<String, String> apiProperties; + private Map<String, String> corsHeaders; + + public RestConfigurationDetails() { + } + + 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 getComponent() { + return component; + } + + public void setComponent(String component) { + this.component = component; + } + + public String getApiComponent() { + return apiComponent; + } + + public void setApiComponent(String apiComponent) { + this.apiComponent = apiComponent; + } + + public String getProducerComponent() { + return producerComponent; + } + + public void setProducerComponent(String producerComponent) { + this.producerComponent = producerComponent; + } + + public String getScheme() { + return scheme; + } + + public void setScheme(String scheme) { + this.scheme = scheme; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public String getApiHost() { + return apiHost; + } + + public void setApiHost(String apiHost) { + this.apiHost = apiHost; + } + + public String getPort() { + return port; + } + + public void setPort(String port) { + this.port = port; + } + + public String getProducerApiDoc() { + return producerApiDoc; + } + + public void setProducerApiDoc(String producerApiDoc) { + this.producerApiDoc = producerApiDoc; + } + + public String getContextPath() { + return contextPath; + } + + public void setContextPath(String contextPath) { + this.contextPath = contextPath; + } + + public String getApiContextPath() { + return apiContextPath; + } + + public void setApiContextPath(String apiContextPath) { + this.apiContextPath = apiContextPath; + } + + public String getApiContextRouteId() { + return apiContextRouteId; + } + + public void setApiContextRouteId(String apiContextRouteId) { + this.apiContextRouteId = apiContextRouteId; + } + + public String getApiContextIdPattern() { + return apiContextIdPattern; + } + + public void setApiContextIdPattern(String apiContextIdPattern) { + this.apiContextIdPattern = apiContextIdPattern; + } + + public String getApiContextListening() { + return apiContextListening; + } + + public void setApiContextListening(String apiContextListening) { + this.apiContextListening = apiContextListening; + } + + public String getApiVendorExtension() { + return apiVendorExtension; + } + + public void setApiVendorExtension(String apiVendorExtension) { + this.apiVendorExtension = apiVendorExtension; + } + + public String getHostNameResolver() { + return hostNameResolver; + } + + public void setHostNameResolver(String hostNameResolver) { + this.hostNameResolver = hostNameResolver; + } + + 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 getJsonDataFormat() { + return jsonDataFormat; + } + + public void setJsonDataFormat(String jsonDataFormat) { + this.jsonDataFormat = jsonDataFormat; + } + + public String getXmlDataFormat() { + return xmlDataFormat; + } + + public void setXmlDataFormat(String xmlDataFormat) { + this.xmlDataFormat = xmlDataFormat; + } + + public Map<String, String> getComponentProperties() { + return componentProperties; + } + + public void setComponentProperties(Map<String, String> componentProperties) { + this.componentProperties = componentProperties; + } + + public Map<String, String> getEndpointProperties() { + return endpointProperties; + } + + public void setEndpointProperties(Map<String, String> endpointProperties) { + this.endpointProperties = endpointProperties; + } + + public Map<String, String> getConsumerProperties() { + return consumerProperties; + } + + public void setConsumerProperties(Map<String, String> consumerProperties) { + this.consumerProperties = consumerProperties; + } + + public Map<String, String> getDataFormatProperties() { + return dataFormatProperties; + } + + public void setDataFormatProperties(Map<String, String> dataFormatProperties) { + this.dataFormatProperties = dataFormatProperties; + } + + public Map<String, String> getApiProperties() { + return apiProperties; + } + + public void setApiProperties(Map<String, String> apiProperties) { + this.apiProperties = apiProperties; + } + + public Map<String, String> getCorsHeaders() { + return corsHeaders; + } + + public void setCorsHeaders(Map<String, String> corsHeaders) { + this.corsHeaders = corsHeaders; + } + + public void addComponentProperty(String key, String value) { + if (componentProperties == null) { + componentProperties = new LinkedHashMap<>(); + } + componentProperties.put(key, value); + } + + public void addEndpointProperty(String key, String value) { + if (endpointProperties == null) { + endpointProperties = new LinkedHashMap<>(); + } + endpointProperties.put(key, value); + } + + public void addConsumerProperty(String key, String value) { + if (consumerProperties == null) { + consumerProperties = new LinkedHashMap<>(); + } + consumerProperties.put(key, value); + } + + public void addDataFormatProperty(String key, String value) { + if (dataFormatProperties == null) { + dataFormatProperties = new LinkedHashMap<>(); + } + dataFormatProperties.put(key, value); + } + + public void addApiProperty(String key, String value) { + if (apiProperties == null) { + apiProperties = new LinkedHashMap<>(); + } + apiProperties.put(key, value); + } + + public void addCorsHeader(String key, String value) { + if (corsHeaders == null) { + corsHeaders = new LinkedHashMap<>(); + } + corsHeaders.put(key, value); + } +} 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..8e5d60d --- /dev/null +++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/RestServiceDetails.java @@ -0,0 +1,189 @@ +/** + * 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 String description; + 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 String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + 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..ba384e2 --- /dev/null +++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/RestVerbDetails.java @@ -0,0 +1,197 @@ +/** + * 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; + private String description; + private String apiDocs; + private String to; + private String toD; + + 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; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getApiDocs() { + return apiDocs; + } + + public void setApiDocs(String apiDocs) { + this.apiDocs = apiDocs; + } + + public String getTo() { + return to; + } + + public void setTo(String to) { + this.to = to; + } + + public String getToD() { + return toD; + } + + public void setToD(String toD) { + this.toD = toD; + } +} 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 new file mode 100644 index 0000000..70afcfb --- /dev/null +++ b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/MyRestDslRouteBuilder.java @@ -0,0 +1,51 @@ +/** + * 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.java; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.model.rest.RestBindingMode; +import org.apache.camel.model.rest.RestHostNameResolver; + +public class MyRestDslRouteBuilder extends RouteBuilder { + + @Override + public void configure() throws Exception { + restConfiguration() + .contextPath("myapi").port(1234) + .component("jetty") + .apiComponent("swagger") + .apiHost("localhost") + .apiContextPath("myapi/swagger") + .skipBindingOnErrorCode(true) + .scheme("https") + .hostNameResolver(RestHostNameResolver.allLocalIp) + .bindingMode(RestBindingMode.json) + .componentProperty("foo", "123") + .endpointProperty("pretty", "false") + .consumerProperty("bar", "456") + .corsHeaderProperty("key1", "value1") + .corsHeaderProperty("key2", "value2"); + + rest("/foo").consumes("xml").produces("json").description("my foo service") + .get("{id}").apiDocs(false) + .description("get by id") + .to("log:id") + .post().bindingMode(RestBindingMode.xml) + .description("post something") + .toD("log:post"); + } +} diff --git a/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterJavaDslTest.java b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterJavaDslTest.java index 2173742..51cc75f 100644 --- a/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterJavaDslTest.java +++ b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterJavaDslTest.java @@ -49,6 +49,8 @@ public class RoasterJavaDslTest extends CamelTestSupport { assertEquals("bar", details.getRouteId()); assertEquals("configure", details.getMethodName()); assertEquals("org.apache.camel.parser.java.MyJavaDslRouteBuilder", details.getClassName()); + assertEquals("28", list.get(0).getLineNumber()); + assertEquals("28", list.get(0).getLineNumberEnd()); String tree = details.dump(0); LOG.info("\n" + tree); 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 new file mode 100644 index 0000000..e5ae2a5 --- /dev/null +++ b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterJavaRestDslTest.java @@ -0,0 +1,103 @@ +/** + * 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.java; + +import java.io.File; +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; +import org.junit.Test; + +public class RoasterJavaRestDslTest extends CamelTestSupport { + + @Override + public boolean isDumpRouteCoverage() { + return false; + } + + @Test + 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, ".", + "src/test/java/org/apache/camel/parser/java/MyRestDslRouteBuilder.java", true); + assertEquals(1, list.size()); + RestConfigurationDetails details = list.get(0); + assertEquals("27", details.getLineNumber()); + assertEquals("41", 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("1234", details.getPort()); + assertEquals("myapi", details.getContextPath()); + assertEquals("jetty", details.getComponent()); + assertEquals("json", details.getBindingMode()); + assertEquals("swagger", details.getApiComponent()); + assertEquals("myapi/swagger", details.getApiContextPath()); + assertEquals("localhost", details.getApiHost()); + assertEquals("true", details.getSkipBindingOnErrorCode()); + assertEquals("https", details.getScheme()); + assertEquals("allLocalIp", details.getHostNameResolver()); + + assertEquals(1, details.getComponentProperties().size()); + assertEquals("123", details.getComponentProperties().get("foo")); + assertEquals(1, details.getEndpointProperties().size()); + assertEquals("false", details.getEndpointProperties().get("pretty")); + assertEquals(1, details.getEndpointProperties().size()); + assertEquals("456", details.getConsumerProperties().get("bar")); + assertEquals(2, details.getCorsHeaders().size()); + assertEquals("value1", details.getCorsHeaders().get("key1")); + 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("my foo service", details.getDescription()); + assertEquals("json", details.getProduces()); + assertEquals("json", details.getProduces()); + assertEquals(2, details.getVerbs().size()); + assertEquals("get", details.getVerbs().get(0).getMethod()); + assertEquals("{id}", details.getVerbs().get(0).getUri()); + assertEquals("get by id", details.getVerbs().get(0).getDescription()); + assertEquals("log:id", details.getVerbs().get(0).getTo()); + assertEquals("false", details.getVerbs().get(0).getApiDocs()); + assertEquals("post", details.getVerbs().get(1).getMethod()); + assertEquals("post something", details.getVerbs().get(1).getDescription()); + assertEquals("xml", details.getVerbs().get(1).getBindingMode()); + assertEquals("log:post", details.getVerbs().get(1).getToD()); + assertNull(details.getVerbs().get(1).getUri()); + } + +} diff --git a/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/xml/XmlRestDslTest.java b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/xml/XmlRestDslTest.java new file mode 100644 index 0000000..78e1156 --- /dev/null +++ b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/xml/XmlRestDslTest.java @@ -0,0 +1,111 @@ +/** + * 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.xml; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.List; + +import org.apache.camel.parser.RestDslParser; +import org.apache.camel.parser.XmlRestDslParser; +import org.apache.camel.parser.model.RestConfigurationDetails; +import org.apache.camel.parser.model.RestServiceDetails; +import org.jboss.forge.roaster.Roaster; +import org.jboss.forge.roaster.model.source.JavaClassSource; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class XmlRestDslTest { + + @Test + public void testXmlTree() throws Exception { + InputStream is = new FileInputStream("src/test/resources/org/apache/camel/parser/xml/myrest.xml"); + String fqn = "src/test/resources/org/apache/camel/camel/parser/xml/myrest.xml"; + String baseDir = "src/test/resources"; + List<RestConfigurationDetails> list = XmlRestDslParser.parseRestConfiguration(is, baseDir, fqn); + + assertEquals(1, list.size()); + RestConfigurationDetails details = list.get(0); + assertEquals("src/test/resources/org/apache/camel/camel/parser/xml/myrest.xml", details.getFileName()); + assertNull(details.getMethodName()); + assertNull(details.getClassName()); + + assertEquals("29", details.getLineNumber()); + assertEquals("35", details.getLineNumberEnd()); + assertEquals("1234", details.getPort()); + assertEquals("myapi", details.getContextPath()); + assertEquals("jetty", details.getComponent()); + assertEquals("json", details.getBindingMode()); + assertEquals("swagger", details.getApiComponent()); + assertEquals("myapi/swagger", details.getApiContextPath()); + assertEquals("localhost", details.getApiHost()); + assertEquals("true", details.getSkipBindingOnErrorCode()); + assertEquals("https", details.getScheme()); + assertEquals("allLocalIp", details.getHostNameResolver()); + + assertEquals(1, details.getComponentProperties().size()); + assertEquals("123", details.getComponentProperties().get("foo")); + assertEquals(1, details.getEndpointProperties().size()); + assertEquals("false", details.getEndpointProperties().get("pretty")); + assertEquals(1, details.getEndpointProperties().size()); + assertEquals("456", details.getConsumerProperties().get("bar")); + assertEquals(2, details.getCorsHeaders().size()); + assertEquals("value1", details.getCorsHeaders().get("key1")); + assertEquals("value2", details.getCorsHeaders().get("key2")); + } + + @Test + public void parseRestService() throws Exception { + InputStream is = new FileInputStream("src/test/resources/org/apache/camel/parser/xml/myrest.xml"); + String fqn = "src/test/resources/org/apache/camel/camel/parser/xml/myrest.xml"; + String baseDir = "src/test/resources"; + List<RestServiceDetails> list = XmlRestDslParser.parseRestService(is, baseDir, fqn); + + assertEquals(1, list.size()); + RestServiceDetails details = list.get(0); + assertEquals("src/test/resources/org/apache/camel/camel/parser/xml/myrest.xml", details.getFileName()); + assertNull(details.getMethodName()); + assertNull(details.getClassName()); + + assertEquals("37", details.getLineNumber()); + assertEquals("47", details.getLineNumberEnd()); + assertEquals("src/test/resources/org/apache/camel/camel/parser/xml/myrest.xml", details.getFileName()); + assertNull(details.getMethodName()); + assertNull(details.getClassName()); + + assertEquals("/foo", details.getPath()); + assertEquals("my foo service", details.getDescription()); + assertEquals("json", details.getProduces()); + assertEquals("json", details.getProduces()); + assertEquals(2, details.getVerbs().size()); + assertEquals("get", details.getVerbs().get(0).getMethod()); + assertEquals("{id}", details.getVerbs().get(0).getUri()); + assertEquals("get by id", details.getVerbs().get(0).getDescription()); + assertEquals("log:id", details.getVerbs().get(0).getTo()); + assertEquals("false", details.getVerbs().get(0).getApiDocs()); + assertEquals("post", details.getVerbs().get(1).getMethod()); + assertEquals("post something", details.getVerbs().get(1).getDescription()); + assertEquals("xml", details.getVerbs().get(1).getBindingMode()); + assertEquals("log:post", details.getVerbs().get(1).getToD()); + assertNull(details.getVerbs().get(1).getUri()); + } + + +} diff --git a/tooling/camel-route-parser/src/test/resources/org/apache/camel/parser/xml/myrest.xml b/tooling/camel-route-parser/src/test/resources/org/apache/camel/parser/xml/myrest.xml new file mode 100644 index 0000000..82f493a --- /dev/null +++ b/tooling/camel-route-parser/src/test/resources/org/apache/camel/parser/xml/myrest.xml @@ -0,0 +1,51 @@ +<?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. + +--> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation=" + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd"> + + <camelContext xmlns="http://camel.apache.org/schema/spring"> + <restConfiguration component="jetty" port="1234" bindingMode="json" apiComponent="swagger" scheme="https" + contextPath="myapi" apiContextPath="myapi/swagger" hostNameResolver="allLocalIp" apiHost="localhost" + skipBindingOnErrorCode="true"> + <componentProperty key="foo" value="123"/> + <endpointProperty key="pretty" value="false"/> + <consumerProperty key="bar" value="456"/> + <corsHeaders key="key1" value="value1"/> + <corsHeaders key="key2" value="value2"/> + </restConfiguration> + + <rest path="/foo" consumes="xml" produces="json"> + <description>my foo service</description> + <get uri="{id}" apiDocs="false"> + <description>get by id</description> + <to uri="log:id"/> + </get> + <post bindingMode="xml"> + <description>post something</description> + <toD uri="log:post"/> + </post> + </rest> + + </camelContext> + +</beans>