You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ju...@apache.org on 2012/09/28 14:17:34 UTC
svn commit: r1391420 - in /sling/trunk/bundles/servlets/resolver: ./
src/main/java/org/apache/sling/servlets/resolver/internal/
src/main/java/org/apache/sling/servlets/resolver/internal/helper/
src/main/resources/res/ src/main/resources/res/ui/ src/tes...
Author: justin
Date: Fri Sep 28 12:17:34 2012
New Revision: 1391420
URL: http://svn.apache.org/viewvc?rev=1391420&view=rev
Log:
SLING-2562 - adding initial implementation of a servlet web console test tool based on work done by Konrad Windszus
Added:
sling/trunk/bundles/servlets/resolver/src/main/resources/res/
sling/trunk/bundles/servlets/resolver/src/main/resources/res/ui/
sling/trunk/bundles/servlets/resolver/src/main/resources/res/ui/styles.css (with props)
Modified:
sling/trunk/bundles/servlets/resolver/pom.xml
sling/trunk/bundles/servlets/resolver/src/main/java/org/apache/sling/servlets/resolver/internal/SlingServletResolver.java
sling/trunk/bundles/servlets/resolver/src/main/java/org/apache/sling/servlets/resolver/internal/helper/ResourceCollector.java
sling/trunk/bundles/servlets/resolver/src/test/java/org/apache/sling/servlets/resolver/internal/SlingServletResolverTest.java
Modified: sling/trunk/bundles/servlets/resolver/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/resolver/pom.xml?rev=1391420&r1=1391419&r2=1391420&view=diff
==============================================================================
--- sling/trunk/bundles/servlets/resolver/pom.xml (original)
+++ sling/trunk/bundles/servlets/resolver/pom.xml Fri Sep 28 12:17:34 2012
@@ -59,7 +59,6 @@
<configuration>
<instructions>
<Import-Package>
- javax.jcr;resolution:=optional,
org.apache.sling.api.resource;provide:=true,
*
</Import-Package>
@@ -77,13 +76,9 @@
<artifactId>servlet-api</artifactId>
</dependency>
<dependency>
- <groupId>javax.jcr</groupId>
- <artifactId>jcr</artifactId>
- </dependency>
- <dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.api</artifactId>
- <version>2.2.5-SNAPSHOT</version>
+ <version>2.2.4</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -126,7 +121,7 @@
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.commons.testing</artifactId>
- <version>2.0.2-incubator</version>
+ <version>2.0.11-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
Modified: sling/trunk/bundles/servlets/resolver/src/main/java/org/apache/sling/servlets/resolver/internal/SlingServletResolver.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/resolver/src/main/java/org/apache/sling/servlets/resolver/internal/SlingServletResolver.java?rev=1391420&r1=1391419&r2=1391420&view=diff
==============================================================================
--- sling/trunk/bundles/servlets/resolver/src/main/java/org/apache/sling/servlets/resolver/internal/SlingServletResolver.java (original)
+++ sling/trunk/bundles/servlets/resolver/src/main/java/org/apache/sling/servlets/resolver/internal/SlingServletResolver.java Fri Sep 28 12:17:34 2012
@@ -28,12 +28,17 @@ import static org.osgi.framework.Constan
import static org.osgi.service.component.ComponentConstants.COMPONENT_NAME;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -41,9 +46,12 @@ import java.util.concurrent.ConcurrentHa
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
@@ -81,6 +89,7 @@ import org.apache.sling.servlets.resolve
import org.apache.sling.servlets.resolver.internal.helper.SlingServletConfig;
import org.apache.sling.servlets.resolver.internal.resource.ServletResourceProvider;
import org.apache.sling.servlets.resolver.internal.resource.ServletResourceProviderFactory;
+import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
@@ -232,6 +241,8 @@ public class SlingServletResolver
*/
private String[] defaultExtensions;
+ private ServletResolverWebConsolePlugin plugin;
+
// ---------- ServletResolver interface -----------------------------------
/**
@@ -877,6 +888,8 @@ public class SlingServletResolver
// and finally register as event listener
this.eventHandlerReg = context.getBundleContext().registerService(EventHandler.class.getName(), this,
properties);
+
+ this.plugin = new ServletResolverWebConsolePlugin(context.getBundleContext());
}
/**
@@ -886,6 +899,10 @@ public class SlingServletResolver
// stop registering of servlets immediately
this.context = null;
+ if (this.plugin != null) {
+ this.plugin.dispose();
+ }
+
// unregister event handler
if (this.eventHandlerReg != null) {
this.eventHandlerReg.unregister();
@@ -1139,4 +1156,243 @@ public class SlingServletResolver
// this is deprecated, but we just delegate anyway
return this.decorate(resource);
}
+
+ @SuppressWarnings("serial")
+ class ServletResolverWebConsolePlugin extends HttpServlet {
+ private static final String PARAMETER_URL = "url";
+ private static final String PARAMETER_METHOD = "method";
+
+ private ServiceRegistration service;
+
+ public ServletResolverWebConsolePlugin(BundleContext context) {
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put(Constants.SERVICE_DESCRIPTION,
+ "Sling Servlet Resolver Web Console Plugin");
+ props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation");
+ props.put(Constants.SERVICE_PID, getClass().getName());
+ props.put("felix.webconsole.label", "servletresolver");
+ props.put("felix.webconsole.title", "Sling Servlet Resolver");
+ props.put("felix.webconsole.css", "/servletresolver/res/ui/styles.css");
+
+ service = context.registerService(
+ new String[] { "javax.servlet.Servlet" }, this, props);
+ }
+
+ public void dispose() {
+ if (service != null) {
+ service.unregister();
+ service = null;
+ }
+ }
+
+ class DecomposedURL {
+ final String extension;
+ final String path;
+ final String[] selectors;
+
+ DecomposedURL(String url) {
+ if (url != null) {
+ final int lastDot = url.lastIndexOf('.');
+ final int firstDot = url.indexOf('.');
+ if (lastDot > 0) {
+ final int slashInExtension = url.indexOf('/', lastDot);
+ // strip suffix, if any
+ if (slashInExtension > 0) {
+ extension = url.substring(lastDot + 1, slashInExtension);
+ } else {
+ extension = url.substring(lastDot + 1);
+ }
+
+ path = url.substring(0, firstDot);
+ if (lastDot != firstDot) {
+ // has selectors
+ final String selectorString = url.substring(firstDot + 1, lastDot);
+ selectors = selectorString.split("\\.");
+ } else {
+ selectors = new String[0];
+ }
+ } else {
+ extension = "";
+ path = url;
+ selectors = new String[0];
+ }
+ } else {
+ extension = "";
+ path = "";
+ selectors = new String[0];
+ }
+ }
+ }
+
+ @Override
+ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ final String url = request.getParameter(PARAMETER_URL);
+ final DecomposedURL decomposed = new DecomposedURL(url);
+ String method = request.getParameter(PARAMETER_METHOD);
+ if (StringUtils.isBlank(method)) {
+ method = "GET";
+ }
+
+ ResourceResolver resourceResolver = null;
+ try {
+ resourceResolver = resourceResolverFactory.getAdministrativeResourceResolver(null);
+
+ final PrintWriter pw = response.getWriter();
+
+ pw.print("<form method='get'>");
+ pw.println("<table class='content' cellpadding='0' cellspacing='0' width='100%'>");
+
+ titleHtml(
+ pw,
+ "Servlet Resolver Test",
+ "To check which servlet is responsible for rendering a response, enter a request path into " +
+ "the field and click 'Resolve' to resolve it.");
+
+ tr(pw);
+ tdLabel(pw, "URL");
+ tdContent(pw);
+
+ pw.println("<input type='text' name='" + PARAMETER_URL + "' value='" +
+ (url != null ? url : "") + "' class='input' size='50'>");
+ closeTd(pw);
+ closeTr(pw);
+ closeTr(pw);
+
+ tr(pw);
+ tdLabel(pw, "Method");
+ tdContent(pw);
+ pw.println("<select name='" + PARAMETER_METHOD + "'>");
+ pw.println("<option value='GET'>GET</option>");
+ pw.println("<option value='POST'>POST</option>");
+ pw.println("</select>");
+ pw.println(" <input type='submit'" +
+ "' value='Resolve' class='submit'>");
+
+ closeTd(pw);
+ closeTr(pw);
+
+ if (StringUtils.isNotBlank(url)) {
+ tr(pw);
+ tdLabel(pw, "Decomposed URL");
+ tdContent(pw);
+ pw.println("<dl>");
+ pw.println("<dt>Path</dt>");
+ pw.println("<dd>" + decomposed.path + "</dd>");
+ pw.println("<dt>Selectors</dt>");
+ pw.print("<dd>");
+ if (decomposed.selectors.length == 0) {
+ pw.print("<none>");
+ } else {
+ pw.print("[");
+ pw.print(StringUtils.join(decomposed.selectors, ", "));
+ pw.print("]");
+ }
+ pw.println("</dd>");
+ pw.println("<dt>Extension</dt>");
+ pw.println("<dd>" + decomposed.extension + "</dd>");
+ pw.println("</dl>");
+ closeTd(pw);
+ closeTr(pw);
+ }
+
+ if (StringUtils.isNotBlank(decomposed.path)) {
+ final Collection<Resource> servlets;
+ Resource resource = resourceResolver.resolve(decomposed.path);
+ if (resource.adaptTo(Servlet.class) != null) {
+ servlets = Collections.singleton(resource);
+ } else {
+ final ResourceCollector locationUtil = ResourceCollector.create(resource, defaultWorkspaceName, decomposed.extension, executionPaths, defaultExtensions, method, decomposed.selectors);
+ servlets = locationUtil.getServlets(resourceResolver);
+ }
+ tr(pw);
+ tdLabel(pw, " ");
+ tdContent(pw);
+
+ if (servlets == null || servlets.isEmpty()) {
+ pw.println("Could not find a suitable servlet for this request!");
+ } else {
+ pw.println("Candidate servlets and scripts in order of preference:<br/>");
+ pw.println("<ol class='servlets'>");
+ Iterator<Resource> iterator = servlets.iterator();
+ outputServlets(pw, iterator);
+ pw.println("</ol>");
+ }
+ pw.println("</td>");
+ closeTr(pw);
+ }
+
+ pw.println("</table>");
+ pw.print("</form>");
+ } catch (LoginException e) {
+ throw new ServletException(e);
+ } finally {
+ if (resourceResolver != null) {
+ resourceResolver.close();
+ }
+ }
+ }
+
+ private void tdContent(final PrintWriter pw) {
+ pw.print("<td class='content' colspan='2'>");
+ }
+
+ private void closeTd(final PrintWriter pw) {
+ pw.print("</td>");
+ }
+
+ @SuppressWarnings("unused")
+ private URL getResource(final String path) {
+ if (path.startsWith("/servletresolver/res/ui")) {
+ return this.getClass().getResource(path.substring(16));
+ } else {
+ return null;
+ }
+ }
+
+ private void closeTr(final PrintWriter pw) {
+ pw.println("</tr>");
+ }
+
+ private void tdLabel(final PrintWriter pw, final String label) {
+ pw.println("<td class='content'>" + label + "</td>");
+ }
+
+ private void tr(final PrintWriter pw) {
+ pw.println("<tr class='content'>");
+ }
+
+ private void outputServlets(PrintWriter pw, Iterator<Resource> iterator) {
+ while (iterator.hasNext()) {
+ Resource candidateResource = iterator.next();
+ Servlet candidate = candidateResource.adaptTo(Servlet.class);
+ if (candidate != null) {
+ boolean isOptingServlet = false;
+
+ if (candidate instanceof SlingScript) {
+ pw.println("<li>" + candidateResource.getPath() + "</li>");
+ } else {
+ if (candidate instanceof OptingServlet) {
+ isOptingServlet = true;
+ }
+ pw.println("<li>" + candidate.getClass().getName() + (isOptingServlet ? " (OptingServlet)" : "") + "</li>");
+ }
+ }
+ }
+ }
+
+ private void titleHtml(PrintWriter pw, String title, String description) {
+ tr(pw);
+ pw.println("<th colspan='3' class='content container'>" + title +
+ "</th>");
+ closeTr(pw);
+
+ if (description != null) {
+ tr(pw);
+ pw.println("<td colspan='3' class='content'>" + description +
+ "</th>");
+ closeTr(pw);
+ }
+ }
+
+ }
}
Modified: sling/trunk/bundles/servlets/resolver/src/main/java/org/apache/sling/servlets/resolver/internal/helper/ResourceCollector.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/resolver/src/main/java/org/apache/sling/servlets/resolver/internal/helper/ResourceCollector.java?rev=1391420&r1=1391419&r2=1391420&view=diff
==============================================================================
--- sling/trunk/bundles/servlets/resolver/src/main/java/org/apache/sling/servlets/resolver/internal/helper/ResourceCollector.java (original)
+++ sling/trunk/bundles/servlets/resolver/src/main/java/org/apache/sling/servlets/resolver/internal/helper/ResourceCollector.java Fri Sep 28 12:17:34 2012
@@ -18,10 +18,12 @@
*/
package org.apache.sling.servlets.resolver.internal.helper;
+import java.util.Arrays;
import java.util.Iterator;
import java.util.Set;
import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.lang.StringUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.request.RequestPathInfo;
import org.apache.sling.api.resource.Resource;
@@ -86,8 +88,20 @@ public class ResourceCollector extends A
public static ResourceCollector create(
final SlingHttpServletRequest request, final String workspaceName,
final String[] executionPaths, final String[] defaultExtensions) {
- boolean isDefaultExtension = ArrayUtils.contains(defaultExtensions, request.getRequestPathInfo().getExtension());
- return new ResourceCollector(request, workspaceName, executionPaths, isDefaultExtension);
+ final RequestPathInfo requestPathInfo = request.getRequestPathInfo();
+ final boolean isDefaultExtension = ArrayUtils.contains(defaultExtensions, requestPathInfo.getExtension());
+ return new ResourceCollector(request.getResource(), workspaceName, requestPathInfo.getExtension(), executionPaths, isDefaultExtension,
+ request.getMethod(), requestPathInfo.getSelectors());
+ }
+
+ public static ResourceCollector create(final Resource resource,
+ final String workspaceName,
+ final String extension,
+ final String[] executionPaths, final String[] defaultExtensions,
+ final String methodName, final String[] selectors
+ ) {
+ boolean isDefaultExtension = ArrayUtils.contains(defaultExtensions, extension);
+ return new ResourceCollector(resource, workspaceName, extension, executionPaths, isDefaultExtension, methodName, selectors);
}
/**
@@ -133,7 +147,7 @@ public class ResourceCollector extends A
/**
* Creates a <code>ResourceCollector</code> finding servlets and scripts for
- * the given <code>methodName</code>.
+ * the given <code>resource</code>.
*
* @param methodName The <code>methodName</code> used to find scripts for.
* This must not be <code>null</code>.
@@ -144,34 +158,35 @@ public class ResourceCollector extends A
* {@link org.apache.sling.servlets.resolver.internal.ServletResolverConstants#DEFAULT_SERVLET_NAME}
* is assumed.
*/
- private ResourceCollector(final SlingHttpServletRequest request,
- final String workspaceName, final String[] executionPaths,
- final boolean isDefaultExtension) {
+ private ResourceCollector(final Resource resource,
+ final String workspaceName, final String extension,
+ final String[] executionPaths,
+ final boolean isDefaultExtension,
+ final String methodName,
+ final String[] selectors) {
super(ServletResolverConstants.DEFAULT_SERVLET_NAME,
- request.getResource().getResourceType(),
- request.getResource().getResourceSuperType(), workspaceName,
- request.getRequestPathInfo().getExtension(), executionPaths);
- this.methodName = request.getMethod();
-
- this.suffExt = "." + extension;
- this.suffMethod = "." + methodName;
- this.suffExtMethod = suffExt + suffMethod;
-
- RequestPathInfo requestpaInfo = request.getRequestPathInfo();
-
- this.requestSelectors = requestpaInfo.getSelectors();
- this.numRequestSelectors = requestSelectors.length;
-
- this.isGet = "GET".equals(methodName) || "HEAD".equals(methodName);
- this.isDefaultExtension = isDefaultExtension;
-
- // create the hash code once
- final String key = methodName + ':' + baseResourceType + ':'
- + extension + ':' + requestpaInfo.getSelectorString() + ':'
- + (this.resourceType == null ? "" : this.resourceType) + ':'
- + (this.resourceSuperType == null ? "" : this.resourceSuperType)
- + ':' + (this.workspaceName == null ? "" : this.workspaceName);
- this.hashCode = key.hashCode();
+ resource.getResourceType(),
+ resource.getResourceSuperType(), workspaceName,
+ extension, executionPaths);
+ this.methodName = methodName;
+
+ this.suffExt = "." + extension;
+ this.suffMethod = "." + methodName;
+ this.suffExtMethod = suffExt + suffMethod;
+
+ this.requestSelectors = selectors;
+ this.numRequestSelectors = requestSelectors.length;
+
+ this.isGet = "GET".equals(methodName) || "HEAD".equals(methodName);
+ this.isDefaultExtension = isDefaultExtension;
+
+ // create the hash code once
+ final String key = methodName + ':' + baseResourceType + ':'
+ + extension + ':' + StringUtils.join(requestSelectors, '.') + ':'
+ + (this.resourceType == null ? "" : this.resourceType) + ':'
+ + (this.resourceSuperType == null ? "" : this.resourceSuperType)
+ + ':' + (this.workspaceName == null ? "" : this.workspaceName);
+ this.hashCode = key.hashCode();
}
protected void getWeightedResources(final Set<Resource> resources,
Added: sling/trunk/bundles/servlets/resolver/src/main/resources/res/ui/styles.css
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/resolver/src/main/resources/res/ui/styles.css?rev=1391420&view=auto
==============================================================================
--- sling/trunk/bundles/servlets/resolver/src/main/resources/res/ui/styles.css (added)
+++ sling/trunk/bundles/servlets/resolver/src/main/resources/res/ui/styles.css Fri Sep 28 12:17:34 2012
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+ol.servlets {
+ margin-top: 10px;
+}
+ol.servlets li {
+ list-style: decimal;
+ margin-left: 20px;
+}
+
+.content dt {
+ font-weight: bold;
+}
+.content dd {
+ margin-left: 10px;
+}
\ No newline at end of file
Propchange: sling/trunk/bundles/servlets/resolver/src/main/resources/res/ui/styles.css
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/bundles/servlets/resolver/src/main/resources/res/ui/styles.css
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision Rev URL
Modified: sling/trunk/bundles/servlets/resolver/src/test/java/org/apache/sling/servlets/resolver/internal/SlingServletResolverTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/resolver/src/test/java/org/apache/sling/servlets/resolver/internal/SlingServletResolverTest.java?rev=1391420&r1=1391419&r2=1391420&view=diff
==============================================================================
--- sling/trunk/bundles/servlets/resolver/src/test/java/org/apache/sling/servlets/resolver/internal/SlingServletResolverTest.java (original)
+++ sling/trunk/bundles/servlets/resolver/src/test/java/org/apache/sling/servlets/resolver/internal/SlingServletResolverTest.java Fri Sep 28 12:17:34 2012
@@ -24,6 +24,7 @@ import static org.junit.Assert.assertTru
import java.lang.reflect.Field;
import java.util.ArrayList;
+import java.util.Dictionary;
import java.util.List;
import java.util.Map;
@@ -38,6 +39,7 @@ import org.apache.sling.api.resource.Res
import org.apache.sling.api.resource.ResourceUtil;
import org.apache.sling.api.servlets.OptingServlet;
import org.apache.sling.commons.testing.osgi.MockBundle;
+import org.apache.sling.commons.testing.osgi.MockBundleContext;
import org.apache.sling.commons.testing.osgi.MockComponentContext;
import org.apache.sling.commons.testing.osgi.MockServiceReference;
import org.apache.sling.commons.testing.sling.MockResource;
@@ -51,6 +53,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
@RunWith(JMock.class)
public class SlingServletResolverTest {
@@ -115,8 +118,19 @@ public class SlingServletResolverTest {
resolverField.set(servletResolver, factory);
MockBundle bundle = new MockBundle(1L);
+ MockBundleContext bundleContext = new MockBundleContext(bundle) {
+ @Override
+ public ServiceRegistration registerService(String s, Object o, Dictionary dictionary) {
+ return null;
+ }
+
+ @Override
+ public ServiceRegistration registerService(String[] strings, Object o, Dictionary dictionary) {
+ return null;
+ }
+ };
MockComponentContext mockComponentContext = new MockComponentContext(
- bundle, SlingServletResolverTest.this.servlet);
+ bundleContext, SlingServletResolverTest.this.servlet);
MockServiceReference serviceReference = new MockServiceReference(bundle);
serviceReference.setProperty(Constants.SERVICE_ID, 1L);
serviceReference.setProperty(SLING_SERLVET_NAME,