This is an automated email from the ASF dual-hosted git repository. bdelacretaz pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git
commit 6fcdce023922b81869a1ff72a84f077e41a351cf Author: Bertrand Delacretaz <[email protected]> AuthorDate: Thu Jul 4 11:59:23 2019 +0200 Using SlingRequestProcessor --- graalvm/README.md | 11 +- graalvm/pom.xml | 34 +++- .../engine/impl/SlingRequestProcessorWrapper.java | 18 ++ .../apache/sling/graalvm/http/SlingResource.java | 55 ++++++ .../apache/sling/graalvm/osgi/SlingContext.java | 35 ++++ .../sling/graalvm/sling/MockErrorHandler.java | 23 +++ .../sling/graalvm/sling/MockFilterManager.java | 12 ++ .../apache/sling/graalvm/sling/MockResource.java | 54 ++++++ .../sling/graalvm/sling/MockResourceProvider.java | 27 +++ .../sling/graalvm/sling/MockResourceResolver.java | 184 +++++++++++++++++++++ .../sling/graalvm/sling/MockServletResolver.java | 41 +++++ .../sling/graalvm/http/NativeSlingResourceIT.java | 8 + .../sling/graalvm/http/SlingResourceTest.java | 30 ++++ 13 files changed, 526 insertions(+), 6 deletions(-) diff --git a/graalvm/README.md b/graalvm/README.md index e9bf7ff..d71ea46 100644 --- a/graalvm/README.md +++ b/graalvm/README.md @@ -22,4 +22,13 @@ At which point the `/hello` path works: curl http://localhost:8080/hello Hello, at Mon Jul 01 17:38:00 CEST 2019 -To run as a Docker container see `src/main/docker/Dockerfile.native` \ No newline at end of file +To run as a Docker container see `src/main/docker/Dockerfile.native` + +## TODO +Running in **quarkus:dev mode fails so far** ("no SCR metadata found"), even if +using + + mvn clean compile bnd:bnd-process quarkus:dev + +The **native startup time** is about 5 seconds on my box, but just a few +milliseconds when runnning in a Docker image. \ No newline at end of file diff --git a/graalvm/pom.xml b/graalvm/pom.xml index 23f1270..89fee09 100644 --- a/graalvm/pom.xml +++ b/graalvm/pom.xml @@ -32,6 +32,10 @@ </dependency> <dependency> <groupId>io.quarkus</groupId> + <artifactId>quarkus-resteasy-jsonb</artifactId> + </dependency> + <dependency> + <groupId>io.quarkus</groupId> <artifactId>quarkus-junit5</artifactId> <scope>test</scope> </dependency> @@ -41,11 +45,6 @@ <scope>test</scope> </dependency> <dependency> - <groupId>org.apache.sling</groupId> - <artifactId>org.apache.sling.testing.osgi-mock.junit5</artifactId> - <version>2.4.8</version> - </dependency> - <dependency> <groupId>org.osgi</groupId> <artifactId>org.osgi.core</artifactId> <version>6.0.0</version> @@ -60,6 +59,31 @@ <artifactId>org.osgi.service.component.annotations</artifactId> <version>1.3.0</version> </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.testing.osgi-mock.junit5</artifactId> + <version>2.4.8</version> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.api</artifactId> + <version>2.20.0</version> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.engine</artifactId> + <version>2.6.20</version> + </dependency> + <dependency> + <groupId>commons-fileupload</groupId> + <artifactId>commons-fileupload</artifactId> + <version>1.4</version> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-all</artifactId> + <version>1.10.19</version> + </dependency> </dependencies> <build> <plugins> diff --git a/graalvm/src/main/java/org/apache/sling/engine/impl/SlingRequestProcessorWrapper.java b/graalvm/src/main/java/org/apache/sling/engine/impl/SlingRequestProcessorWrapper.java new file mode 100644 index 0000000..0f2d901 --- /dev/null +++ b/graalvm/src/main/java/org/apache/sling/engine/impl/SlingRequestProcessorWrapper.java @@ -0,0 +1,18 @@ +package org.apache.sling.engine.impl; + +import org.apache.sling.engine.SlingRequestProcessor; +import org.apache.sling.engine.impl.SlingRequestProcessorImpl; +import org.apache.sling.graalvm.sling.MockErrorHandler; +import org.apache.sling.graalvm.sling.MockFilterManager; +import org.apache.sling.graalvm.sling.MockServletResolver; +import org.osgi.service.component.annotations.Component; + +/** TODO hack: using the engine.impl package to access package private methods... */ +@Component(service=SlingRequestProcessor.class) +public class SlingRequestProcessorWrapper extends SlingRequestProcessorImpl { + public SlingRequestProcessorWrapper() { + this.setServletResolver(new MockServletResolver()); + this.setErrorHandler(new MockErrorHandler()); + this.setFilterManager(new MockFilterManager()); + } +} \ No newline at end of file diff --git a/graalvm/src/main/java/org/apache/sling/graalvm/http/SlingResource.java b/graalvm/src/main/java/org/apache/sling/graalvm/http/SlingResource.java new file mode 100644 index 0000000..566d8f8 --- /dev/null +++ b/graalvm/src/main/java/org/apache/sling/graalvm/http/SlingResource.java @@ -0,0 +1,55 @@ +package org.apache.sling.graalvm.http; + +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.doReturn; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.apache.sling.graalvm.osgi.SlingContext; +import org.apache.sling.graalvm.sling.MockServletResolver; +import org.apache.sling.spi.resource.provider.ResourceProvider; +import org.mockito.Mockito; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.engine.SlingRequestProcessor; + +@Path("/sling/{resourcePath: [^/][a-zA-Z/_0-9\\.]*}") +@Produces(MediaType.APPLICATION_JSON) +public class SlingResource { + + @Context + private HttpServletRequest request; + + @GET + public Response sling(@PathParam("resourcePath") String resourcePath) throws IOException { + final SlingRequestProcessor p = SlingContext.get().getService(SlingRequestProcessor.class); + assert (p != null); + final ResourceResolver resolver = SlingContext.get().getService(ResourceResolver.class); + assert (resolver != null); + + final StringWriter sw = new StringWriter(); + final HttpServletResponse resp = Mockito.mock(HttpServletResponse.class); + when(resp.getWriter()).thenReturn(new PrintWriter(sw)); + + try { + p.processRequest(request, resp, resolver); + } catch (Exception e) { + throw new RuntimeException(e); + } + + final Resource r = (Resource)request.getAttribute(MockServletResolver.RESOURCE_ATTR); + return Response.ok(r).build(); + } +} \ No newline at end of file diff --git a/graalvm/src/main/java/org/apache/sling/graalvm/osgi/SlingContext.java b/graalvm/src/main/java/org/apache/sling/graalvm/osgi/SlingContext.java new file mode 100644 index 0000000..bf9e27b --- /dev/null +++ b/graalvm/src/main/java/org/apache/sling/graalvm/osgi/SlingContext.java @@ -0,0 +1,35 @@ +package org.apache.sling.graalvm.osgi; + +import org.apache.sling.engine.impl.SlingRequestProcessorWrapper; +import org.apache.sling.graalvm.sling.MockResourceProvider; +import org.apache.sling.graalvm.sling.MockResourceResolver; +import org.apache.sling.testing.mock.osgi.junit5.OsgiContext; + +public class SlingContext { + private static OsgiContext context; + + public static OsgiContext get() { + if(context != null) { + return context; + } + + synchronized(SlingContext.class) { + context = initialize(); + } + + return context; + } + + /** This is where we wire the system, like the OSGi framework + * would do. As it seems hard to run that framework in a GraalVM + * environment for now, we wire things statically. + */ + private static OsgiContext initialize() { + final OsgiContext result = new OsgiContext(); + final MockResourceProvider mrp = new MockResourceProvider(); + result.registerInjectActivateService(new MockResourceResolver(mrp)); + result.registerInjectActivateService(mrp); + result.registerInjectActivateService(new SlingRequestProcessorWrapper()); + return result; + } +} \ No newline at end of file diff --git a/graalvm/src/main/java/org/apache/sling/graalvm/sling/MockErrorHandler.java b/graalvm/src/main/java/org/apache/sling/graalvm/sling/MockErrorHandler.java new file mode 100644 index 0000000..e933d09 --- /dev/null +++ b/graalvm/src/main/java/org/apache/sling/graalvm/sling/MockErrorHandler.java @@ -0,0 +1,23 @@ +package org.apache.sling.graalvm.sling; + +import java.io.IOException; + +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.SlingHttpServletResponse; +import org.apache.sling.engine.servlets.ErrorHandler; + +public class MockErrorHandler implements ErrorHandler { + + @Override + public void handleError(int status, String message, SlingHttpServletRequest request, + SlingHttpServletResponse response) throws IOException { + response.getWriter().write(getClass().getSimpleName() + ':' + status + ':' + message); + } + + @Override + public void handleError(Throwable throwable, SlingHttpServletRequest request, SlingHttpServletResponse response) + throws IOException { + response.getWriter().write(getClass().getSimpleName() + ':' + throwable.toString()); + } + +} \ No newline at end of file diff --git a/graalvm/src/main/java/org/apache/sling/graalvm/sling/MockFilterManager.java b/graalvm/src/main/java/org/apache/sling/graalvm/sling/MockFilterManager.java new file mode 100644 index 0000000..ee07726 --- /dev/null +++ b/graalvm/src/main/java/org/apache/sling/graalvm/sling/MockFilterManager.java @@ -0,0 +1,12 @@ +package org.apache.sling.graalvm.sling; + +import org.apache.sling.engine.impl.filter.ServletFilterManager; +import org.apache.sling.engine.impl.helper.SlingServletContext; +import org.mockito.Mockito; +import org.osgi.framework.BundleContext; + +public class MockFilterManager extends ServletFilterManager { + public MockFilterManager() { + super(Mockito.mock(BundleContext.class), Mockito.mock(SlingServletContext.class)); + } +} \ No newline at end of file diff --git a/graalvm/src/main/java/org/apache/sling/graalvm/sling/MockResource.java b/graalvm/src/main/java/org/apache/sling/graalvm/sling/MockResource.java new file mode 100644 index 0000000..b5c73cb --- /dev/null +++ b/graalvm/src/main/java/org/apache/sling/graalvm/sling/MockResource.java @@ -0,0 +1,54 @@ +package org.apache.sling.graalvm.sling; + +import org.apache.sling.api.resource.AbstractResource; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceMetadata; +import org.apache.sling.api.resource.ResourceResolver; + +import io.quarkus.runtime.annotations.RegisterForReflection; + +@RegisterForReflection +public class MockResource extends AbstractResource { + + private final String path; + private final ResourceResolver resolver; + private final ResourceMetadata metadata; + + public MockResource(ResourceResolver resolver, String path) { + this.resolver = resolver; + this.path = path; + this.metadata = new ResourceMetadata(); + metadata.put(ResourceMetadata.RESOLUTION_PATH, path); + metadata.put(ResourceMetadata.RESOLUTION_PATH_INFO, path); + } + + @Override + public String getPath() { + return path; + } + + @Override + public String getResourceType() { + return "mock/resource"; + } + + @Override + public String getResourceSuperType() { + return null; + } + + @Override + public ResourceMetadata getResourceMetadata() { + return metadata; + } + + @Override + public ResourceResolver getResourceResolver() { + return resolver; + } + + @Override + public Resource getParent() { + return null; + } +} \ No newline at end of file diff --git a/graalvm/src/main/java/org/apache/sling/graalvm/sling/MockResourceProvider.java b/graalvm/src/main/java/org/apache/sling/graalvm/sling/MockResourceProvider.java new file mode 100644 index 0000000..555ad85 --- /dev/null +++ b/graalvm/src/main/java/org/apache/sling/graalvm/sling/MockResourceProvider.java @@ -0,0 +1,27 @@ +package org.apache.sling.graalvm.sling; + +import java.util.Iterator; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.spi.resource.provider.ResolveContext; +import org.apache.sling.spi.resource.provider.ResourceContext; +import org.apache.sling.spi.resource.provider.ResourceProvider; +import org.osgi.service.component.annotations.Component; + +@Component(service=ResourceProvider.class) +public class MockResourceProvider extends ResourceProvider<MockResource> { + + private static final String MAGIC = "chouc"; + + @Override + public Resource getResource(ResolveContext<MockResource> ctx, String path, ResourceContext resourceContext, + Resource parent) { + return path.contains(MAGIC) ? new MockResource(null, path) : null; + } + + @Override + public Iterator<Resource> listChildren(ResolveContext<MockResource> ctx, Resource parent) { + return null; + } + +} \ No newline at end of file diff --git a/graalvm/src/main/java/org/apache/sling/graalvm/sling/MockResourceResolver.java b/graalvm/src/main/java/org/apache/sling/graalvm/sling/MockResourceResolver.java new file mode 100644 index 0000000..2ac4af7 --- /dev/null +++ b/graalvm/src/main/java/org/apache/sling/graalvm/sling/MockResourceResolver.java @@ -0,0 +1,184 @@ +package org.apache.sling.graalvm.sling; + +import java.util.Iterator; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.sling.api.resource.LoginException; +import org.apache.sling.api.resource.PersistenceException; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.spi.resource.provider.ResourceProvider; +import org.osgi.service.component.annotations.Component; + +@Component(service=ResourceResolver.class) +public class MockResourceResolver implements ResourceResolver { + + private final ResourceProvider<?> provider; + + public MockResourceResolver(MockResourceProvider provider) { + this.provider = provider; + } + + @Override + public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) { + return null; + } + + @Override + public Resource resolve(HttpServletRequest request, String absPath) { + return provider.getResource(null, absPath, null, null); + } + + @Override + public Resource resolve(String absPath) { + return null; + } + + @Override + public Resource resolve(HttpServletRequest request) { + return null; + } + + @Override + public String map(String resourcePath) { + return null; + } + + @Override + public String map(HttpServletRequest request, String resourcePath) { + return null; + } + + @Override + public Resource getResource(String path) { + return null; + } + + @Override + public Resource getResource(Resource base, String path) { + return null; + } + + @Override + public String[] getSearchPath() { + return null; + } + + @Override + public Iterator<Resource> listChildren(Resource parent) { + return null; + } + + @Override + public Resource getParent(Resource child) { + return null; + } + + @Override + public Iterable<Resource> getChildren(Resource parent) { + return null; + } + + @Override + public Iterator<Resource> findResources(String query, String language) { + return null; + } + + @Override + public Iterator<Map<String, Object>> queryResources(String query, String language) { + return null; + } + + @Override + public boolean hasChildren(Resource resource) { + return false; + } + + @Override + public ResourceResolver clone(Map<String, Object> authenticationInfo) throws LoginException { + return null; + } + + @Override + public boolean isLive() { + return false; + } + + @Override + public void close() { + + } + + @Override + public String getUserID() { + return null; + } + + @Override + public Iterator<String> getAttributeNames() { + return null; + } + + @Override + public Object getAttribute(String name) { + return null; + } + + @Override + public void delete(Resource resource) throws PersistenceException { + + } + + @Override + public Resource create(Resource parent, String name, Map<String, Object> properties) throws PersistenceException { + return null; + } + + @Override + public void revert() { + + } + + @Override + public void commit() throws PersistenceException { + + } + + @Override + public boolean hasChanges() { + return false; + } + + @Override + public String getParentResourceType(Resource resource) { + return null; + } + + @Override + public String getParentResourceType(String resourceType) { + return null; + } + + @Override + public boolean isResourceType(Resource resource, String resourceType) { + return false; + } + + @Override + public void refresh() { + + } + + @Override + public Resource copy(String srcAbsPath, String destAbsPath) throws PersistenceException { + return null; + } + + @Override + public Resource move(String srcAbsPath, String destAbsPath) throws PersistenceException { + return null; + } + +} \ No newline at end of file diff --git a/graalvm/src/main/java/org/apache/sling/graalvm/sling/MockServletResolver.java b/graalvm/src/main/java/org/apache/sling/graalvm/sling/MockServletResolver.java new file mode 100644 index 0000000..f0bf437 --- /dev/null +++ b/graalvm/src/main/java/org/apache/sling/graalvm/sling/MockServletResolver.java @@ -0,0 +1,41 @@ +package org.apache.sling.graalvm.sling; + +import java.io.IOException; + +import javax.servlet.Servlet; + +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.SlingHttpServletResponse; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.api.servlets.ServletResolver; +import org.apache.sling.api.servlets.SlingSafeMethodsServlet; + +public class MockServletResolver implements ServletResolver { + + public static final String RESOURCE_ATTR = MockServletResolver.class.getName(); + + static class DummyServlet extends SlingSafeMethodsServlet { + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException { + request.setAttribute(RESOURCE_ATTR, request.getResource()); + } + } + + @Override + public Servlet resolveServlet(SlingHttpServletRequest request) { + return new DummyServlet(); + } + + @Override + public Servlet resolveServlet(Resource resource, String scriptName) { + return new DummyServlet(); + } + + @Override + public Servlet resolveServlet(ResourceResolver resolver, String scriptName) { + return new DummyServlet(); + } +} \ No newline at end of file diff --git a/graalvm/src/test/java/org/apache/sling/graalvm/http/NativeSlingResourceIT.java b/graalvm/src/test/java/org/apache/sling/graalvm/http/NativeSlingResourceIT.java new file mode 100644 index 0000000..9e7bcca --- /dev/null +++ b/graalvm/src/test/java/org/apache/sling/graalvm/http/NativeSlingResourceIT.java @@ -0,0 +1,8 @@ +package org.apache.sling.graalvm.http; + +import io.quarkus.test.junit.SubstrateTest; + +@SubstrateTest +public class NativeSlingResourceIT extends SlingResourceTest { + // Execute the same tests but in native mode. +} \ No newline at end of file diff --git a/graalvm/src/test/java/org/apache/sling/graalvm/http/SlingResourceTest.java b/graalvm/src/test/java/org/apache/sling/graalvm/http/SlingResourceTest.java new file mode 100644 index 0000000..d401d16 --- /dev/null +++ b/graalvm/src/test/java/org/apache/sling/graalvm/http/SlingResourceTest.java @@ -0,0 +1,30 @@ +package org.apache.sling.graalvm.http; + +import io.quarkus.test.junit.QuarkusTest; + +import org.junit.jupiter.api.Test; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.equalTo; + +import javax.ws.rs.core.MediaType; + +@QuarkusTest +public class SlingResourceTest { + + @Test + public void testSlingResourceEndpoint() { + final String prefix = "/sling/"; + final String path = "chouc/route"; + final String resourceType = "mock/resource"; + + given() + .when().get(prefix + path) + .then() + .statusCode(200) + .contentType(MediaType.APPLICATION_JSON) + .body("path", equalTo(prefix + path)) + .body("resourceType", equalTo(resourceType)); + } + +} \ No newline at end of file
