org.jahia.bin.Render.java Source code

Java tutorial

Introduction

Here is the source code for org.jahia.bin.Render.java

Source

/**
 * ==========================================================================================
 * =                   JAHIA'S DUAL LICENSING - IMPORTANT INFORMATION                       =
 * ==========================================================================================
 *
 *                                 http://www.jahia.com
 *
 *     Copyright (C) 2002-2017 Jahia Solutions Group SA. All rights reserved.
 *
 *     THIS FILE IS AVAILABLE UNDER TWO DIFFERENT LICENSES:
 *     1/GPL OR 2/JSEL
 *
 *     1/ GPL
 *     ==================================================================================
 *
 *     IF YOU DECIDE TO CHOOSE THE GPL LICENSE, YOU MUST COMPLY WITH THE FOLLOWING TERMS:
 *
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 *
 *     2/ JSEL - Commercial and Supported Versions of the program
 *     ===================================================================================
 *
 *     IF YOU DECIDE TO CHOOSE THE JSEL LICENSE, YOU MUST COMPLY WITH THE FOLLOWING TERMS:
 *
 *     Alternatively, commercial and supported versions of the program - also known as
 *     Enterprise Distributions - must be used in accordance with the terms and conditions
 *     contained in a separate written agreement between you and Jahia Solutions Group SA.
 *
 *     If you are unsure which license is appropriate for your use,
 *     please contact the sales department at sales@jahia.com.
 */
package org.jahia.bin;

import net.htmlparser.jericho.*;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.tika.io.IOUtils;
import org.jahia.api.Constants;
import org.jahia.bin.errors.DefaultErrorHandler;
import org.jahia.bin.errors.ErrorHandler;
import org.jahia.exceptions.JahiaBadRequestException;
import org.jahia.exceptions.JahiaForbiddenAccessException;
import org.jahia.exceptions.JahiaUnauthorizedException;
import org.jahia.registries.ServicesRegistry;
import org.jahia.services.applications.pluto.JahiaPortalURLParserImpl;
import org.jahia.services.content.*;
import org.jahia.services.content.decorator.JCRSiteNode;
import org.jahia.services.content.decorator.JCRUserNode;
import org.jahia.services.logging.MetricsLoggingService;
import org.jahia.services.render.*;
import org.jahia.services.seo.urlrewrite.SessionidRemovalResponseWrapper;
import org.jahia.services.templates.JahiaTemplateManagerService;
import org.jahia.services.usermanager.JahiaUser;
import org.jahia.services.usermanager.JahiaUserManagerService;
import org.jahia.settings.SettingsBean;
import org.jahia.tools.files.FileUpload;
import org.jahia.utils.Url;
import org.jahia.utils.i18n.Messages;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.springframework.web.context.ServletConfigAware;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.jcr.*;
import javax.servlet.ServletConfig;
import javax.servlet.http.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLDecoder;
import java.util.*;
import java.util.regex.Pattern;

/**
 * Rendering controller. Resolves the node and the template, and renders it by executing the appropriate script.
 */
public class Render extends HttpServlet implements Controller, ServletConfigAware {

    private static final long serialVersionUID = 5377039107890340659L;

    public static final String METHOD_DELETE = "DELETE";
    public static final String METHOD_HEAD = "HEAD";
    public static final String METHOD_GET = "GET";
    public static final String METHOD_OPTIONS = "OPTIONS";
    public static final String METHOD_POST = "POST";
    public static final String METHOD_PUT = "PUT";
    public static final String METHOD_TRACE = "TRACE";

    protected static final String HEADER_IFMODSINCE = "If-Modified-Since";
    protected static final String HEADER_LASTMOD = "Last-Modified";

    // Here we define the constants for the reserved keywords for post methods
    public static final String NODE_TYPE = "jcrNodeType";
    public static final String NODE_NAME = "jcrNodeName";
    public static final String NODE_NAME_PROPERTY = "jcrNodeNameProperty";
    public static final String NEW_NODE_OUTPUT_FORMAT = "jcrNewNodeOutputFormat";
    public static final String REDIRECT_TO = "jcrRedirectTo";
    public static final String REDIRECT_HTTP_RESPONSE_CODE = "jcrRedirectResponseCode";
    public static final String METHOD_TO_CALL = "jcrMethodToCall";
    public static final String AUTO_CHECKIN = "jcrAutoCheckin";
    public static final String CAPTCHA = "jcrCaptcha";
    public static final String TARGETDIRECTORY = "jcrTargetDirectory";
    public static final String TARGETNAME = "jcrTargetName";
    public static final String NORMALIZE_NODE_NAME = "jcrNormalizeNodeName";
    public static final String VERSION = "jcrVersion";
    public static final String SUBMIT = "jcrSubmit";
    public static final String AUTO_ASSIGN_ROLE = "jcrAutoAssignRole";
    public static final String ALIAS_USER = "alias";
    public static final String PARENT_TYPE = "jcrParentType";
    public static final String RETURN_CONTENTTYPE = "jcrReturnContentType";
    public static final String RETURN_CONTENTTYPE_OVERRIDE = "jcrReturnContentTypeOverride";
    public static final String RESOURCE_ID = "jcrResourceID";
    public static final String REMOVE_MIXIN = "jcrRemoveMixin";
    public static final String COOKIE_VALUE = "jcrCookieValue";
    public static final String COOKIE_NAME = "jcrCookieName";
    public static final String COOKIE_PATH = "jcrCookiePath";
    public static final String CONTRIBUTE_POST = "jcrContributePost";
    public static final String MARK_FOR_DELETION = "jcrMarkForDeletion";
    public static final String MARK_FOR_DELETION_MESSAGE = "jcrDeletionMessage";
    public static final String PREVIEW_DATE = "prevdate";
    public static final String DISABLE_XSS_FILTERING = "disableXSSFiltering";
    public static final List<String> EVENT_ATTRIBUTE_NAMES = Arrays.asList("onblur", "onchange", "onclick",
            "ondblclick", "onfocus", "onkeydown", "onkeypress", "onkeyup", "onload", "onmousedown", "onmousemove",
            "onmouseover", "onmouseout", "onmouseup", "onselect", "onsubmit", "onabort", "oncanplay",
            "oncanplaythrough", "oncontextmenu", "ondrag", "ondragend", "ondragenter", "ondragleave", "ondragstart",
            "ondrop", "ondurationchange", "onemptied", "onended", "onerror", "onformchange", "onforminput",
            "oninput", "oninvalid", "onloadeddata", "onloadedmetadata", "onloadstart", "onmousewheel", "onpause",
            "onplay", "onplaying", "onprogress", "onratechange", "onreadystatechange", "onscroll", "onseeked",
            "onseeking", "onshow", "onstalled", "onsuspend", "ontimeupdate", "onvolumechange", "onwaiting");
    public static final Pattern TAG_MISSING_END_BIGGERTHAN_PATTERN = Pattern.compile("<([^<>]*)(?=<|$)");
    public static final Pattern TAG_MISSING_START_LESSERTHAN_PATTERN = Pattern.compile("(^|(?<=>))([^<>]*)>");
    public static final String ALLOWS_MULTIPLE_SUBMITS = "allowsMultipleSubmits";

    private static final List<String> REDIRECT_CODE_MOVED_PERMANENTLY = new ArrayList<String>(
            Arrays.asList(new String[] { String.valueOf(HttpServletResponse.SC_MOVED_PERMANENTLY) }));
    private static final List<String> LIST_WITH_EMPTY_STRING = new ArrayList<String>(
            Arrays.asList(new String[] { StringUtils.EMPTY }));

    public static final String PLUTO_PREFIX = "__";
    public static final String PLUTO_ACTION = "ac";
    public static final String PLUTO_RESOURCE = "rs";

    protected static final Set<String> RESERVED_PARAMETERS;
    static {
        RESERVED_PARAMETERS = new HashSet<String>();
        RESERVED_PARAMETERS.add(NODE_TYPE);
        RESERVED_PARAMETERS.add(NODE_NAME);
        RESERVED_PARAMETERS.add(NODE_NAME_PROPERTY);
        RESERVED_PARAMETERS.add(NEW_NODE_OUTPUT_FORMAT);
        RESERVED_PARAMETERS.add(REDIRECT_TO);
        RESERVED_PARAMETERS.add(METHOD_TO_CALL);
        RESERVED_PARAMETERS.add(AUTO_CHECKIN);
        RESERVED_PARAMETERS.add(CAPTCHA);
        RESERVED_PARAMETERS.add(TARGETDIRECTORY);
        RESERVED_PARAMETERS.add(TARGETNAME);
        RESERVED_PARAMETERS.add(Constants.JCR_MIXINTYPES);
        RESERVED_PARAMETERS.add(NORMALIZE_NODE_NAME);
        RESERVED_PARAMETERS.add(VERSION);
        RESERVED_PARAMETERS.add(SUBMIT);
        RESERVED_PARAMETERS.add(AUTO_ASSIGN_ROLE);
        RESERVED_PARAMETERS.add(PARENT_TYPE);
        RESERVED_PARAMETERS.add(RETURN_CONTENTTYPE);
        RESERVED_PARAMETERS.add(RETURN_CONTENTTYPE_OVERRIDE);
        RESERVED_PARAMETERS.add(COOKIE_NAME);
        RESERVED_PARAMETERS.add(COOKIE_VALUE);
        RESERVED_PARAMETERS.add(COOKIE_PATH);
        RESERVED_PARAMETERS.add(CONTRIBUTE_POST);
        RESERVED_PARAMETERS.add(MARK_FOR_DELETION);
        RESERVED_PARAMETERS.add(DISABLE_XSS_FILTERING);
        RESERVED_PARAMETERS.add(ALLOWS_MULTIPLE_SUBMITS);
    }

    private static Logger logger = org.slf4j.LoggerFactory.getLogger(Render.class);

    private transient String workspace;
    private transient MetricsLoggingService loggingService;
    private transient JahiaTemplateManagerService templateService;
    private transient Action defaultPostAction;
    private transient Action defaultPutAction;

    private final transient Action defaultDeleteAction = new DefaultDeleteAction();
    private transient Action webflowAction;
    private transient Map<String, String> defaultContentType = new HashMap<>();

    private transient SettingsBean settingsBean;
    private transient RenderService renderService;
    private transient JCRSessionFactory jcrSessionFactory;
    private transient URLResolverFactory urlResolverFactory;

    private transient Integer sessionExpiryTime = null;
    private Integer cookieExpirationInDays = 1;

    private Set<String> allowedMethods = new HashSet<String>();

    private transient ServletConfig servletConfig;

    /**
     * Returns the time the <code>HttpServletRequest</code> object was last modified, in milliseconds since midnight January 1, 1970 GMT. If
     * the time is unknown, this method returns a negative number (the default).
     * <p/>
     * <p/>
     * Servlets that support HTTP GET requests and can quickly determine their last modification time should override this method. This
     * makes browser and proxy caches work more effectively, reducing the load on server and network resources.
     *
     * @return a <code>long</code> integer specifying the time the <code>HttpServletRequest</code> object was last modified, in milliseconds
     *         since midnight, January 1, 1970 GMT, or -1 if the time is not known
     */
    protected long getLastModified(Resource resource, RenderContext renderContext)
            throws RepositoryException, IOException {
        // Node node = resource.getNode();
        // if (node.hasProperty("jcr:lastModified")) {
        // return node.getProperty("jcr:lastModified").getDate().getTime().getTime();
        // }
        return -1;
    }

    /**
     * Sets the Last-Modified entity header field, if it has not already been set and if the value is meaningful. Called before doGet, to
     * ensure that headers are set before response data is written. A subclass might have set this header already, so we check.
     */
    protected void maybeSetLastModified(HttpServletResponse resp, long lastModified) {
        if (resp.containsHeader(HEADER_LASTMOD)) {
            return;
        }
        if (lastModified >= 0) {
            resp.setDateHeader(HEADER_LASTMOD, lastModified);
        }
    }

    protected RenderContext createRenderContext(HttpServletRequest req, HttpServletResponse resp, JahiaUser user) {
        RenderContext context = new RenderContext(req, resp, user);
        int index = req.getPathInfo().indexOf('/', 1);
        if (index == -1 || index == req.getPathInfo().length() - 1) {
            throw new JahiaBadRequestException("Invalid path");
        }
        context.setServletPath(req.getServletPath() + req.getPathInfo().substring(0, index));
        return context;
    }

    protected void doGet(HttpServletRequest req, HttpServletResponse resp, RenderContext renderContext,
            Resource resource, long startTime) throws RepositoryException, RenderException, IOException {
        loggingService.startProfiler("MAIN");
        resp.setCharacterEncoding(getSettingsBean().getCharacterEncoding());
        String out = renderService.render(resource, renderContext).trim();
        if (renderContext.getRedirect() != null && !resp.isCommitted()) {
            resp.sendRedirect(renderContext.getRedirect());
        } else if (!renderContext.isPortletActionRequest()) {
            resp.setContentType(renderContext.getContentType() != null ? renderContext.getContentType()
                    : getDefaultContentType(resource.getTemplateType()));
            resp.getWriter().print(out);
        }
        String sessionID = "";
        HttpSession httpSession = req.getSession(false);
        if (httpSession != null) {
            sessionID = httpSession.getId();
        }
        loggingService.stopProfiler("MAIN");
        if (loggingService.isEnabled()) {
            loggingService.logContentEvent(renderContext.getUser().getName(), req.getRemoteAddr(), sessionID,
                    resource.getNode().getIdentifier(), resource.getNode().getPath(),
                    resource.getNode().getPrimaryNodeType().getName(), "pageViewed", req.getHeader("User-Agent"),
                    req.getHeader("Referer"), Long.toString(System.currentTimeMillis() - startTime));
        }
    }

    protected void doPut(HttpServletRequest req, HttpServletResponse resp, RenderContext renderContext,
            URLResolver urlResolver) throws Exception {
        doAction(req, resp, urlResolver, renderContext, null, defaultPutAction, toParameterMapOfListOfString(req));
    }

    public void addCookie(HttpServletRequest req, HttpServletResponse resp) {
        if (req.getParameter(COOKIE_NAME) != null && req.getParameter(COOKIE_VALUE) != null) {
            Cookie cookie = new Cookie(req.getParameter(COOKIE_NAME), req.getParameter(COOKIE_VALUE));
            cookie.setMaxAge(60 * 60 * 24 * cookieExpirationInDays);
            if (req.getParameter(COOKIE_PATH) != null)
                cookie.setPath(req.getParameter(COOKIE_PATH));
            else {
                cookie.setPath("/");
            }
            resp.addCookie(cookie);
        }
    }

    public static JSONObject serializeNodeToJSON(JCRNodeWrapper node)
            throws RepositoryException, IOException, JSONException {
        final PropertyIterator stringMap = node.getProperties();
        Map<String, String> map = new HashMap<String, String>();
        while (stringMap.hasNext()) {
            JCRPropertyWrapper propertyWrapper = (JCRPropertyWrapper) stringMap.next();
            final int type = propertyWrapper.getType();
            final String name = JCRContentUtils.replaceColon(propertyWrapper.getName());
            if (!Constants.forbiddenPropertiesToSerialize.contains(propertyWrapper.getDefinition().getName())) {
                if (type == PropertyType.WEAKREFERENCE || type == PropertyType.REFERENCE) {
                    if (!propertyWrapper.isMultiple()) {
                        map.put(name, ((JCRNodeWrapper) propertyWrapper.getNode()).getUrl());
                    }
                } else {
                    if (!propertyWrapper.isMultiple()) {
                        map.put(name, propertyWrapper.getValue().getString());
                    }
                }
            }
        }
        JSONObject nodeJSON = new JSONObject(map);
        return nodeJSON;
    }

    protected void doPost(HttpServletRequest req, HttpServletResponse resp, RenderContext renderContext,
            URLResolver urlResolver) throws Exception {
        if (req.getParameter(JahiaPortalURLParserImpl.PORTLET_INFO) != null) {
            Resource resource = urlResolver.getResource();
            renderContext.setMainResource(resource);
            JCRSiteNode site = resource.getNode().getResolveSite();
            renderContext.setSite(site);
            doGet(req, resp, renderContext, resource, System.currentTimeMillis());
            return;
        }
        Map<String, List<String>> parameters = new HashMap<String, List<String>>();
        if (checkForUploadedFiles(req, resp, urlResolver.getWorkspace(), urlResolver.getLocale(), parameters,
                urlResolver)) {
            if (parameters.isEmpty()) {
                return;
            }
        }
        if (parameters.isEmpty()) {
            parameters = toParameterMapOfListOfString(req);
        }

        Action action;
        Resource resource = null;
        if (urlResolver.getPath().endsWith(".do") || isWebflowRequest(req)) {
            resource = urlResolver.getResource();
            renderContext.setMainResource(resource);
            try {
                JCRSiteNode site = resource.getNode().getResolveSite();
                renderContext.setSite(site);
            } catch (RepositoryException e) {
                logger.warn("Cannot get site for action context", e);
            }

            if (isWebflowRequest(req)) {
                action = webflowAction;
            } else {
                action = templateService.getActions().get(resource.getResolvedTemplate());
            }
        } else {
            final String path = urlResolver.getPath();

            String resourcePath = JCRTemplate.getInstance().doExecuteWithSystemSessionAsUser(null,
                    urlResolver.getWorkspace(), urlResolver.getLocale(), new JCRCallback<String>() {
                        public String doInJCR(JCRSessionWrapper session) throws RepositoryException {
                            String resourcePath = path.endsWith("*") ? StringUtils.substringBeforeLast(path, "/")
                                    : path;
                            do {
                                try {
                                    session.getNode(resourcePath);
                                    break;
                                } catch (PathNotFoundException e) {
                                    resourcePath = StringUtils.substringBeforeLast(resourcePath, "/");
                                }
                            } while (resourcePath.contains("/"));
                            return resourcePath;
                        }
                    });

            resource = urlResolver.getResource(resourcePath + ".html");
            renderContext.setMainResource(resource);
            try {
                JCRSiteNode site = resource.getNode().getResolveSite();
                renderContext.setSite(site);
            } catch (RepositoryException e) {
            }
            action = defaultPostAction;
        }
        if (action == null) {
            if (urlResolver.getPath().endsWith(".do")) {
                logger.error("Couldn't resolve action named [" + resource.getResolvedTemplate() + "]");
            }
            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
        } else {
            doAction(req, resp, urlResolver, renderContext, resource, action, parameters);
        }
    }

    private Map<String, List<String>> toParameterMapOfListOfString(HttpServletRequest req) {
        Map<String, List<String>> parameters = new HashMap<String, List<String>>();
        Map<?, ?> parameterMap = req.getParameterMap();

        boolean doXSSFilter = true;
        String token = parameterMap.get("form-token") != null ? ((String[]) parameterMap.get("form-token"))[0]
                : null;
        if (token != null) {
            @SuppressWarnings("unchecked")
            Map<String, Map<String, List<String>>> toks = (Map<String, Map<String, List<String>>>) req.getSession()
                    .getAttribute("form-tokens");
            if (toks != null && toks.containsKey(token)) {
                doXSSFilter = !toks.get(token).containsKey(DISABLE_XSS_FILTERING)
                        || toks.get(token).get(DISABLE_XSS_FILTERING).contains("false");
            }
        }
        for (Object key : parameterMap.keySet()) {
            if (key != null) {
                String[] parameterValues = (String[]) parameterMap.get(key);
                List<String> stringList;
                if (doXSSFilter && !RESERVED_PARAMETERS.contains(key)) {
                    stringList = new ArrayList<String>();
                    for (String s : parameterValues) {
                        stringList.add(xssFilter(s));
                    }
                } else {
                    stringList = new ArrayList<String>(Arrays.asList(parameterValues));
                }
                parameters.put((String) key, stringList);
            }
        }
        return parameters;
    }

    private String xssFilter(String stringValue) {

        // fail fast if we find no start or end of a tag
        if (!stringValue.contains("<") && !stringValue.contains(">")) {
            return stringValue;
        }

        // fix for https://jira.jahia.org/browse/QA-4337, attack with unclosed tags. These regexp will encode unclosed tags.
        stringValue = TAG_MISSING_END_BIGGERTHAN_PATTERN.matcher(stringValue).replaceAll("&lt;$1");
        stringValue = TAG_MISSING_START_LESSERTHAN_PATTERN.matcher(stringValue).replaceAll("$1&gt;");

        Source source = new Source(stringValue);
        OutputDocument outputDocument = new OutputDocument(source);
        List<Element> elements = source.getAllElements();

        for (Element element : elements) {
            if (HTMLElementName.SCRIPT.equals(element.getName())) {
                StartTag startTag = element.getStartTag();
                outputDocument.remove(startTag);
                if (!startTag.isSyntacticalEmptyElementTag()) {
                    EndTag endTag = element.getEndTag();
                    if (endTag != null) {
                        outputDocument.remove(endTag);
                    }
                }
            }
            final Attributes attributes = element.getAttributes();
            if (attributes != null) {
                for (Attribute attribute : attributes) {
                    String name = attribute.getName();
                    if (name != null && EVENT_ATTRIBUTE_NAMES.contains(name.toLowerCase())) {
                        outputDocument.remove(attribute);
                    }
                }
            }
        }

        return outputDocument.toString();
    }

    private boolean checkForUploadedFiles(HttpServletRequest req, HttpServletResponse resp, String workspace,
            Locale locale, Map<String, List<String>> parameters, URLResolver urlResolver)
            throws RepositoryException, IOException {

        if (isMultipartRequest(req)) {
            // multipart is processed only if it's not a portlet request.
            // otherwise it's the task the portlet
            if (!isPortletRequest(req)) {
                final String savePath = getSettingsBean().getTmpContentDiskPath();
                final File tmp = new File(savePath);
                if (!tmp.exists()) {
                    tmp.mkdirs();
                }
                try {
                    final FileUpload fileUpload = new FileUpload(req, savePath, Integer.MAX_VALUE);
                    req.setAttribute(FileUpload.FILEUPLOAD_ATTRIBUTE, fileUpload);
                    if (fileUpload.getFileItems() != null && fileUpload.getFileItems().size() > 0) {
                        boolean isTargetDirectoryDefined = fileUpload.getParameterNames().contains(TARGETDIRECTORY);
                        boolean isAction = urlResolver.getPath().endsWith(".do");
                        boolean isContributePost = fileUpload.getParameterNames().contains(CONTRIBUTE_POST);
                        final String requestWith = req.getHeader("x-requested-with");
                        boolean isAjaxRequest = req.getHeader("accept") != null
                                && req.getHeader("accept").contains("application/json") && requestWith != null
                                && requestWith.equals("XMLHttpRequest") || fileUpload.getParameterMap().isEmpty();
                        List<String> uuids = new LinkedList<String>();
                        List<String> files = new ArrayList<String>();
                        List<String> urls = new LinkedList<String>();
                        // If target directory is defined or if it is an ajax request then save the file now
                        // otherwise we delay the save of the file to the node creation
                        if (!isAction && (isContributePost || isTargetDirectoryDefined || isAjaxRequest)) {
                            JCRSessionWrapper session = jcrSessionFactory.getCurrentUserSession(workspace, locale);
                            String target;
                            if (isTargetDirectoryDefined) {
                                target = (fileUpload.getParameterValues(TARGETDIRECTORY))[0];
                            } else if (isContributePost) {
                                String path = urlResolver.getPath();
                                path = (path.endsWith("*") ? StringUtils.substringBeforeLast(path, "/") : path);
                                JCRNodeWrapper sessionNode = session.getNode(path);
                                JCRSiteNode siteNode = sessionNode.getResolveSite();
                                if (siteNode != null) {
                                    String s = sessionNode.getResolveSite().getPath() + "/files/contributed/";
                                    String name = JCRContentUtils.replaceColon(sessionNode.getPrimaryNodeTypeName())
                                            + "_" + sessionNode.getName();
                                    target = s + name;
                                    try {
                                        session.getNode(target);
                                    } catch (RepositoryException e) {
                                        JCRNodeWrapper node = session.getNode(s);
                                        session.checkout(node);
                                        node.addNode(name, "jnt:folder");
                                        session.save();
                                    }
                                } else {
                                    target = sessionNode.getPath() + "/files";
                                    if (!sessionNode.hasNode("files")) {
                                        session.checkout(sessionNode);
                                        sessionNode.addNode("files", "jnt:folder");
                                        session.save();
                                    }
                                }
                            } else {
                                String path = urlResolver.getPath();
                                target = (path.endsWith("*") ? StringUtils.substringBeforeLast(path, "/") : path);
                            }
                            final JCRNodeWrapper targetDirectory = session.getNode(target);

                            boolean isVersionActivated = fileUpload.getParameterNames().contains(VERSION)
                                    ? (fileUpload.getParameterValues(VERSION))[0].equals("true")
                                    : false;

                            final Map<String, DiskFileItem> stringDiskFileItemMap = fileUpload.getFileItems();
                            for (Map.Entry<String, DiskFileItem> itemEntry : stringDiskFileItemMap.entrySet()) {
                                //if node exists, do a checkout before
                                String name = itemEntry.getValue().getName();

                                if (fileUpload.getParameterNames().contains(TARGETNAME)) {
                                    name = (fileUpload.getParameterValues(TARGETNAME))[0];
                                }

                                name = JCRContentUtils.escapeLocalNodeName(FilenameUtils.getName(name));

                                JCRNodeWrapper fileNode = targetDirectory.hasNode(name)
                                        ? targetDirectory.getNode(name)
                                        : null;
                                if (fileNode != null && isVersionActivated) {
                                    session.checkout(fileNode);
                                }
                                // checkout parent directory
                                session.checkout(targetDirectory);
                                InputStream is = null;
                                JCRNodeWrapper wrapper = null;
                                try {
                                    is = itemEntry.getValue().getInputStream();
                                    wrapper = targetDirectory.uploadFile(name, is, JCRContentUtils.getMimeType(name,
                                            itemEntry.getValue().getContentType()));
                                } finally {
                                    IOUtils.closeQuietly(is);
                                }
                                uuids.add(wrapper.getIdentifier());
                                urls.add(wrapper.getAbsoluteUrl(req));
                                files.add(itemEntry.getValue().getName());
                                if (isVersionActivated) {
                                    if (!wrapper.isVersioned()) {
                                        wrapper.versionFile();
                                    }
                                    session.save();
                                    // Handle potential move of the node after save
                                    wrapper = session.getNodeByIdentifier(wrapper.getIdentifier());
                                    wrapper.checkpoint();
                                }
                            }
                            fileUpload.disposeItems();
                            fileUpload.markFilesAsConsumed();
                            session.save();
                        }

                        if (isAction || (!isAjaxRequest && !isContributePost)) {
                            parameters.putAll(fileUpload.getParameterMap());
                            if (isTargetDirectoryDefined) {
                                parameters.put(NODE_NAME, files);
                            }
                            return true;
                        } else {
                            try {
                                resp.setStatus(HttpServletResponse.SC_CREATED);
                                Map<String, Object> map = new LinkedHashMap<String, Object>();
                                map.put("uuids", uuids);
                                map.put("urls", urls);
                                JSONObject nodeJSON = new JSONObject(map);
                                nodeJSON.write(resp.getWriter());
                                return true;
                            } catch (JSONException e) {
                                logger.error(e.getMessage(), e);
                            }
                        }
                    }
                    if (fileUpload.getParameterMap() != null && !fileUpload.getParameterMap().isEmpty()) {
                        parameters.putAll(fileUpload.getParameterMap());
                    }
                } catch (IOException e) {
                    logger.error("Cannot parse multipart data !", e);
                }
            } else {
                logger.debug("Mulipart request is not processed. It's the task of the portlet");
            }
        }

        return false;
    }

    protected void doDelete(HttpServletRequest req, HttpServletResponse resp, RenderContext renderContext,
            URLResolver urlResolver) throws Exception {
        doAction(req, resp, urlResolver, renderContext, null, defaultDeleteAction,
                toParameterMapOfListOfString(req));
    }

    public boolean isMultipartRequest(final HttpServletRequest req) {
        final String contentType = req.getHeader("Content-Type");

        return ((contentType != null) && (contentType.indexOf("multipart/form-data") >= 0));
    }

    /**
     * If the request is a portlet request, it returns true, otherwise returns false.
     *
     * @param req An HttpServletRequest.
     * @return True if request is a portlet request.
     */
    public boolean isPortletRequest(final HttpServletRequest req) {
        String pathInfo = req.getPathInfo();
        if (pathInfo != null && pathInfo.contains(PLUTO_PREFIX)) {
            StringTokenizer st = new StringTokenizer(pathInfo, "/", false);
            while (st.hasMoreTokens()) {
                String token = st.nextToken();
                // remder/resource url
                if (token.startsWith(PLUTO_PREFIX + PLUTO_RESOURCE)) {
                    return true;
                } else if (token.startsWith(PLUTO_PREFIX + PLUTO_ACTION)) {
                    return true;
                }
            }
        }
        return false;

    }

    /**
     * This method allows you to define where you want to redirect the user after request.
     *
     * @param url
     * @param path
     * @param req
     * @param resp
     * @param parameters
     * @param bypassCache If true we will append a parameter to the URL that should match the id of the resource to refresh
     * @throws IOException
     */
    public static void performRedirect(String url, String path, HttpServletRequest req, HttpServletResponse resp,
            Map<String, List<String>> parameters, boolean bypassCache) throws IOException {
        String renderedURL = null;

        List<String> stringList = parameters.get(NEW_NODE_OUTPUT_FORMAT);
        String outputFormat = !CollectionUtils.isEmpty(stringList) && stringList.get(0) != null ? stringList.get(0)
                : "html";

        stringList = parameters.get(REDIRECT_HTTP_RESPONSE_CODE);
        int responseCode = !CollectionUtils.isEmpty(stringList) && !StringUtils.isBlank(stringList.get(0))
                ? Integer.parseInt(stringList.get(0))
                : HttpServletResponse.SC_SEE_OTHER;

        stringList = parameters.get(REDIRECT_TO);
        String stayOnPage = !CollectionUtils.isEmpty(stringList) && !StringUtils.isBlank(stringList.get(0))
                ? StringUtils.substringBeforeLast(stringList.get(0), ";")
                : null;

        if (!Login.isAuthorizedRedirect(req, stayOnPage, true)) {
            logger.warn("Unauthorized attempt redirect to {}", stayOnPage);
            stayOnPage = null;
        }

        if (!StringUtils.isEmpty(stayOnPage)) {
            renderedURL = stayOnPage + (!StringUtils.isEmpty(outputFormat) ? "." + outputFormat : "");
        } else if (!StringUtils.isEmpty(url)) {
            String requestedURL = req.getRequestURI();
            //            String encodedPath = URLEncoder.encode(path, "UTF-8").replace("%2F", "/").replace("+", "%20");
            String decodedURL = URLDecoder.decode(requestedURL, "UTF-8");

            int index = decodedURL.indexOf(path);

            renderedURL = decodedURL.substring(0, index) + url
                    + (!StringUtils.isEmpty(outputFormat) ? "." + outputFormat : "");
        }
        if (bypassCache) {
            stringList = parameters.get(RESOURCE_ID);
            String formuuid = !CollectionUtils.isEmpty(stringList) && !StringUtils.isBlank(stringList.get(0))
                    ? stringList.get(0)
                    : null;
            if (formuuid != null) {
                renderedURL = renderedURL + "?ec=" + formuuid;
            }
        }
        if (!StringUtils.isEmpty(renderedURL)) {
            String redirect = resp.encodeRedirectURL(renderedURL);
            if (SettingsBean.getInstance().isDisableJsessionIdParameter()) {
                String s = ";" + SettingsBean.getInstance().getJsessionIdParameterName();
                if (redirect.contains(s)) {
                    redirect = SessionidRemovalResponseWrapper.removeJsessionId(redirect);
                }
            }
            if (StringUtils.isEmpty(stayOnPage)) {
                resp.setHeader("Location", redirect);
            } else if (responseCode == HttpServletResponse.SC_SEE_OTHER) {
                resp.setHeader("Location", redirect);
            }
            if (responseCode == HttpServletResponse.SC_FOUND) {
                resp.sendRedirect(redirect);
            } else {
                resp.setStatus(responseCode);
            }
        }
    }

    public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        if (isDisabled()) {
            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
            return null;
        }
        String method = req.getMethod();
        if (req.getParameter(METHOD_TO_CALL) != null) {
            method = req.getParameter(METHOD_TO_CALL).toUpperCase();
        }
        if (!isMethodAllowed(method)) {
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
            return null;
        }
        long startTime = System.currentTimeMillis();
        String sessionId = null;
        try {
            final HttpSession session = req.getSession();
            if (logger.isInfoEnabled()) {
                sessionId = session.getId();
            }
            URLResolver urlResolver = urlResolverFactory.createURLResolver(req.getPathInfo(), req.getServerName(),
                    workspace, req);

            req.setAttribute("urlResolver", urlResolver);

            session.setAttribute("workspace", urlResolver.getWorkspace());

            if (sessionExpiryTime != null && session.getMaxInactiveInterval() != sessionExpiryTime * 60) {
                session.setMaxInactiveInterval(sessionExpiryTime * 60);
            }

            RenderContext renderContext = createRenderContext(req, resp, jcrSessionFactory.getCurrentUser());
            renderContext.setWorkspace(urlResolver.getWorkspace());

            urlResolver.setRenderContext(renderContext);
            req.getSession().setAttribute(Constants.SESSION_LOCALE, urlResolver.getLocale());
            jcrSessionFactory.setCurrentLocale(urlResolver.getLocale());
            if (renderContext.isPreviewMode() && req.getParameter(ALIAS_USER) != null
                    && !JahiaUserManagerService.isGuest(jcrSessionFactory.getCurrentUser())) {
                JahiaUserManagerService userManagerService = ServicesRegistry.getInstance()
                        .getJahiaUserManagerService();
                JCRUserNode userNode = userManagerService.lookupUser(req.getParameter(ALIAS_USER),
                        urlResolver.getSiteKey());
                if (userNode != null) {
                    jcrSessionFactory.setCurrentAliasedUser(userNode.getJahiaUser());
                }
            }

            // check permission
            try {
                if (!hasAccess(urlResolver.getNode())) {
                    if (JahiaUserManagerService.isGuest(jcrSessionFactory.getCurrentUser())) {
                        throw new JahiaUnauthorizedException();
                    } else {
                        throw new JahiaForbiddenAccessException();
                    }
                }
            } catch (PathNotFoundException e) {

            }

            renderContext.setSiteInfo(urlResolver.getSiteInfo());

            if (renderContext.isPreviewMode() && req.getParameter(PREVIEW_DATE) != null
                    && !JahiaUserManagerService.isGuest(jcrSessionFactory.getCurrentUser())) {
                Calendar previewDate = Calendar.getInstance();
                previewDate.setTime(new Date(new Long(req.getParameter(PREVIEW_DATE))));
                jcrSessionFactory.setCurrentPreviewDate(previewDate);
            }
            if (method.equals(METHOD_GET)) {
                Resource resource;
                resource = urlResolver.getResource();
                if (!StringUtils.isEmpty(urlResolver.getRedirectUrl())
                        && (StringUtils.isEmpty(resource.getTemplate())
                                || StringUtils.equals(resource.getTemplate(), "default"))) {
                    Map<String, List<String>> parameters = new HashMap<String, List<String>>();
                    parameters.put(NEW_NODE_OUTPUT_FORMAT, LIST_WITH_EMPTY_STRING);
                    parameters.put(REDIRECT_HTTP_RESPONSE_CODE, REDIRECT_CODE_MOVED_PERMANENTLY);

                    performRedirect(urlResolver.getRedirectUrl(),
                            StringUtils.isEmpty(urlResolver.getVanityUrl())
                                    ? "/" + urlResolver.getLocale().toString() + urlResolver.getPath()
                                    : urlResolver.getVanityUrl(),
                            req, resp, parameters, false);
                } else {
                    renderContext.setMainResource(resource);
                    if (renderContext.getSite() == null) {
                        // If Site has not been resolved by the servlet (so far only dashboard mode is doing that
                        JCRSiteNode site = resource.getNode().getResolveSite();
                        if (!Url.isLocalhost(req.getServerName()) && !renderContext.isEditMode()) {
                            JCRSessionWrapper session1 = resource.getNode().getSession();
                            if (urlResolver.getSiteKey() != null
                                    && (site == null || !site.getSiteKey().equals(urlResolver.getSiteKey()))) {
                                site = (JCRSiteNode) session1.getNode("/sites/" + urlResolver.getSiteKey());
                            } else if (renderContext.isLiveMode() && urlResolver.getSiteKeyByServerName() != null
                                    && (site == null
                                            || !site.getSiteKey().equals(urlResolver.getSiteKeyByServerName()))) {
                                site = (JCRSiteNode) session1
                                        .getNode("/sites/" + urlResolver.getSiteKeyByServerName());
                            }
                        }
                        String jsite = null;
                        HttpServletRequest request = renderContext.getRequest();
                        if (request != null) {
                            jsite = request.getParameter("jsite");
                        }
                        if (jsite == null && renderContext.getMainResource() != null) {
                            jsite = (String) renderContext.getMainResource().getModuleParams().get("jsite");
                        }
                        if (jsite != null) {
                            try {
                                site = (JCRSiteNode) resource.getNode().getSession().getNodeByIdentifier(jsite);
                            } catch (ItemNotFoundException e) {
                                if (JahiaUserManagerService.isGuest(jcrSessionFactory.getCurrentUser())) {
                                    throw new JahiaUnauthorizedException();
                                } else {
                                    throw new JahiaForbiddenAccessException();
                                }
                            }
                        }
                        if (resource.getNode().getPath().startsWith("/sites/") && (site == null || (!site.getPath()
                                .startsWith("/modules/")
                                && !site.isAllowsUnlistedLanguages()
                                && !(renderContext.isLiveMode()
                                        ? site.getActiveLiveLanguagesAsLocales().contains(urlResolver.getLocale())
                                        : site.getLanguagesAsLocales().contains(urlResolver.getLocale()))))) {
                            throw new PathNotFoundException("This language does not exist on this site");
                        }
                        renderContext.setSite(site);
                    }
                    //                    resource.pushWrapper("wrapper.fullpage");

                    if (urlResolver.getPath().endsWith(".do")) {
                        Action action = templateService.getActions().get(resource.getResolvedTemplate());
                        Map<String, List<String>> parameters = toParameterMapOfListOfString(req);
                        if (action != null) {
                            doAction(req, resp, urlResolver, renderContext, resource, action, parameters);
                        } else {
                            logger.error("Action {} does not exist", resource.getResolvedTemplate());
                            throw new PathNotFoundException("Action does not exist");
                        }
                    } else {
                        long lastModified = getLastModified(resource, renderContext);

                        if (lastModified == -1) {
                            // servlet doesn't support if-modified-since, no reason
                            // to go through further expensive logic
                            doGet(req, resp, renderContext, resource, startTime);
                        } else {
                            long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                            if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                                // If the servlet mod time is later, call doGet()
                                // Round down to the nearest second for a proper compare
                                // A ifModifiedSince of -1 will always be less
                                maybeSetLastModified(resp, lastModified);
                                doGet(req, resp, renderContext, resource, startTime);
                            } else {
                                resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                            }
                        }
                    }
                }
            } else if (method.equals(METHOD_HEAD)) {
                doHead(req, resp);

            } else if (method.equals(METHOD_POST)) {
                doPost(req, resp, renderContext, urlResolver);

            } else if (method.equals(METHOD_PUT)) {
                doPut(req, resp, renderContext, urlResolver);

            } else if (method.equals(METHOD_DELETE)) {
                doDelete(req, resp, renderContext, urlResolver);

            } else if (method.equals(METHOD_OPTIONS)) {
                doOptions(req, resp);

            } else if (method.equals(METHOD_TRACE)) {
                doTrace(req, resp);

            } else {
                //
                // Note that this means NO servlet supports whatever
                // method was requested, anywhere on this server.
                //
                resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
            }
        } catch (Exception e) {
            List<ErrorHandler> handlers = templateService.getErrorHandler();
            for (ErrorHandler handler : handlers) {
                if (handler.handle(e, req, resp)) {
                    return null;
                }
            }
            DefaultErrorHandler.getInstance().handle(e, req, resp);
        } finally {
            if (logger.isInfoEnabled()) {
                StringBuilder sb = new StringBuilder(100);
                sb.append("Rendered [").append(req.getRequestURI());
                if (jcrSessionFactory.getCurrentUser() != null) {
                    sb.append("] user=[").append(jcrSessionFactory.getCurrentUser().getUsername());
                }
                sb.append("] ip=[").append(req.getRemoteAddr()).append("] sessionID=[").append(sessionId)
                        .append("] in [").append(System.currentTimeMillis() - startTime).append("ms]");
                logger.info(sb.toString());
            }
        }
        return null;
    }

    private boolean isWebflowRequest(HttpServletRequest req) {
        boolean webflowRequest = false;
        if (req.getMethod().equals(METHOD_POST)) {
            Enumeration<?> parameterNames = req.getParameterNames();
            while (parameterNames.hasMoreElements()) {
                String s = (String) parameterNames.nextElement();
                if (s.startsWith("webflowexecution")) {
                    webflowRequest = true;
                    break;
                }
            }
        }
        return webflowRequest;
    }

    protected boolean isDisabled() {
        return false;
    }

    private void doAction(HttpServletRequest req, HttpServletResponse resp, URLResolver urlResolver,
            RenderContext renderContext, Resource resource, Action action, Map<String, List<String>> parameters)
            throws Exception {
        final String requestWith = req.getHeader("x-requested-with");
        boolean isAjaxRequest = req.getHeader("accept") != null
                && req.getHeader("accept").contains("application/json") && requestWith != null
                && requestWith.equals("XMLHttpRequest");

        final Action originalAction = action;

        int tokenResult = TokenChecker.NO_TOKEN;
        if (!isWebflowRequest(req)) {
            tokenResult = TokenChecker.checkToken(req, resp, parameters);
        }

        switch (tokenResult) {
        case TokenChecker.NO_TOKEN:
            break;
        case TokenChecker.INVALID_TOKEN:
            throw new AccessDeniedException("Invalid token.");
        case TokenChecker.INVALID_CAPTCHA:
            Map<String, String[]> formDatas = new HashMap<String, String[]>();
            Set<Map.Entry<String, List<String>>> set = parameters.entrySet();
            for (Map.Entry<String, List<String>> params : set) {
                formDatas.put(params.getKey(), params.getValue().toArray(new String[params.getValue().size()]));
            }
            String errorMessage = Messages.getInternal("failure.captcha", urlResolver.getLocale(),
                    "Your captcha is invalid");
            if (!isAjaxRequest) {
                req.getSession().setAttribute("formDatas", formDatas);
                req.getSession().setAttribute("formError", errorMessage);
                performRedirect(renderContext.getMainResource().getNode().getPath(), urlResolver.getPath(), req,
                        resp, parameters, true);
            } else {
                resp.setContentType("application/json; charset=UTF-8");
                Map<String, String> res = new HashMap<String, String>();
                res.put("status", errorMessage);
                new JSONObject(res).write(resp.getWriter());
            }
            return;
        case TokenChecker.INVALID_HIDDEN_FIELDS:
            throw new AccessDeniedException();
        case TokenChecker.VALID_TOKEN:
            action = new SystemAction() {
                @Override
                public ActionResult doExecuteAsSystem(HttpServletRequest req, RenderContext renderContext,
                        JCRSessionWrapper systemSession, Resource resource, Map<String, List<String>> parameters,
                        URLResolver urlResolver) throws Exception {
                    return originalAction.doExecute(req, renderContext, resource, systemSession, parameters,
                            urlResolver);
                }
            };
        }

        if (!(action instanceof SystemAction)) {
            if (action.getRequiredWorkspace() != null
                    && !action.getRequiredWorkspace().equals(urlResolver.getWorkspace())) {
                throw new PathNotFoundException("Action is not supported for this workspace");
            }
            if (action.isRequireAuthenticatedUser() && !renderContext.isLoggedIn()) {
                throw new AccessDeniedException("Action '" + action.getName() + "' requires an authenticated user");
            }
            if (!action.isPermitted(urlResolver.getNode())) {
                throw new AccessDeniedException("Action '" + action.getName() + "' requires '"
                        + action.getRequiredPermission() + "' permission.");
            }
        } else if (originalAction instanceof LicensedAction) {
            LicensedAction licensedAction = (LicensedAction) originalAction;
            if (!licensedAction.isAllowedByLicense()) {
                logger.error(
                        "Action '{}' requires a licene feature '{}'"
                                + " which is not allowed by the current license terms",
                        originalAction.getName(), licensedAction.getLicenseFeature());
                throw new AccessDeniedException("Action '" + action.getName() + "' requires a licene feature '"
                        + licensedAction.getLicenseFeature()
                        + "' which is not allowed by the current license terms");
            }
        }

        JCRSessionWrapper session = null;
        if (resource != null) {
            session = resource.getNode().getSession();
        } else {
            session = JCRSessionFactory.getInstance().getCurrentUserSession(urlResolver.getWorkspace(),
                    urlResolver.getLocale());
        }
        ActionResult result;
        try {
            result = action.doExecute(req, renderContext, resource, session, parameters, urlResolver);
        } catch (Exception e) {
            logger.error("An error occurs when executing action {}", action.getName(), e);
            throw e;
        }
        if (result != null) {
            boolean returnJSON = "json".equals(
                    parameters.get(RETURN_CONTENTTYPE) != null ? parameters.get(RETURN_CONTENTTYPE).get(0) : "")
                    || req.getHeader("accept") != null && req.getHeader("accept").contains("application/json");
            if (result.getResultCode() < 300 || returnJSON) {
                resp.setStatus(result.getResultCode());
                addCookie(req, resp);
                if (result.getJson() != null && returnJSON) {
                    try {
                        String contentType = parameters.get(RETURN_CONTENTTYPE_OVERRIDE) != null
                                ? StringUtils.defaultIfEmpty(parameters.get(RETURN_CONTENTTYPE_OVERRIDE).get(0),
                                        null)
                                : null;
                        if (contentType == null) {
                            contentType = "application/json; charset=UTF-8";
                        } else if (!contentType.toLowerCase().contains("charset")) {
                            // append the charset
                            contentType += "; charset=UTF-8";
                        }
                        resp.setContentType(contentType);
                        result.getJson().write(resp.getWriter());
                    } catch (JSONException e) {
                        logger.error(e.getMessage(), e);
                    }
                } else {
                    if (!result.isAbsoluteUrl()) {
                        performRedirect(result.getUrl(), urlResolver.getPath(), req, resp, parameters, false);
                    } else {
                        resp.sendRedirect(resp.encodeRedirectURL(result.getUrl()));
                    }
                }
            } else {
                resp.sendError(result.getResultCode());
            }
        }
    }

    protected boolean isMethodAllowed(String method) {
        return allowedMethods.isEmpty() || allowedMethods.contains(method);
    }

    protected boolean hasAccess(JCRNodeWrapper node) {
        return true;
    }

    public void setServletConfig(ServletConfig servletConfig) {
        this.servletConfig = servletConfig;
    }

    @Override
    public ServletConfig getServletConfig() {
        return servletConfig;
    }

    @Override
    public String getServletName() {
        return getServletConfig().getServletName();
    }

    public static String getRenderServletPath() {
        // TODO move this into configuration
        return "/cms/render";
    }

    public void setWorkspace(String workspace) {
        this.workspace = workspace;
    }

    public void setLoggingService(MetricsLoggingService loggingService) {
        this.loggingService = loggingService;
    }

    public void setTemplateService(JahiaTemplateManagerService templateService) {
        this.templateService = templateService;
    }

    public void setSessionExpiryTime(int sessionExpiryTime) {
        this.sessionExpiryTime = sessionExpiryTime;
    }

    public void setDefaultPostAction(Action defaultPostActionResult) {
        this.defaultPostAction = defaultPostActionResult;
    }

    public void setDefaultPutAction(Action defaultPutActionResult) {
        this.defaultPutAction = defaultPutActionResult;
    }

    public void setWebflowAction(Action webflowAction) {
        this.webflowAction = webflowAction;
    }

    public static Set<String> getReservedParameters() {
        return RESERVED_PARAMETERS;
    }

    /**
     * @param settingsBean the settingsBean to set
     */
    public void setSettingsBean(SettingsBean settingsBean) {
        this.settingsBean = settingsBean;
    }

    /**
     * @param renderService the renderService to set
     */
    public void setRenderService(RenderService renderService) {
        this.renderService = renderService;
    }

    /**
     * @param jcrSessionFactory the jcrSessionFactory to set
     */
    public void setJcrSessionFactory(JCRSessionFactory jcrSessionFactory) {
        this.jcrSessionFactory = jcrSessionFactory;
    }

    public void setCookieExpirationInDays(Integer cookieExpirationInDays) {
        this.cookieExpirationInDays = cookieExpirationInDays;
    }

    public void setUrlResolverFactory(URLResolverFactory urlResolverFactory) {
        this.urlResolverFactory = urlResolverFactory;
    }

    /**
     * Specifies the set of allowed HTTP methods.
     *
     * @param allowedMethods the set of allowed HTTP methods
     */
    public void setAllowedMethods(Set<String> allowedMethods) {
        this.allowedMethods = new HashSet<String>(allowedMethods.size());
        for (String method : allowedMethods) {
            this.allowedMethods.add(method.toUpperCase());
        }
    }

    public String getDefaultContentType(String templateType) {
        if (templateType != null && defaultContentType.get(templateType) != null) {
            return defaultContentType.get(templateType);
        }
        return "text/html; charset=UTF-8";
    }

    public void setDefaultContentType(Map<String, String> defaultContentType) {
        this.defaultContentType = defaultContentType;
    }

    public SettingsBean getSettingsBean() {
        return settingsBean;
    }
}