Servlet Plugin Fixes

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:

  1. A string beginning with a '/' character and ending with a '/*' postfix is used for path mapping.
  2. A string beginning with a'*.' prefix is used as an extension mapping.
  3. A string containing only the '/' character indicates the "default" servlet of the application. In this case the servlet path is the request URI minus the context path and the path info is null.
  4. All other strings are used for exact matches only.

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:

  1. 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.
  2. 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);
}

Labels

added added Delete
spec spec Delete
information information Delete
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Apr 23, 2006

    Dylan Etkin says:

    This is being tracked as issue, http://jira.atlassian.com/browse/PLUG-11

    This is being tracked as issue, http://jira.atlassian.com/browse/PLUG-11

  2. 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:

    <servlet>
    <servlet-name>labels</servlet-name>
    <servlet-class>com.atlassian.confluence.servlet.LabelServlet</servlet-class>
    </servlet>

    At the moment I guess the only way to do this would be to manually edit the web.xml and change it to:

    <servlet>
    <servlet-name>labels</servlet-name>
    <servlet-class>com.mycustom.servlet.MyCustomLabelServlet</servlet-class>
    </servlet>

    right?

    1. 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.