io.milton.servlet.SpringMiltonFilter.java Source code

Java tutorial

Introduction

Here is the source code for io.milton.servlet.SpringMiltonFilter.java

Source

/*
 * 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.
 */
package io.milton.servlet;

import io.milton.config.HttpManagerBuilder;
import io.milton.http.HttpManager;
import io.milton.http.Request;
import io.milton.http.ResourceFactory;
import io.milton.http.Response;
import io.milton.http.annotated.AnnotationResourceFactory;
import io.milton.http.template.JspViewResolver;
import io.milton.http.template.ViewResolver;
import io.milton.mail.MailServer;
import io.milton.mail.MailServerBuilder;

import java.io.File;
import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

/**
 * Loads the spring context either from a spring configuration XML file or a
 * spring @Configuration class.
 *
 * <p>
 * Use {@code contextConfigClass} to define the @Configuration class or
 * {@code contextConfigLocation} to define the Spring XML configuration file. If
 * any of them is defined, {@link SpringMiltonFilter} will try to load a file
 * named applicationContext.xml from the classpath.
 * <br>If it still fails, only the parent context will be considered.
 *
 * <p>
 * This filter then gets the bean named milton.http.manager and uses that for
 * Milton processing.
 *
 * <p>
 * The milton.http.manager bean can either be a {@link HttpManager} or it can be
 * a {@link HttpManagerBuilder}, in which case a {@link HttpManager} is
 * constructed from it
 *
 * <p>
 * Requests with a path which begins with one of the exclude paths will not be
 * processed by Milton. Instead, for these requests, the filter chain will be
 * invoked so the request can be serviced by JSP or a servlet, etc
 *
 * <p>
 * This uses an init parameter called {@code milton.exclude.paths}, which should
 * be a comma separated list of paths to ignore. For example:
 *
 * <pre>/static,/images,/login.jsp</pre>
 *
 * <p>
 * This allows non-Milton resources to be accessed, while still mapping all URLs
 * to Milton
 *
 * @author bradm
 */
public class SpringMiltonFilter implements javax.servlet.Filter {

    private static final Logger log = LoggerFactory.getLogger(SpringMiltonFilter.class);
    private ConfigurableApplicationContext context;
    private HttpManager httpManager;
    private MailServer mailServer;
    private ServletContext servletContext;

    /**
     * Resources with this as the first part of their path will not be served
     * from Milton. Instead, this filter will allow filter processing to
     * continue so they will be served by JSP or a servlet
     */
    private String[] excludeMiltonPaths;

    @Override
    public void init(FilterConfig fc) throws ServletException {
        log.info("init");

        initSpringApplicationContext(fc);

        servletContext = fc.getServletContext();

        String sExcludePaths = fc.getInitParameter(EXCLUDE_PATHS_SYSPROP);
        if (sExcludePaths != null) {
            log.info("init: exclude paths: " + sExcludePaths);
            excludeMiltonPaths = sExcludePaths.split(",");
        } else {
            log.info("init: exclude paths property has not been set in filter init param " + EXCLUDE_PATHS_SYSPROP);
        }

        Object milton = context.getBean("milton.http.manager");
        if (milton instanceof HttpManager) {
            this.httpManager = (HttpManager) milton;
        } else if (milton instanceof HttpManagerBuilder) {
            HttpManagerBuilder builder = (HttpManagerBuilder) milton;
            ResourceFactory rf = builder.getMainResourceFactory();
            if (rf instanceof AnnotationResourceFactory) {
                AnnotationResourceFactory arf = (AnnotationResourceFactory) rf;
                if (arf.getViewResolver() == null) {
                    ViewResolver viewResolver = new JspViewResolver(servletContext);
                    arf.setViewResolver(viewResolver);
                }
            }
            this.httpManager = builder.buildHttpManager();
        }

        // init mail server
        if (context.containsBean("milton.mail.server")) {
            log.info("init mailserver...");
            Object oMailServer = context.getBean("milton.mail.server");
            if (oMailServer instanceof MailServer) {
                mailServer = (MailServer) oMailServer;
            } else if (oMailServer instanceof MailServerBuilder) {
                MailServerBuilder builder = (MailServerBuilder) oMailServer;
                mailServer = builder.build();
            } else {
                throw new RuntimeException("Unsupported type: " + oMailServer.getClass() + " expected "
                        + MailServer.class + " or " + MailServerBuilder.class);
            }
            log.info("starting mailserver");
            mailServer.start();
        }
        log.info("Finished init");
    }

    private static final String EXCLUDE_PATHS_SYSPROP = "milton.exclude.paths";

    @SuppressWarnings("resource")
    protected void initSpringApplicationContext(FilterConfig fc) {

        final WebApplicationContext rootContext = WebApplicationContextUtils
                .getWebApplicationContext(fc.getServletContext());

        StaticApplicationContext parent;
        if (rootContext != null) {
            log.info("Found a root spring context, and using it");
            parent = new StaticApplicationContext(rootContext);
        } else {
            log.info("No root spring context");
            parent = new StaticApplicationContext();
        }

        final FilterConfigWrapper configWrapper = new FilterConfigWrapper(fc);
        parent.getBeanFactory().registerSingleton("config", configWrapper);
        parent.getBeanFactory().registerSingleton("servletContext", fc.getServletContext());
        File webRoot = new File(fc.getServletContext().getRealPath("/"));
        parent.getBeanFactory().registerSingleton("webRoot", webRoot);
        log.info("Registered root webapp path in: webroot=" + webRoot.getAbsolutePath());
        parent.refresh();

        final String configClass = fc.getInitParameter("contextConfigClass");
        final String sFiles = fc.getInitParameter("contextConfigLocation");

        ConfigurableApplicationContext ctx = null;
        if (StringUtils.isNotBlank(configClass)) {
            try {
                Class<?> clazz = Class.forName(configClass);
                final AnnotationConfigApplicationContext annotationCtx = new AnnotationConfigApplicationContext();
                annotationCtx.setParent(parent);
                annotationCtx.register(clazz);
                annotationCtx.refresh();
                ctx = annotationCtx;
            } catch (ClassNotFoundException e) {
                ctx = null;
                log.error("Unable to create a child context for Milton", e);
            }
        } else {
            String[] contextFiles;
            if (sFiles != null && sFiles.trim().length() > 0) {
                contextFiles = sFiles.split(" ");
            } else {
                contextFiles = new String[] { "applicationContext.xml" };
            }

            try {
                ctx = new ClassPathXmlApplicationContext(contextFiles, parent);
            } catch (BeansException e) {
                log.error("Unable to create a child context for Milton", e);
            }
        }

        if (ctx == null) {
            log.warn("No child context available, only using parent context");
            context = parent;
        } else {
            context = ctx;
        }

    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain fc)
            throws IOException, ServletException {
        if (req instanceof HttpServletRequest) {
            HttpServletRequest hsr = (HttpServletRequest) req;
            String url = hsr.getRequestURI();
            // Allow certain paths to be excluded from Milton, these might be other servlets, for example
            if (excludeMiltonPaths != null) {
                for (String s : excludeMiltonPaths) {
                    if (url.startsWith(s)) {
                        log.trace("doFilter: is excluded path");
                        fc.doFilter(req, resp);
                        return;
                    }
                }
            }
            log.trace("doFilter: begin milton processing");
            doMiltonProcessing((HttpServletRequest) req, (HttpServletResponse) resp);
        } else {
            log.trace("doFilter: request is not a supported type, continue with filter chain");
            fc.doFilter(req, resp);
            return;
        }
    }

    @Override
    public void destroy() {
        context.close();
        if (httpManager != null) {
            httpManager.shutdown();
        }
        if (mailServer != null) {
            mailServer.stop();
        }
    }

    private void doMiltonProcessing(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        try {
            MiltonServlet.setThreadlocals(req, resp);
            Request request = new io.milton.servlet.ServletRequest(req, servletContext);
            Response response = new io.milton.servlet.ServletResponse(resp);
            httpManager.process(request, response);
        } finally {
            MiltonServlet.clearThreadlocals();
            //resp.getOutputStream().flush();
            resp.flushBuffer();
        }
    }
}