Argh. I forgot to remove my changes to the pom.xml before committing... the environment I was testing this in didn't have the latest API jar.
But question... is there a reason servlets.resolver should depend upon the latest API? It works OK with a dependency on 2.1.0. Also, the JCR API isn't used as all. I'm inclined to keep my pom.xml changes unless there's a good reason for these extra dependencies. If they are required, I'll roll back my changes. Regards, Justin On Fri, Sep 28, 2012 at 1:17 PM, <[email protected]> wrote: > 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, > > >
