com.agiletec.apsadmin.system.dispatcher.Struts2ServletDispatcher.java Source code

Java tutorial

Introduction

Here is the source code for com.agiletec.apsadmin.system.dispatcher.Struts2ServletDispatcher.java

Source

/*
*
* Copyright 2012 Entando S.r.l. (http://www.entando.com) All rights reserved.
*
* This file is part of Entando software.
* Entando is a free software; 
* you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) as published by the Free Software Foundation; version 2.
* 
* See the file License for the specific language governing permissions   
* and limitations under the License
* 
* 
* 
* Copyright 2012 Entando S.r.l. (http://www.entando.com) All rights reserved.
*
*/
package com.agiletec.apsadmin.system.dispatcher;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLDecoder;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.RequestUtils;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.StrutsStatics;
import org.apache.struts2.dispatcher.Dispatcher;
import org.apache.struts2.dispatcher.mapper.ActionMapper;
import org.apache.struts2.dispatcher.mapper.ActionMapping;

import com.agiletec.apsadmin.system.ApsAdminSystemConstants;
import com.agiletec.apsadmin.system.dispatcher.mapper.ExtendedDefaultActionMapper;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ClassLoaderUtil;
import com.opensymphony.xwork2.util.profiling.UtilTimerStack;

/**
 * Servlet a servizio della showlet "Internal Servlet".
 * La servlet ha lo scopo di permettere la erogazione nel Front-End di jAPS (all'interno dei frames di pagine) 
 * di funzionalit implementate su base Struts2.
 * @author E.Santoboni
 */
public class Struts2ServletDispatcher extends HttpServlet implements StrutsStatics {

    @Override
    public void service(ServletRequest req, ServletResponse resp) throws ServletException, IOException {
        ActionMapper actionMapper = new ExtendedDefaultActionMapper();
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        ServletContext servletContext = getServletContext();
        String timerKey = "FilterDispatcher_doFilter: ";
        try {
            UtilTimerStack.push(timerKey);
            request = prepareDispatcherAndWrapRequest(request, response);
            ActionMapping mapping;
            try {
                mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager());
            } catch (Exception ex) {
                LOG.error("error getting ActionMapping", ex);
                dispatcher.sendError(request, response, servletContext,
                        HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
                return;
            }

            if (mapping == null) {
                // there is no action in this request, should we look for a static resource?
                String resourcePath = RequestUtils.getServletPath(request);

                if ("".equals(resourcePath) && null != request.getPathInfo()) {
                    resourcePath = request.getPathInfo();
                }

                if (serveStatic && resourcePath.startsWith("/struts")) {
                    String name = resourcePath.substring("/struts".length());
                    findStaticResource(name, request, response);
                }

                return;
            }

            dispatcher.serviceAction(request, response, servletContext, mapping);

        } finally {
            try {
                cleanUp(req);
            } finally {
                UtilTimerStack.pop(timerKey);
            }
        }
    }

    /**
      * Provide a logging instance.
      */
    private static final Log LOG = LogFactory.getLog(Struts2ServletDispatcher.class);

    /**
     * Store set of path prefixes to use with static resources.
     */
    private String[] pathPrefixes;

    /**
     * Provide a formatted date for setting heading information when caching static content.
     */
    private final Calendar lastModifiedCal = Calendar.getInstance();

    /**
     * Store state of StrutsConstants.STRUTS_SERVE_STATIC_CONTENT setting.
     */
    private static boolean serveStatic;

    /**
     * Store state of StrutsConstants.STRUTS_SERVE_STATIC_BROWSER_CACHE setting.
     */
    private static boolean serveStaticBrowserCache;

    /**
     * Store state of StrutsConstants.STRUTS_I18N_ENCODING setting.
     */
    private static String encoding;

    /**
     * Expose Dispatcher instance to subclass.
     */
    protected Dispatcher dispatcher;

    /**
     * Calls dispatcher.cleanup,
     * which in turn releases local threads and destroys any DispatchListeners.
     *
     * @see javax.servlet.Filter#destroy()
     */
    public void destroy() {
        if (dispatcher == null) {
            LOG.warn("something is seriously wrong, Dispatcher is not initialized (null) ");
        } else {
            dispatcher.cleanup();
        }
    }

    /**
     * Modify state of StrutsConstants.STRUTS_SERVE_STATIC_CONTENT setting.
     * @param val New setting
     */
    @Inject(StrutsConstants.STRUTS_SERVE_STATIC_CONTENT)
    public static void setServeStaticContent(String val) {
        serveStatic = "true".equals(val);
    }

    /**
     * Modify state of StrutsConstants.STRUTS_SERVE_STATIC_BROWSER_CACHE setting.
     * @param val New setting
     */
    @Inject(StrutsConstants.STRUTS_SERVE_STATIC_BROWSER_CACHE)
    public static void setServeStaticBrowserCache(String val) {
        serveStaticBrowserCache = "true".equals(val);
    }

    /**
     * Modify state of StrutsConstants.STRUTS_I18N_ENCODING setting.
     * @param val New setting
     */
    @Inject(StrutsConstants.STRUTS_I18N_ENCODING)
    public static void setEncoding(String val) {
        encoding = val;
    }

    /**
     * Wrap and return the given request, if needed, so as to to transparently
     * handle multipart data as a wrapped class around the given request.
     *
     * @param request Our ServletRequest object
     * @param response Our ServerResponse object
     * @return Wrapped HttpServletRequest object
     * @throws ServletException on any error
     */
    protected HttpServletRequest prepareDispatcherAndWrapRequest(HttpServletRequest request,
            HttpServletResponse response) throws ServletException {

        Dispatcher du = Dispatcher.getInstance();

        // Prepare and wrap the request if the cleanup filter hasn't already, cleanup filter should be
        // configured first before struts2 dispatcher filter, hence when its cleanup filter's turn,
        // static instance of Dispatcher should be null.
        if (du == null) {
            if (this.dispatcher == null) {
                Properties props = new Properties();
                InputStream is = this.getClass().getClassLoader().getResourceAsStream("struts.properties");
                try {
                    props.load(is);
                } catch (Throwable t) {
                    throw new ServletException("Errore in caricamento struts.properties", t);
                }

                String struts2Config = this.getServletContext()
                        .getInitParameter(ApsAdminSystemConstants.STRUTS2_CONFIG_INIT_PARAM_NAME);
                if (null != struts2Config) {
                    props.setProperty("config", struts2Config);
                } else {
                    props.setProperty("config", "struts-default.xml,struts-plugin.xml,struts.xml");
                }

                Map params = new HashMap(props);
                dispatcher = new Dispatcher(this.getServletContext(), params);
                dispatcher.init();
                Dispatcher.setInstance(dispatcher);
                dispatcher.prepare(request, response);
            } else {
                Dispatcher.setInstance(dispatcher);
            }

        } else {
            dispatcher = du;
        }

        try {
            // Wrap request first, just in case it is multipart/form-data
            // parameters might not be accessible through before encoding (ww-1278)
            request = dispatcher.wrapRequest(request, this.getServletContext());
        } catch (IOException e) {
            String message = "Could not wrap servlet request with MultipartRequestWrapper!";
            LOG.error(message, e);
            throw new ServletException(message, e);
        }
        return request;
    }

    protected static void cleanUp(ServletRequest req) {
        // should we clean up yet?
        Integer count = (Integer) req.getAttribute(COUNTER);
        if (count != null && count > 0) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("skipping cleanup counter=" + count);
            }
            return;
        }
        // always dontClean up the thread request, even if an action hasn't been executed
        ActionContext.setContext(null);
        Dispatcher.setInstance(null);
    }

    private static final String COUNTER = "__cleanup_recursion_counter";

    /**
     * Locate a static resource and copy directly to the response,
     * setting the appropriate caching headers. 
     *
     * @param name The resource name
     * @param request The request
     * @param response The response
     * @throws IOException If anything goes wrong
     */
    protected void findStaticResource(String name, HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        if (!name.endsWith(".class")) {
            for (String pathPrefix : pathPrefixes) {
                InputStream is = findInputStream(name, pathPrefix);
                if (is != null) {
                    Calendar cal = Calendar.getInstance();

                    // check for if-modified-since, prior to any other headers
                    long ifModifiedSince = 0;
                    try {
                        ifModifiedSince = request.getDateHeader("If-Modified-Since");
                    } catch (Exception e) {
                        LOG.warn("Invalid If-Modified-Since header value: '"
                                + request.getHeader("If-Modified-Since") + "', ignoring");
                    }
                    long lastModifiedMillis = lastModifiedCal.getTimeInMillis();
                    long now = cal.getTimeInMillis();
                    cal.add(Calendar.DAY_OF_MONTH, 1);
                    long expires = cal.getTimeInMillis();

                    if (ifModifiedSince > 0 && ifModifiedSince <= lastModifiedMillis) {
                        // not modified, content is not sent - only basic headers and status SC_NOT_MODIFIED
                        response.setDateHeader("Expires", expires);
                        response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                        is.close();
                        return;
                    }

                    // set the content-type header
                    String contentType = getContentType(name);
                    if (contentType != null) {
                        response.setContentType(contentType);
                    }

                    if (serveStaticBrowserCache) {
                        // set heading information for caching static content
                        response.setDateHeader("Date", now);
                        response.setDateHeader("Expires", expires);
                        response.setDateHeader("Retry-After", expires);
                        response.setHeader("Cache-Control", "public");
                        response.setDateHeader("Last-Modified", lastModifiedMillis);
                    } else {
                        response.setHeader("Cache-Control", "no-cache");
                        response.setHeader("Pragma", "no-cache");
                        response.setHeader("Expires", "-1");
                    }

                    try {
                        copy(is, response.getOutputStream());
                    } finally {
                        is.close();
                    }
                    return;
                }
            }
        }

        response.sendError(HttpServletResponse.SC_NOT_FOUND);
    }

    /**
     * Determine the content type for the resource name.
     *
     * @param name The resource name
     * @return The mime type
     */
    protected String getContentType(String name) {
        // NOT using the code provided activation.jar to avoid adding yet another dependency
        // this is generally OK, since these are the main files we server up
        if (name.endsWith(".js")) {
            return "text/javascript";
        } else if (name.endsWith(".css")) {
            return "text/css";
        } else if (name.endsWith(".html")) {
            return "text/html";
        } else if (name.endsWith(".txt")) {
            return "text/plain";
        } else if (name.endsWith(".gif")) {
            return "image/gif";
        } else if (name.endsWith(".jpg") || name.endsWith(".jpeg")) {
            return "image/jpeg";
        } else if (name.endsWith(".png")) {
            return "image/png";
        } else {
            return null;
        }
    }

    /**
     * Copy bytes from the input stream to the output stream.
     *
     * @param input The input stream
     * @param output The output stream
     * @throws IOException If anything goes wrong
     */
    protected void copy(InputStream input, OutputStream output) throws IOException {
        final byte[] buffer = new byte[4096];
        int n;
        while (-1 != (n = input.read(buffer))) {
            output.write(buffer, 0, n);
        }
        output.flush(); // WW-1526
    }

    /**
     * Look for a static resource in the classpath.
     *
     * @param name The resource name
     * @param packagePrefix The package prefix to use to locate the resource
     * @return The inputstream of the resource
     * @throws IOException If there is a problem locating the resource
     */
    protected InputStream findInputStream(String name, String packagePrefix) throws IOException {
        String resourcePath;
        if (packagePrefix.endsWith("/") && name.startsWith("/")) {
            resourcePath = packagePrefix + name.substring(1);
        } else {
            resourcePath = packagePrefix + name;
        }

        resourcePath = URLDecoder.decode(resourcePath, encoding);

        return ClassLoaderUtil.getResourceAsStream(resourcePath, getClass());
    }

}