ExtensionsPortletFilter.java

package org.andromda.cartridges.jsf.portlet.myfaces.tomahawk;

import java.io.IOException;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletConfig;
import javax.portlet.PortletException;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import org.andromda.cartridges.jsf.portlet.myfaces.tomahawk.support.ExtensionsResponseWrapper;
import org.andromda.cartridges.jsf.portlet.myfaces.tomahawk.support.HttpServletRequestWrapper;
import org.andromda.cartridges.jsf.portlet.myfaces.tomahawk.support.HttpServletResponseWrapper;
import org.andromda.cartridges.jsf.portlet.myfaces.tomahawk.support.MultipartPortletRequestWrapper;
import org.apache.commons.fileupload.portlet.PortletFileUpload;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.myfaces.renderkit.html.util.AddResource;
import org.apache.myfaces.renderkit.html.util.AddResourceFactory;
import org.apache.portals.bridges.portletfilter.PortletFilter;
import org.apache.portals.bridges.portletfilter.PortletFilterChain;
import org.apache.portals.bridges.portletfilter.PortletFilterConfig;

/**
 * This portlet filter supports Tomahawk's extended components, such as
 * inputHtml and fileUpload.
 *
 * @author <a href="mailto:shinsuke@yahoo.co.jp">Shinsuke Sugaya</a>
 * @author Chad Brandon
 */
public class ExtensionsPortletFilter
    implements PortletFilter
{
    private static final Log log = LogFactory.getLog(ExtensionsPortletFilter.class);

    private static final String UPLOAD_REPOSITORY_PATH = "uploadRepositoryPath";

    private static final String UPLOAD_THRESHOLD_SIZE = "uploadThresholdSize";

    private static final String UPLOAD_MAX_FILE_SIZE = "uploadMaxFileSize";

    private static final String MULTIPART_ENCODING = "multipartEncoding";

    private int uploadMaxFileSize = 100 * 1024 * 1024; // 10 MB

    private int uploadThresholdSize = 1 * 1024 * 1024; // 1 MB

    private String uploadRepositoryPath = null; // standard temp directory

    private String multipartEncoding = null;

    private PortletConfig portletConfig;

    /**
     * Called by init method of MyFacesFilterPortlet to initialize this portlet
     * filter.
     *
     * @param filterConfig
     * @throws PortletException
     */
    public void init(PortletFilterConfig filterConfig) throws PortletException
    {
        if (log.isDebugEnabled())
        {
            log.debug("Initializing ExtensionsPortletFilter.");
        }

        setPortletConfig(filterConfig.getPortletConfig());

        // for inputFileUpload
        String param = filterConfig.getInitParameter(UPLOAD_MAX_FILE_SIZE);

        uploadMaxFileSize = resolveSize(param, uploadMaxFileSize);

        param = filterConfig.getInitParameter(UPLOAD_THRESHOLD_SIZE);

        uploadThresholdSize = resolveSize(param, uploadThresholdSize);

        uploadRepositoryPath = filterConfig.getInitParameter(UPLOAD_REPOSITORY_PATH);

        multipartEncoding = filterConfig.getInitParameter(MULTIPART_ENCODING);

        if (log.isDebugEnabled())
        {
            log.debug("uploadMaxFileSize=" + uploadMaxFileSize);
            log.debug("uploadThresholdSize=" + uploadThresholdSize);
            log.debug("uploadRepositoryPath=" + uploadRepositoryPath);
            log.debug("multipartEncoding=" + multipartEncoding);
        }
    }

    /**
     * Called by render method of MyFacesFilterPortlet to put tags, such as
     * &lt;style&gt;, into &lt;head&gt;.
     *
     * @param request
     * @param response
     * @param chain PortletFilterChain instance
     * @throws PortletException
     * @throws IOException
     */
    @SuppressWarnings("deprecation")
    public void renderFilter(
        RenderRequest request,
        RenderResponse response,
        PortletFilterChain chain) throws PortletException, IOException
    {
        if (log.isDebugEnabled())
        {
            log.debug("called renderFilter.");
            log.debug("RenderRequest=" + request.getClass().getName());
            log.debug("RenderResponse=" + response.getClass().getName());
        }

        final HttpServletRequestWrapper extendedRequest = new HttpServletRequestWrapper(
            request,
            getPortletConfig().getPortletContext());

        // Serve resources
        AddResource addResource = null;
        try
        {
            addResource = AddResourceFactory.getInstance(extendedRequest);
        }
        catch (Throwable throwable)
        {
            log.error(throwable);
            throw new PortletException(throwable);
        }

        try
        {
            addResource.responseStarted();

            if (addResource.requiresBuffer())
            {
                final HttpServletResponseWrapper servletResponse = new HttpServletResponseWrapper(response);
                final ExtensionsResponseWrapper extendedResponse = new ExtensionsResponseWrapper(
                    servletResponse,
                    response);

                // call next rednerFilter
                chain.renderFilter(request, extendedResponse);

                extendedResponse.finishResponse();

                // only parse HTML responses
                if (extendedResponse.getContentType() != null
                    && isValidContentType(extendedResponse.getContentType()))
                {
                    addResource.parseResponse(
                        extendedRequest,
                        extendedResponse.toString(),
                        servletResponse);

                    addResource.writeMyFacesJavascriptBeforeBodyEnd(
                        extendedRequest,
                        servletResponse);

                    if (!addResource.hasHeaderBeginInfos())
                    {
                        // writes the response if no header info is needed
                        addResource.writeResponse(extendedRequest, servletResponse);
                        return;
                    }

                    // Some headerInfo has to be added
                    addResource.writeWithFullHeader(extendedRequest, servletResponse);

                    // writes the response
                    addResource.writeResponse(extendedRequest, servletResponse);
                }
                else
                {

                    byte[] responseArray = extendedResponse.getBytes();

                    if (responseArray.length > 0)
                    {
                        // When not filtering due to not valid content-type,
                        // deliver the byte-array instead of a charset-converted
                        // string.  Otherwise a binary stream gets corrupted.
                        servletResponse.getOutputStream().write(responseArray);
                    }
                }

            }
            else
            {
                chain.renderFilter(request, response);
            }
        }
        finally
        {
            addResource.responseFinished();
        }
    }

    /**
     * Called by render method of MyFacesFilterPortlet to wrap the request when
     * it has a multipart content.
     *
     * @param request
     * @param response
     * @param chain PortletFilterChain instance
     * @throws PortletException
     * @throws IOException
     */
    public void processActionFilter(
        ActionRequest request,
        ActionResponse response,
        PortletFilterChain chain) throws PortletException, IOException
    {
        if (log.isDebugEnabled())
        {
            log.debug("called processActionFilter.");
        }

        // Check multipart/form-data
        if (PortletFileUpload.isMultipartContent(request))
        {
            if (log.isDebugEnabled())
                log.debug("ActionRequest is multipart content.");
            if (multipartEncoding != null)
            {
                if (log.isDebugEnabled())
                    log.debug("Mutlipart encoding is " + multipartEncoding);
                request.setCharacterEncoding(multipartEncoding);
            }
            request = new MultipartPortletRequestWrapper(
                request,
                uploadMaxFileSize,
                uploadThresholdSize,
                uploadRepositoryPath);
        }

        // call next processActionFilter
        chain.processActionFilter(request, response);
    }

    /**
     * Called by destroy method of MyFacesFilterPortlet to destroy this portlet
     * filter.
     */
    public void destroy()
    {}

    private int resolveSize(String param, int defaultValue)
    {
        int numberParam = defaultValue;

        if (param != null)
        {
            param = param.toLowerCase();
            int factor = 1;
            String number = param;

            if (param.endsWith("g"))
            {
                factor = 1024 * 1024 * 1024;
                number = param.substring(0, param.length() - 1);
            }
            else if (param.endsWith("m"))
            {
                factor = 1024 * 1024;
                number = param.substring(0, param.length() - 1);
            }
            else if (param.endsWith("k"))
            {
                factor = 1024;
                number = param.substring(0, param.length() - 1);
            }

            numberParam = Integer.parseInt(number) * factor;
        }
        return numberParam;
    }

    /**
     * @return Returns the portletConfig.
     */
    public PortletConfig getPortletConfig()
    {
        return portletConfig;
    }

    /**
     * @param portletConfig The portletConfig to set.
     */
    public void setPortletConfig(PortletConfig portletConfig)
    {
        this.portletConfig = portletConfig;
    }

    /**
     * @param contentType
     * @return validContentType
     */
    public boolean isValidContentType(String contentType)
    {
        return contentType != null
            && (contentType.startsWith("text/html") || contentType.startsWith("text/xml")
                || contentType.startsWith("application/xhtml+xml") || contentType
                .startsWith("application/xml"));
    }
}