Spark: Why did you use a Filter instead of a Servlet?

Created on 9 Sep 2014  ·  12Comments  ·  Source: perwendel/spark

I'm using Shiro for Access-Verification. Shiro is a Filter which makes sense but if Spark is also a Filter it always depends on "who is first" in the filter chain if it works or not.
I took your Filter-code change the Servlet-according-things and not it works nicely together with Shiro.
So again - is there a reason for the SparkFilter?

Here is my Servlet:

package at.mikemitterer.webapp.guice.spark;

import com.google.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import spark.route.RouteMatcherFactory;
import spark.servlet.SparkApplication;
import spark.webserver.MatcherFilter;

import javax.inject.Inject;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @since 09.09.14, 12:14
 */
@Singleton
public class SparkServlet extends HttpServlet {
    @SuppressWarnings("unused")
    private static final Logger logger = LoggerFactory.getLogger(SparkServlet.class);

    private static final String SLASH_WILDCARD       = "/*";
    private static final String SLASH                = "/";
    private static final String FILTER_MAPPING_PARAM = "filterMappingUrlPattern";

    private String filterPath;
    private MatcherFilter matcherFilter;

    final SparkApplication application;

    @Inject
    public SparkServlet(final SparkApplication application) {
        this.application = application;
    }

    @Override
    public void init(final ServletConfig config) throws ServletException {
        super.init(config);
        Access.runFromServlet();

        application.init();

        filterPath = getConfigPath(config);
        matcherFilter = new MatcherFilter(RouteMatcherFactory.get(), true, false);
    }

    @Override
    protected void service(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
        //super.service(req, resp);

        HttpServletRequest httpRequest = (HttpServletRequest) req; // NOSONAR

        final String relativePath = getRelativePath(httpRequest, filterPath);

        logger.debug(relativePath);

        HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(httpRequest) {
            @Override
            public String getRequestURI() {
                return relativePath;
            }
        };
        matcherFilter.doFilter(requestWrapper, resp, null);
    }

    // --------------------------------------------------------------------------------------------
    // private
    // --------------------------------------------------------------------------------------------

    private String getConfigPath(ServletConfig config) {
        String result = config.getInitParameter(FILTER_MAPPING_PARAM);
        if (result == null || result.equals(SLASH_WILDCARD)) {
            return "";

        } else if (!result.startsWith(SLASH) || !result.endsWith(SLASH_WILDCARD)) {
            throw new RuntimeException(
                    "The " + FILTER_MAPPING_PARAM + " must start with \"/\" and end with \"/*\". It's: "
                            + result); // NOSONAR
        }
        return result.substring(1, result.length() - 1);
    }

    private String getRelativePath(HttpServletRequest request, String filterPath) {
        String path = request.getRequestURI();
        String contextPath = request.getContextPath();

        path = path.substring(contextPath.length());

        if (path.length() > 0) {
            path = path.substring(1);
        }

        if (!path.startsWith(filterPath) && filterPath.equals(path + SLASH)) {
            path += SLASH;
        }
        if (path.startsWith(filterPath)) {
            path = path.substring(filterPath.length());
        }

        if (!path.startsWith(SLASH)) {
            path = SLASH + path;
        }

        return path;
    }
}
Question (inner workings)

Most helpful comment

I'm curious when this issue is going to be prioritized into a release. Until it is resolved - either with the solution I've provided in #671 or another solution - my company needs to maintain a custom version of SparkServlet and keep it up-to-date with Spark releases. That adds quite a bit of overhead and risk to the upgrade process for us.

All 12 comments

@MikeMitterer
I actually don't remember why I chose to implement it as a filter. Would a change to servlet affect performance in any way?

Performance is nice here - so I would say no.

+1. Using filter (instead of servlet) is quite strange. Moving logic to servlet will allow adding custom filters (such as security filter, mentioned above). Here is a part of J2EE tutorial:

A filter is an object that can transform the header and content (or both) of a request or response. Filters differ from web components in that filters usually do not themselves create a response. Instead, a filter provides functionality that can be “attached” to any kind of web resource. Consequently, a filter should not have any dependencies on a web resource for which it is acting as a filter; this way it can be composed with more than one type of web resource.

The main tasks that a filter can perform are as follows:

Query the request and act accordingly.
Block the request-and-response pair from passing any further.
Modify the request headers and data. You do this by providing a customized version of the request.
Modify the response headers and data. You do this by providing a customized version of the response.
Interact with external resources.

so i've been looking into this a bit and i think it does need to be a filter otherwise in the current implementation, it won't be able to handle any static resources that have been defined via spark.

It can handle static resources. Actually, behaviour is completely the same with filter:
https://github.com/perwendel/spark/pull/254

any update on this?

I've just provided an alternate implementation in #671

It would be great to see some solution provided - one of the open PRs or another tack - in Spark 2.6 so I can stop maintaining my own version of the SparkServlet.

Any update on this issue? It's frustrating and potentially extra work each time we upgrade Spark that we have to maintain our own version of SparkServlet.

Bump

I'm also facing an issue which requires me to use a servlet as opposed to a filter. I would be very interested in something like this being merged.

Bump

I'm curious when this issue is going to be prioritized into a release. Until it is resolved - either with the solution I've provided in #671 or another solution - my company needs to maintain a custom version of SparkServlet and keep it up-to-date with Spark releases. That adds quite a bit of overhead and risk to the upgrade process for us.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

itaied246 picture itaied246  ·  7Comments

chibongho picture chibongho  ·  4Comments

kiriti999 picture kiriti999  ·  4Comments

fgaule picture fgaule  ·  4Comments

cnmade picture cnmade  ·  6Comments