You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2018/09/20 09:41:10 UTC

[camel] 01/02: CAMEL-12824: camel-route-parser - Add parser for rest-dsl

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

commit adcf45c5dea9c5ce25eb58debb3b67f29095d71b
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Sep 20 11:09:55 2018 +0200

    CAMEL-12824: camel-route-parser - Add parser for rest-dsl
---
 .../org/apache/camel/parser/RestDslParser.java     |  75 ++++
 .../helper/CamelJavaRestDslParserHelper.java       | 381 +++++++++++++++++++++
 .../parser/model/RestConfigurationDetails.java     | 328 ++++++++++++++++++
 .../camel/parser/java/MyRestDslRouteBuilder.java   |  31 ++
 .../camel/parser/java/RoasterJavaRestDslTest.java  |  55 +++
 5 files changed, 870 insertions(+)

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..fb29182
--- /dev/null
+++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RestDslParser.java
@@ -0,0 +1,75 @@
+/**
+ * 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.jboss.forge.roaster.model.source.JavaClassSource;
+import org.jboss.forge.roaster.model.source.MethodSource;
+
+/**
+ * A Camel Rest DSL parser that parses Camel Java Rest DSL.
+ * <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;
+    }
+
+}
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..38cc0c1
--- /dev/null
+++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelJavaRestDslParserHelper.java
@@ -0,0 +1,381 @@
+/**
+ * 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.List;
+
+import org.apache.camel.parser.model.RestConfigurationDetails;
+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);
+                            }
+                            node.setFileName(fullyQualifiedFileName);
+                            node.setClassName(clazz.getQualifiedName());
+                            node.setMethodName(configureMethod.getName());
+
+                            parseExpression(node, fullyQualifiedFileName, clazz, configureMethod, block, exp);
+                        }
+                    }
+                }
+            }
+        }
+
+        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 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 doParseRestConfiguration(RestConfigurationDetails node, String fullyQualifiedFileName,
+                                          JavaClassSource clazz, MethodSource<JavaClassSource> configureMethod, Block block,
+                                          MethodInvocation mi) {
+        String name = mi.getName().getIdentifier();
+        if ("port".equals(name)) {
+            node.setPort(extractValueFromFirstArgument(clazz, block, mi));
+        } else if ("contextPath".equals(name)) {
+            node.setContextPath(extractValueFromFirstArgument(clazz, block, mi));
+        }
+    }
+
+    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;
+    }
+
+    @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 (usually a constant field in another class)
+        // then add a dummy value as we cannot find the field value in other classes and maybe even outside the
+        // source code we have access to
+        if (expression instanceof QualifiedName) {
+            QualifiedName qn = (QualifiedName) expression;
+            String name = qn.getFullyQualifiedName();
+            return "{{" + name + "}}";
+        }
+
+        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/model/RestConfigurationDetails.java b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/RestConfigurationDetails.java
new file mode 100644
index 0000000..c38cb65
--- /dev/null
+++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/RestConfigurationDetails.java
@@ -0,0 +1,328 @@
+/**
+ * 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.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;
+    }
+}
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..b73f77d
--- /dev/null
+++ b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/MyRestDslRouteBuilder.java
@@ -0,0 +1,31 @@
+/**
+ * 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;
+
+public class MyRestDslRouteBuilder extends RouteBuilder {
+
+    @Override
+    public void configure() throws Exception {
+        restConfiguration().contextPath("myapi").port(1234);
+
+        rest()
+            .get("/foo")
+                .to("log:foo");
+    }
+}
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..81a19a4
--- /dev/null
+++ b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterJavaRestDslTest.java
@@ -0,0 +1,55 @@
+/**
+ * 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.test.junit4.CamelTestSupport;
+import org.jboss.forge.roaster.Roaster;
+import org.jboss.forge.roaster.model.source.JavaClassSource;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class RoasterJavaRestDslTest extends CamelTestSupport {
+
+    private static final Logger LOG = LoggerFactory.getLogger(RoasterJavaRestDslTest.class);
+
+    @Override
+    public boolean isDumpRouteCoverage() {
+        return false;
+    }
+
+    @Test
+    public void parseTree() 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("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());
+    }
+
+}