You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by nf...@apache.org on 2023/03/08 16:27:16 UTC

[camel-quarkus] branch 4384/add-support-of-groovy-extensions created (now b7f8ea0a7d)

This is an automated email from the ASF dual-hosted git repository.

nfilotto pushed a change to branch 4384/add-support-of-groovy-extensions
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git


      at b7f8ea0a7d Ref #4624: Upgrade to Groovy 4.x

This branch includes the following new commits:

     new e35a97f475 Ref #4384: Groovy DSL - Add support of Groovy extensions
     new b7f8ea0a7d Ref #4624: Upgrade to Groovy 4.x

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[camel-quarkus] 02/02: Ref #4624: Upgrade to Groovy 4.x

Posted by nf...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nfilotto pushed a commit to branch 4384/add-support-of-groovy-extensions
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git

commit b7f8ea0a7d113829ce272524bb37c4df772e6577
Author: Nicolas Filotto <nf...@talend.com>
AuthorDate: Wed Mar 8 11:57:15 2023 +0100

    Ref #4624: Upgrade to Groovy 4.x
---
 .../groovy/runtime/graal/GroovySubstitutions.java  | 130 ++++++++++++++++-----
 1 file changed, 98 insertions(+), 32 deletions(-)

diff --git a/extensions/groovy-dsl/runtime/src/main/java/org/apache/camel/quarkus/dsl/groovy/runtime/graal/GroovySubstitutions.java b/extensions/groovy-dsl/runtime/src/main/java/org/apache/camel/quarkus/dsl/groovy/runtime/graal/GroovySubstitutions.java
index 3ddba6d540..927018517c 100644
--- a/extensions/groovy-dsl/runtime/src/main/java/org/apache/camel/quarkus/dsl/groovy/runtime/graal/GroovySubstitutions.java
+++ b/extensions/groovy-dsl/runtime/src/main/java/org/apache/camel/quarkus/dsl/groovy/runtime/graal/GroovySubstitutions.java
@@ -16,68 +16,134 @@
  */
 package org.apache.camel.quarkus.dsl.groovy.runtime.graal;
 
-import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MutableCallSite;
-import java.lang.reflect.Constructor;
+import java.util.function.BiFunction;
 
-import com.oracle.svm.core.annotate.Delete;
+import com.oracle.svm.core.annotate.Alias;
 import com.oracle.svm.core.annotate.Substitute;
 import com.oracle.svm.core.annotate.TargetClass;
-import org.codehaus.groovy.control.ParserPluginFactory;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.runtime.memoize.MemoizeCache;
+import org.codehaus.groovy.vmplugin.v8.CacheableCallSite;
+import org.codehaus.groovy.vmplugin.v8.IndyInterface;
 
 final class GroovySubstitutions {
 }
 
-@TargetClass(className = "org.codehaus.groovy.vmplugin.v8.IndyInterface")
+@TargetClass(className = "org.codehaus.groovy.vmplugin.v8.MethodHandleWrapper")
+final class SubstituteMethodHandleWrapper {
+
+    @Alias
+    public boolean isCanSetTarget() {
+        return false;
+    }
+
+    @Alias
+    public MethodHandle getCachedMethodHandle() {
+        return null;
+    }
+}
+
+@TargetClass(className = "org.codehaus.groovy.vmplugin.v8.IndyInterface$FallbackSupplier")
+final class SubstituteIndyFallbackSupplier {
+
+    @Alias
+    SubstituteIndyFallbackSupplier(MutableCallSite callSite, Class<?> sender, String methodName, int callID,
+            Boolean safeNavigation, Boolean thisCall, Boolean spreadCall, Object dummyReceiver, Object[] arguments) {
+
+    }
+
+    @Alias
+    SubstituteMethodHandleWrapper get() {
+        return null;
+    }
+}
+
+@TargetClass(CacheableCallSite.class)
+final class SubstituteCacheableCallSite {
+
+    @Alias
+    public SubstituteMethodHandleWrapper getAndPut(String className,
+            MemoizeCache.ValueProvider<? super String, ? extends SubstituteMethodHandleWrapper> valueProvider) {
+        return null;
+    }
+}
+
+@TargetClass(IndyInterface.class)
 final class SubstituteIndyInterface {
 
+    @Alias
+    private static SubstituteMethodHandleWrapper NULL_METHOD_HANDLE_WRAPPER;
+
     @Substitute
     protected static void invalidateSwitchPoints() {
         throw new UnsupportedOperationException("invalidateSwitchPoints is not supported");
     }
 
-    @Substitute
-    public static Object fromCache(MutableCallSite callSite, Class<?> sender, String methodName, int callID,
-            Boolean safeNavigation, Boolean thisCall, Boolean spreadCall, Object dummyReceiver, Object[] arguments) {
-        throw new UnsupportedOperationException("fromCache is not supported");
+    @Alias
+    private static boolean bypassCache(Boolean spreadCall, Object[] arguments) {
+        return false;
     }
-}
 
-@TargetClass(className = "org.codehaus.groovy.control.SourceUnit")
-final class SubstituteSourceUnit {
+    @Alias
+    private static <T> T doWithCallSite(MutableCallSite callSite, Object[] arguments,
+            BiFunction<? super SubstituteCacheableCallSite, ? super Object, ? extends T> f) {
+        return null;
+    }
 
     @Substitute
-    public void convert() {
-        throw new UnsupportedOperationException("convert is not supported");
+    public static Object selectMethod(MutableCallSite callSite, Class<?> sender, String methodName, int callID,
+            Boolean safeNavigation, Boolean thisCall, Boolean spreadCall, Object dummyReceiver, Object[] arguments)
+            throws Throwable {
+        throw new UnsupportedOperationException("selectMethod is not supported");
     }
-}
-
-@TargetClass(className = "org.codehaus.groovy.vmplugin.v8.Java8")
-final class SubstituteJava8 {
 
     @Substitute
-    private static Constructor<MethodHandles.Lookup> getLookupConstructor() {
-        throw new UnsupportedOperationException("getLookupConstructor is not supported");
+    public static Object fromCache(MutableCallSite callSite, Class<?> sender, String methodName, int callID,
+            Boolean safeNavigation, Boolean thisCall, Boolean spreadCall, Object dummyReceiver, Object[] arguments)
+            throws Throwable {
+        SubstituteIndyFallbackSupplier fallbackSupplier = new SubstituteIndyFallbackSupplier(callSite, sender, methodName,
+                callID, safeNavigation, thisCall, spreadCall, dummyReceiver, arguments);
+
+        SubstituteMethodHandleWrapper mhw = bypassCache(spreadCall, arguments)
+                ? NULL_METHOD_HANDLE_WRAPPER
+                : doWithCallSite(
+                        callSite, arguments,
+                        new FromCacheBiFunction(fallbackSupplier));
+
+        if (NULL_METHOD_HANDLE_WRAPPER == mhw) {
+            mhw = fallbackSupplier.get();
+        }
+
+        return mhw.getCachedMethodHandle().invokeExact(arguments);
     }
-}
 
-@TargetClass(className = "org.codehaus.groovy.antlr.AntlrParserPlugin")
-@Delete
-final class SubstituteAntlrParserPlugin {
+    static class FromCacheBiFunction implements BiFunction<SubstituteCacheableCallSite, Object, SubstituteMethodHandleWrapper> {
 
-}
+        private final SubstituteIndyFallbackSupplier fallbackSupplier;
 
-@TargetClass(className = "org.codehaus.groovy.antlr.AntlrParserPluginFactory")
-@Delete
-final class SubstituteAntlrParserPluginFactory {
+        FromCacheBiFunction(SubstituteIndyFallbackSupplier fallbackSupplier) {
+            this.fallbackSupplier = fallbackSupplier;
+        }
 
+        @Override
+        public SubstituteMethodHandleWrapper apply(SubstituteCacheableCallSite cs, Object receiver) {
+            return cs.getAndPut(
+                    receiver.getClass().getName(),
+                    c -> {
+                        SubstituteMethodHandleWrapper fbMhw = fallbackSupplier.get();
+                        return fbMhw.isCanSetTarget() ? fbMhw : NULL_METHOD_HANDLE_WRAPPER;
+                    });
+        }
+    }
 }
 
-@TargetClass(className = "org.codehaus.groovy.control.ParserPluginFactory")
-final class SubstituteParserPluginFactory {
+@TargetClass(SourceUnit.class)
+final class SubstituteSourceUnit {
 
     @Substitute
-    public static ParserPluginFactory antlr2() {
-        throw new UnsupportedOperationException("antlr2 is not supported");
+    public void convert() {
+        throw new UnsupportedOperationException("convert is not supported");
     }
 }


[camel-quarkus] 01/02: Ref #4384: Groovy DSL - Add support of Groovy extensions

Posted by nf...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nfilotto pushed a commit to branch 4384/add-support-of-groovy-extensions
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git

commit e35a97f47574b4b8b45b5a2cf1ca398aa8410f5b
Author: Nicolas Filotto <nf...@talend.com>
AuthorDate: Wed Mar 8 10:28:33 2023 +0100

    Ref #4384: Groovy DSL - Add support of Groovy extensions
---
 .../pages/reference/extensions/groovy-dsl.adoc     |   6 -
 .../dsl/groovy/deployment/GroovyDslProcessor.java  |  64 +++++++++-
 .../runtime/src/main/doc/limitations.adoc          |   1 -
 integration-tests/groovy-dsl/pom.xml               |   5 -
 .../main/resources/routes/routes-with-eip.groovy   |  36 ++++++
 .../camel/quarkus/dsl/groovy/GroovyDslTest.java    | 142 ++++++++++++++-------
 6 files changed, 191 insertions(+), 63 deletions(-)

diff --git a/docs/modules/ROOT/pages/reference/extensions/groovy-dsl.adoc b/docs/modules/ROOT/pages/reference/extensions/groovy-dsl.adoc
index f9c62daa0d..1926f73527 100644
--- a/docs/modules/ROOT/pages/reference/extensions/groovy-dsl.adoc
+++ b/docs/modules/ROOT/pages/reference/extensions/groovy-dsl.adoc
@@ -43,9 +43,3 @@ Or add the coordinates to your existing project:
 ifeval::[{doc-show-user-guide-link} == true]
 Check the xref:user-guide/index.adoc[User guide] for more information about writing Camel Quarkus applications.
 endif::[]
-
-[id="extensions-groovy-dsl-camel-quarkus-limitations"]
-== Camel Quarkus limitations
-
-The Groovy extensions are not supported which means that the extensions defined in the Camel project are ignored, more details in https://github.com/apache/camel-quarkus/issues/4384[Groovy DSL - Add support of Groovy extensions issue #4384].
-
diff --git a/extensions/groovy-dsl/deployment/src/main/java/org/apache/camel/quarkus/dsl/groovy/deployment/GroovyDslProcessor.java b/extensions/groovy-dsl/deployment/src/main/java/org/apache/camel/quarkus/dsl/groovy/deployment/GroovyDslProcessor.java
index 5f23bb77c0..c7f114f6a8 100644
--- a/extensions/groovy-dsl/deployment/src/main/java/org/apache/camel/quarkus/dsl/groovy/deployment/GroovyDslProcessor.java
+++ b/extensions/groovy-dsl/deployment/src/main/java/org/apache/camel/quarkus/dsl/groovy/deployment/GroovyDslProcessor.java
@@ -19,14 +19,19 @@ package org.apache.camel.quarkus.dsl.groovy.deployment;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 
 import io.quarkus.deployment.annotations.BuildProducer;
 import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
 import io.quarkus.deployment.builditem.FeatureBuildItem;
 import io.quarkus.deployment.builditem.GeneratedClassBuildItem;
 import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
@@ -34,6 +39,10 @@ import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
 import io.quarkus.deployment.pkg.steps.NativeBuild;
 import io.quarkus.maven.dependency.ResolvedDependency;
 import io.quarkus.paths.PathCollection;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.ExchangePattern;
+import org.apache.camel.Message;
 import org.apache.camel.quarkus.core.deployment.main.CamelMainHelper;
 import org.apache.camel.quarkus.dsl.groovy.runtime.Configurer;
 import org.apache.camel.quarkus.support.dsl.deployment.DslGeneratedClassBuildItem;
@@ -44,6 +53,9 @@ import org.codehaus.groovy.control.CompilationUnit;
 import org.codehaus.groovy.control.CompilerConfiguration;
 import org.codehaus.groovy.control.Phases;
 import org.codehaus.groovy.tools.GroovyClass;
+import org.jboss.jandex.ClassInfo;
+import org.jboss.jandex.DotName;
+import org.jboss.jandex.IndexView;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -53,6 +65,11 @@ import static org.apache.camel.quarkus.support.dsl.deployment.DslSupportProcesso
 public class GroovyDslProcessor {
 
     private static final Logger LOG = LoggerFactory.getLogger(GroovyDslProcessor.class);
+    private static final List<Class<?>> CAMEL_REFLECTIVE_CLASSES = Arrays.asList(
+            Exchange.class,
+            Message.class,
+            ExchangePattern.class,
+            CamelContext.class);
     private static final String PACKAGE_NAME = "org.apache.camel.quarkus.dsl.groovy.generated";
     private static final String FILE_FORMAT = """
             package %s
@@ -117,12 +134,47 @@ public class GroovyDslProcessor {
         }
     }
 
-    // To put it back once the Groovy extensions will be supported (https://github.com/apache/camel-quarkus/issues/4384)
-    //    @BuildStep
-    //    void registerNativeImageResources(BuildProducer<ServiceProviderBuildItem> serviceProvider) {
-    //        serviceProvider
-    //                .produce(ServiceProviderBuildItem.allProvidersFromClassPath("org.codehaus.groovy.runtime.ExtensionModule"));
-    //    }
+    @BuildStep(onlyIf = NativeBuild.class)
+    void registerReflectiveClasses(
+            BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
+            CombinedIndexBuildItem combinedIndexBuildItem) {
+
+        IndexView view = combinedIndexBuildItem.getIndex();
+
+        for (Class<?> type : CAMEL_REFLECTIVE_CLASSES) {
+            DotName name = DotName.createSimple(type.getName());
+
+            if (type.isInterface()) {
+                for (ClassInfo info : view.getAllKnownImplementors(name)) {
+                    reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, info.name().toString()));
+                }
+            } else {
+                for (ClassInfo info : view.getAllKnownSubclasses(name)) {
+                    reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, info.name().toString()));
+                }
+            }
+
+            reflectiveClass.produce(new ReflectiveClassBuildItem(true, type.isEnum(), type));
+        }
+
+        Set<Class<?>> types = new HashSet<>();
+        // Register all the Camel return types of public methods of the camel reflective classes for reflection to
+        // be accessible in native mode from a Groovy resource
+        for (Class<?> c : CAMEL_REFLECTIVE_CLASSES) {
+            for (Method method : c.getMethods()) {
+                if (!method.getDeclaringClass().equals(Object.class)) {
+                    Class<?> returnType = method.getReturnType();
+                    if (returnType.getPackageName().startsWith("org.apache.camel.")
+                            && !CAMEL_REFLECTIVE_CLASSES.contains(returnType)) {
+                        types.add(returnType);
+                    }
+                }
+            }
+        }
+        // Allow access to methods by reflection to be accessible in native mode from a Groovy resource
+        reflectiveClass.produce(new ReflectiveClassBuildItem(false, true, false, types.toArray(new Class<?>[0])));
+
+    }
 
     /**
      * Convert a Groovy script into a Groovy class to be able to compile it.
diff --git a/extensions/groovy-dsl/runtime/src/main/doc/limitations.adoc b/extensions/groovy-dsl/runtime/src/main/doc/limitations.adoc
deleted file mode 100644
index a77743a491..0000000000
--- a/extensions/groovy-dsl/runtime/src/main/doc/limitations.adoc
+++ /dev/null
@@ -1 +0,0 @@
-The Groovy extensions are not supported which means that the extensions defined in the Camel project are ignored, more details in https://github.com/apache/camel-quarkus/issues/4384[Groovy DSL - Add support of Groovy extensions issue #4384].
diff --git a/integration-tests/groovy-dsl/pom.xml b/integration-tests/groovy-dsl/pom.xml
index 07f11a865a..2b35814bb7 100644
--- a/integration-tests/groovy-dsl/pom.xml
+++ b/integration-tests/groovy-dsl/pom.xml
@@ -66,11 +66,6 @@
             <artifactId>quarkus-junit5</artifactId>
             <scope>test</scope>
         </dependency>
-        <dependency>
-            <groupId>io.rest-assured</groupId>
-            <artifactId>rest-assured</artifactId>
-            <scope>test</scope>
-        </dependency>
         <dependency>
             <groupId>org.assertj</groupId>
             <artifactId>assertj-core</artifactId>
diff --git a/integration-tests/groovy-dsl/src/main/resources/routes/routes-with-eip.groovy b/integration-tests/groovy-dsl/src/main/resources/routes/routes-with-eip.groovy
new file mode 100644
index 0000000000..8d21881910
--- /dev/null
+++ b/integration-tests/groovy-dsl/src/main/resources/routes/routes-with-eip.groovy
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+from('direct:routes-with-eip-setBody')
+    .id('routes-with-eip-setBody')
+    .setBody { "true" }
+
+from('direct:routes-with-eip-body')
+    .id('routes-with-eip-body')
+    .transform().body { b -> "true"}
+
+from('direct:routes-with-eip-message')
+        .id('routes-with-eip-message')
+        .transform().message { m -> m.body = "true"}
+
+from('direct:routes-with-eip-exchange')
+        .id('routes-with-eip-exchange')
+        .transform().exchange { e -> e.in.body = "true"}
+
+from('direct:routes-with-eip-process')
+        .id('routes-with-eip-process')
+        .process {e -> e.in.body = "true" }
diff --git a/integration-tests/groovy-dsl/src/test/java/org/apache/camel/quarkus/dsl/groovy/GroovyDslTest.java b/integration-tests/groovy-dsl/src/test/java/org/apache/camel/quarkus/dsl/groovy/GroovyDslTest.java
index fdc6a4faf4..b0a0b6ae6a 100644
--- a/integration-tests/groovy-dsl/src/test/java/org/apache/camel/quarkus/dsl/groovy/GroovyDslTest.java
+++ b/integration-tests/groovy-dsl/src/test/java/org/apache/camel/quarkus/dsl/groovy/GroovyDslTest.java
@@ -16,64 +16,116 @@
  */
 package org.apache.camel.quarkus.dsl.groovy;
 
+import java.net.URL;
+
+import io.quarkus.test.common.http.TestHTTPResource;
 import io.quarkus.test.junit.QuarkusTest;
-import io.restassured.RestAssured;
 import org.apache.camel.dsl.groovy.GroovyRoutesBuilderLoader;
-import org.hamcrest.CoreMatchers;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.util.EntityUtils;
 import org.junit.jupiter.api.Test;
 
+import static org.assertj.core.api.Assertions.assertThat;
+
 @QuarkusTest
 class GroovyDslTest {
 
+    @TestHTTPResource
+    URL url;
+
     @Test
-    void groovyHello() {
-        RestAssured.given()
-                .body("John Smith")
-                .post("/groovy-dsl/hello")
-                .then()
-                .statusCode(200)
-                .body(CoreMatchers.is("Hello John Smith from Groovy!"));
+    void groovyHello() throws Exception {
+        try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
+            // Given
+            HttpPost httpPost = new HttpPost(this.url.toExternalForm() + "/groovy-dsl/hello");
+            httpPost.setEntity(new StringEntity("John Smith", ContentType.TEXT_PLAIN));
+
+            // When
+            HttpResponse httpResponse = client.execute(httpPost);
+
+            // Then
+            assertThat(httpResponse.getStatusLine().getStatusCode()).isEqualTo(HttpStatus.SC_OK);
+            assertThat(EntityUtils.toString(httpResponse.getEntity())).isEqualTo("Hello John Smith from Groovy!");
+        }
     }
 
     @Test
-    void testMainInstanceWithJavaRoutes() {
-        RestAssured.given()
-                .get("/groovy-dsl/main/groovyRoutesBuilderLoader")
-                .then()
-                .statusCode(200)
-                .body(CoreMatchers.is(GroovyRoutesBuilderLoader.class.getName()));
-
-        RestAssured.given()
-                .get("/groovy-dsl/main/routeBuilders")
-                .then()
-                .statusCode(200)
-                .body(CoreMatchers.is(""));
-
-        RestAssured.given()
-                .get("/groovy-dsl/main/routes")
-                .then()
-                .statusCode(200)
-                .body(CoreMatchers.is(
-                        "my-groovy-route,routes-with-components-configuration,routes-with-dataformats-configuration,routes-with-endpoint-dsl,routes-with-error-handler,routes-with-languages-configuration,routes-with-rest,routes-with-rest-dsl-get,routes-with-rest-dsl-post,routes-with-rest-get,routes-with-rest-post"));
-        RestAssured.given()
-                .get("/groovy-dsl/main/successful/routes")
-                .then()
-                .statusCode(200)
-                .body(CoreMatchers.is("5"));
+    void testMainInstanceWithJavaRoutes() throws Exception {
+        try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
+            // Given
+            HttpUriRequest request = new HttpGet(this.url.toExternalForm() + "/groovy-dsl/main/groovyRoutesBuilderLoader");
+
+            // When
+            HttpResponse httpResponse = client.execute(request);
+
+            // Then
+            assertThat(httpResponse.getStatusLine().getStatusCode()).isEqualTo(HttpStatus.SC_OK);
+            assertThat(EntityUtils.toString(httpResponse.getEntity())).isEqualTo(GroovyRoutesBuilderLoader.class.getName());
+
+            // Given
+            request = new HttpGet(this.url.toExternalForm() + "/groovy-dsl/main/routeBuilders");
+
+            // When
+            httpResponse = client.execute(request);
+
+            // Then
+            assertThat(httpResponse.getStatusLine().getStatusCode()).isEqualTo(HttpStatus.SC_OK);
+            assertThat(EntityUtils.toString(httpResponse.getEntity())).isEmpty();
+
+            // Given
+            request = new HttpGet(this.url.toExternalForm() + "/groovy-dsl/main/routes");
+
+            // When
+            httpResponse = client.execute(request);
+
+            // Then
+            assertThat(httpResponse.getStatusLine().getStatusCode()).isEqualTo(HttpStatus.SC_OK);
+            assertThat(EntityUtils.toString(httpResponse.getEntity())).isEqualTo(
+                    "my-groovy-route,routes-with-components-configuration,routes-with-dataformats-configuration,routes-with-eip-body,routes-with-eip-exchange,routes-with-eip-message,routes-with-eip-process,routes-with-eip-setBody,routes-with-endpoint-dsl,routes-with-error-handler,routes-with-languages-configuration,routes-with-rest,routes-with-rest-dsl-get,routes-with-rest-dsl-post,routes-with-rest-get,routes-with-rest-post");
+
+            // Given
+            request = new HttpGet(this.url.toExternalForm() + "/groovy-dsl/main/successful/routes");
+
+            // When
+            httpResponse = client.execute(request);
+
+            // Then
+            assertThat(httpResponse.getStatusLine().getStatusCode()).isEqualTo(HttpStatus.SC_OK);
+            assertThat(EntityUtils.toString(httpResponse.getEntity())).isEqualTo("10");
+        }
     }
 
     @Test
-    void testRestEndpoints() {
-        RestAssured.given()
-                .get("/root/my/path/get")
-                .then()
-                .statusCode(200)
-                .body(CoreMatchers.is("Hello World"));
-        RestAssured.given()
-                .body("Will")
-                .post("/root/post")
-                .then()
-                .statusCode(200)
-                .body(CoreMatchers.is("Hello Will"));
+    void testRestEndpoints() throws Exception {
+        try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
+            // Given
+            final HttpGet httpGet = new HttpGet(this.url.toExternalForm() + "/root/my/path/get");
+
+            // When
+            HttpResponse httpResponse = client.execute(httpGet);
+
+            // Then
+            assertThat(httpResponse.getStatusLine().getStatusCode()).isEqualTo(HttpStatus.SC_OK);
+            assertThat(EntityUtils.toString(httpResponse.getEntity())).isEqualTo("Hello World");
+
+            // Given
+            HttpPost httpPost = new HttpPost(this.url.toExternalForm() + "/root/post");
+            httpPost.setEntity(new StringEntity("Will", ContentType.TEXT_PLAIN));
+
+            // When
+            httpResponse = client.execute(httpPost);
+
+            // Then
+            assertThat(httpResponse.getStatusLine().getStatusCode()).isEqualTo(HttpStatus.SC_OK);
+            assertThat(EntityUtils.toString(httpResponse.getEntity())).isEqualTo("Hello Will");
+        }
     }
 }