Return-Path: X-Original-To: apmail-sling-commits-archive@www.apache.org Delivered-To: apmail-sling-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 1C35F9A45 for ; Fri, 28 Sep 2012 12:18:26 +0000 (UTC) Received: (qmail 31290 invoked by uid 500); 28 Sep 2012 12:18:26 -0000 Delivered-To: apmail-sling-commits-archive@sling.apache.org Received: (qmail 31108 invoked by uid 500); 28 Sep 2012 12:18:21 -0000 Mailing-List: contact commits-help@sling.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@sling.apache.org Delivered-To: mailing list commits@sling.apache.org Received: (qmail 31072 invoked by uid 99); 28 Sep 2012 12:18:20 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 28 Sep 2012 12:18:20 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 28 Sep 2012 12:18:18 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id EF1E023888E3; Fri, 28 Sep 2012 12:17:34 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: 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... Date: Fri, 28 Sep 2012 12:17:34 -0000 To: commits@sling.apache.org From: justin@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20120928121734.EF1E023888E3@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org 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 @@ - javax.jcr;resolution:=optional, org.apache.sling.api.resource;provide:=true, * @@ -77,13 +76,9 @@ servlet-api - javax.jcr - jcr - - org.apache.sling org.apache.sling.api - 2.2.5-SNAPSHOT + 2.2.4 provided @@ -126,7 +121,7 @@ org.apache.sling org.apache.sling.commons.testing - 2.0.2-incubator + 2.0.11-SNAPSHOT test 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 props = new Hashtable(); + 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("
"); + pw.println(""); + + 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(""); + closeTd(pw); + closeTr(pw); + closeTr(pw); + + tr(pw); + tdLabel(pw, "Method"); + tdContent(pw); + pw.println(""); + pw.println("  "); + + closeTd(pw); + closeTr(pw); + + if (StringUtils.isNotBlank(url)) { + tr(pw); + tdLabel(pw, "Decomposed URL"); + tdContent(pw); + pw.println("
"); + pw.println("
Path
"); + pw.println("
" + decomposed.path + "
"); + pw.println("
Selectors
"); + pw.print("
"); + if (decomposed.selectors.length == 0) { + pw.print("<none>"); + } else { + pw.print("["); + pw.print(StringUtils.join(decomposed.selectors, ", ")); + pw.print("]"); + } + pw.println("
"); + pw.println("
Extension
"); + pw.println("
" + decomposed.extension + "
"); + pw.println("
"); + closeTd(pw); + closeTr(pw); + } + + if (StringUtils.isNotBlank(decomposed.path)) { + final Collection 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:
"); + pw.println("
    "); + Iterator iterator = servlets.iterator(); + outputServlets(pw, iterator); + pw.println("
"); + } + pw.println(""); + closeTr(pw); + } + + pw.println("
"); + pw.print("
"); + } catch (LoginException e) { + throw new ServletException(e); + } finally { + if (resourceResolver != null) { + resourceResolver.close(); + } + } + } + + private void tdContent(final PrintWriter pw) { + pw.print(""); + } + + private void closeTd(final PrintWriter pw) { + pw.print(""); + } + + @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(""); + } + + private void tdLabel(final PrintWriter pw, final String label) { + pw.println("" + label + ""); + } + + private void tr(final PrintWriter pw) { + pw.println(""); + } + + private void outputServlets(PrintWriter pw, Iterator 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("
  • " + candidateResource.getPath() + "
  • "); + } else { + if (candidate instanceof OptingServlet) { + isOptingServlet = true; + } + pw.println("
  • " + candidate.getClass().getName() + (isOptingServlet ? " (OptingServlet)" : "") + "
  • "); + } + } + } + } + + private void titleHtml(PrintWriter pw, String title, String description) { + tr(pw); + pw.println("" + title + + ""); + closeTr(pw); + + if (description != null) { + tr(pw); + pw.println("" + description + + ""); + 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 ResourceCollector finding servlets and scripts for - * the given methodName. + * the given resource. * * @param methodName The methodName used to find scripts for. * This must not be null. @@ -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 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,