| Base Path needs to be worked out from the url-pattern
Currently, if you want the pathInfo and servletPath's to be set correctly, they need a helping hand. If you have /plugins/servlet/myplugin/dwr/index.html and you want it to be reported as index.html and not /myplugin/dwr/index.html then you need to add the following to your servlet descriptor in atlassian-plugin.xml: <init-param> <param-name>base-path</param-name> <param-value>/myplugin/dwr</param-value> </init-param> A commenter based on the servlet mapping spec defined the mapping processing as:
Suggestions / Code Samples on how to take /myplugin/dwr/index.html and from /myplugin/dwr/* (or any other url-pattern) get the correct outcome (in this case: /index.html) are most welcome! |
I recently tried Running DWR inside a Plugin, and quickly came up against some issues:
- When DWR used req.getPathInfo() it contained the plugin portion of the path, when it was expecting the servlet to be a the root - the servlet path suffered a similar issue. (eg: if the url used was http://conf.server.dom/plugins/servlet/myplugin/dwr/index.html then the path info was being reported as /myplugin/dwr/index.html and not /index.html as it should have been.
- When DWR used getServletContext().getResourceAsStream() it pulled the resource from Confluence and not the plugin as it should.
I have created a patch for Confluence to fix this.
ServletModuleContainerServlet (com.atlassian.confluence.servlet)
This solves the first problem of the incorrectly reported path info and servlet path variables.
Add the following inner class:
public static class PluginHttpRequestWrapper extends HttpServletRequestWrapper { private HttpServletRequest request; private String basePath; public PluginHttpRequestWrapper(HttpServletRequest request, String basePath) { super(request); this.request = request; this.basePath = basePath == null ? "" : basePath; } public String getServletPath() { return super.getServletPath() + basePath; } public String getPathInfo() { String pathInfo = super.getPathInfo(); if (pathInfo != null && pathInfo.length() >= basePath.length()) { return pathInfo.substring( basePath.length() ); } return pathInfo; } }
Add the following just before the servlet.service(...); line:
// Dan Hardiker :: Wrap the request with something which will remove the base path request = new PluginHttpRequestWrapper(request, servlet.getInitParameter("base-path"));
ServletModuleManager(com.atlassian.confluence.plugin.descriptor.servlet)
This proxies the ServletContext requests straight through to the backing context, with the exception of URL getResource(String) and InputStream getResourceAsStream(String) which are looked up in the plugin's ClassLoader first before being proxied (if unsuccessful).
Add the following inner class (if only there were a Wrapper for ServletContext):
public static class PluginServletContextWrapper implements ServletContext { private ServletContext context; private Servlet servlet; public PluginServletContextWrapper(ServletContext context, Servlet servlet) { this.context = context; this.servlet = servlet; } public ServletContext getContext(String string) { return context.getContext(string); } public int getMajorVersion() { return context.getMajorVersion(); } public int getMinorVersion() { return context.getMinorVersion(); } public String getMimeType(String string) { return context.getMimeType(string); } public Set getResourcePaths(String string) { return context.getResourcePaths(string); } public URL getResource(String string) throws MalformedURLException { URL resource = servlet.getClass().getClassLoader().getResource(string); if (resource == null) { resource = context.getResource(string); } return resource; } public InputStream getResourceAsStream(String string) { InputStream resource = servlet.getClass().getClassLoader().getResourceAsStream(string); if (resource == null) { resource = context.getResourceAsStream(string); } return resource; } public RequestDispatcher getRequestDispatcher(String string) { return context.getRequestDispatcher(string); } public RequestDispatcher getNamedDispatcher(String string) { return context.getNamedDispatcher(string); } public Servlet getServlet(String string) throws ServletException { return context.getServlet(string); } public Enumeration getServlets() { return context.getServlets(); } public Enumeration getServletNames() { return context.getServletNames(); } public void log(String string) { context.log(string); } public void log(Exception exception, String string) { context.log(exception, string); } public void log(String string, Throwable throwable) { context.log(string, throwable); } public String getRealPath(String string) { return context.getRealPath(string); } public String getServerInfo() { return context.getServerInfo(); } public String getInitParameter(String string) { return getInitParameter(string); } public Enumeration getInitParameterNames() { return getInitParameterNames(); } public Object getAttribute(String string) { return getAttribute(string); } public Enumeration getAttributeNames() { return getAttributeNames(); } public void setAttribute(String string, Object object) { setAttribute(string, object); } public void removeAttribute(String string) { removeAttribute(string); } public String getServletContextName() { return getServletContextName(); } }
Find if (descriptor != null) and replace that block of code with:
if (descriptor != null) { servlet = descriptor.getServlet(); // Wrapping the servlet context so to allow resource lookup inside of the plugin // exposing as a final variable to allow it to be used in an anonymous inner class final ServletContext servletContext = new PluginServletContextWrapper(servletConfig.getServletContext(), servlet); servlet.init(new ServletConfig() { public String getServletName() { return descriptor.getName(); } public ServletContext getServletContext() { return servletContext; } public String getInitParameter(String s) { return (String) descriptor.getInitParams().get(s); } public Enumeration getInitParameterNames() { return Collections.enumeration(descriptor.getInitParams().keySet()); } }); inittedServlets.put(completeKey, servlet); }

Comments (3)
Apr 23, 2006
Dylan Etkin says:
This is being tracked as issue, http://jira.atlassian.com/browse/PLUG-11This is being tracked as issue, http://jira.atlassian.com/browse/PLUG-11
Jul 01
Kevin Aubeelack says:
Is there any way to override a core servlet functionality in confluence by using...Is there any way to override a core servlet functionality in confluence by using a plugin (i.e. upload a jar which overrides the following functionality?) In web.xml you can find:
At the moment I guess the only way to do this would be to manually edit the web.xml and change it to:
right?
Jul 01
David Peterson [CustomWare] says:
I believe you are correct. Not only that, but your 'MyCustomLabelServlet' class ...I believe you are correct. Not only that, but your 'MyCustomLabelServlet' class would have to be in a jar in WEB-INF/lib, not uploaded.