de.betterform.agent.web.WebUtil.java Source code

Java tutorial

Introduction

Here is the source code for de.betterform.agent.web.WebUtil.java

Source

/*
 * Copyright (c) 2012. betterFORM Project - http://www.betterform.de
 * Licensed under the terms of BSD License
 */

package de.betterform.agent.web;

import de.betterform.agent.web.cache.XFSessionCache;
import de.betterform.agent.web.flux.FluxProcessor;
import de.betterform.connector.http.AbstractHTTPConnector;
import de.betterform.xml.config.XFormsConfigException;
import de.betterform.xml.xforms.XFormsProcessor;
import de.betterform.xml.xforms.exception.XFormsException;
import de.betterform.xml.xforms.model.submission.RequestHeaders;
import de.betterform.xml.xslt.TransformerService;
import de.betterform.xml.xslt.impl.CachingTransformerService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.cookie.ClientCookie;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

import javax.servlet.ServletContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;

/**
 * Collection of static methods to be re-used by different Servlets/Filter implementations.
 *
 * @author Joern Turner
 */
public class WebUtil {
    private static final Log LOGGER = LogFactory.getLog(WebUtil.class);
    public static final String HTML_CONTENT_TYPE = "text/html;charset=UTF-8";
    public static final String HTTP_SESSION_ID = "httpSessionId";
    public static final String EXISTDB_USER = "_eXist_xmldb_user";
    private static final String FILENAME = "fileName";
    private static final String PLAIN_PATH = "plainPath";
    private static final String CONTEXT_PATH = "contextPath";

    public static String getRequestURI(HttpServletRequest request) {
        StringBuffer buffer = new StringBuffer(request.getScheme());
        buffer.append("://");
        buffer.append(request.getServerName());
        buffer.append(":");
        buffer.append(request.getServerPort());
        buffer.append(request.getContextPath());
        return buffer.toString();
    }

    public static void printSessionKeys(HttpSession session) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("--------------- http session dump ---------------");
            Enumeration keys = session.getAttributeNames();
            if (keys.hasMoreElements()) {
                while (keys.hasMoreElements()) {
                    String s = (String) keys.nextElement();
                    LOGGER.debug("sessionkey: " + s + ":" + session.getAttribute(s));
                }
            } else {
                LOGGER.debug("--- no keys present in session ---");
            }
        }
    }

    public static void nonCachingResponse(HttpServletResponse response) {
        response.setHeader("Cache-Control", "private, no-store,  no-cache, must-revalidate");
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Expires", "-1");
    }

    /**
    *
    * @return String for use as root of URLs
    */
    public static String getContextRoot(HttpServletRequest request) {
        if (request.getAttribute(WebProcessor.ALTERNATIVE_ROOT) != null) {
            return (String) request.getAttribute(WebProcessor.ALTERNATIVE_ROOT);
        } else {
            return request.getContextPath();
        }
    }

    /**
     * fetches the XFormsSession from the HTTP Session
     *
     * @param request the HTTP Request
     * @return the xformsSession for the request
     */
    public static WebProcessor getWebProcessor(HttpServletRequest request, HttpServletResponse response,
            HttpSession session) {

        //        String key = request.getParameter("sessionKey");
        //        XFormsSessionManager manager = (XFormsSessionManager) session.getAttribute(XFormsSessionManager.XFORMS_SESSION_MANAGER);
        //        XFormsSession xFormsSession = manager.getWebProcessor(key);
        String key = request.getParameter("sessionKey");
        if (key == null) {
            LOGGER.warn("Request " + request + " has no parameter session key");
            return null;
        } else {
            return getWebProcessor(key, request, response, session);
        }
    }

    public static WebProcessor getWebProcessor(String key, HttpServletRequest request, HttpServletResponse response,
            HttpSession session) {
        if (key == null || key.equals("")) {
            LOGGER.warn("SessionKey is null");
            return null;
        }

        org.infinispan.Cache<String, FluxProcessor> sessionCache;
        try {
            sessionCache = XFSessionCache.getCache();
        } catch (XFormsException xfe) {
            sessionCache = null;
        }

        if (sessionCache == null || !(sessionCache.containsKey(key))) {
            LOGGER.warn("No xformsSession for key " + key + " in Cache");
            return null;
        }
        WebProcessor processor = sessionCache.get(key);

        if (processor.getContext() != null) {
            return processor;
        } else {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Element is read from disk " + processor.toString());
            }
            //re-initialize transient state
            processor.setRequest(request);
            processor.setResponse(response);
            processor.setHttpSession(session);
            processor.setKey(key);
            processor.getHttpRequestHandler();
            processor.setContext(session.getServletContext());

            try {
                processor.configure();
                processor.createUIGenerator();
                processor.init();
                return processor;
            } catch (Exception e) {
                LOGGER.error("Could not reload xformSession from disk.", e);
            }
        }

        return null;

    }

    /**
     * remove session with given key from infinispan cache
     *
     * @param key the entry identifier
     * @return true if session existed and could be removed
     */
    public static boolean removeSession(String key) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("removing key: '" + key + "' from cache");
        }
        boolean removedSession = false;

        try {
            org.infinispan.Cache<String, FluxProcessor> sessionCache = XFSessionCache.getCache();
            if (sessionCache != null) {
                //TODO: rethink ...
                removedSession = (sessionCache.remove(key) != null);
            }
        } catch (XFormsException xfe) {
        }

        if (LOGGER.isDebugEnabled()) {
            String result = removedSession ? "successful" : "unsuccessful";
            LOGGER.debug("Removal of session '" + key + "' was " + result);
        }

        return removedSession;
    }

    public static String decodeUrl(String formPath, HttpServletRequest request)
            throws UnsupportedEncodingException {
        String decoded = URLDecoder.decode(formPath, "UTF-8");
        if (decoded.startsWith("http://")) {
            return decoded;
        } else {
            String requestURI = getRequestURI(request);
            return requestURI + formPath;
        }
    }

    /**
     * stores cookies that may exist in request and passes them on to processor for usage in
     * HTTPConnectors. Instance loading and submission then uses these cookies. Important for
     * applications using auth.
     * <p/>
     * NOTE: this method should be called *before* the Adapter is initialized cause the cookies may
     * already be needed for setup (e.g. loading of XForms via Http)
     */
    public static void storeCookies(List<Cookie> requestCookies, XFormsProcessor processor) {
        Vector<BasicClientCookie> commonsCookies = new Vector<BasicClientCookie>();

        if (requestCookies != null && requestCookies.size() > 0) {
            commonsCookies = saveAsBasicClientCookie(requestCookies.iterator(), commonsCookies);
        }

        /*
        if (responseCookies != null && responseCookies.size() > 0) {
        commonsCookies= saveAsBasicClientCookie(responseCookies.iterator(), commonsCookies);
        }
        */
        if (commonsCookies.size() == 0) {
            BasicClientCookie sessionCookie = new BasicClientCookie("JSESSIONID",
                    ((WebProcessor) processor).httpSession.getId());
            sessionCookie.setSecure(false);
            sessionCookie.setDomain(null);
            sessionCookie.setPath(null);

            commonsCookies.add(sessionCookie);

        }
        processor.setContextParam(AbstractHTTPConnector.REQUEST_COOKIE,
                commonsCookies.toArray(new BasicClientCookie[0]));
    }

    private static Vector<BasicClientCookie> saveAsBasicClientCookie(Iterator iterator,
            Vector<BasicClientCookie> commonsCookies) {
        while (iterator.hasNext()) {
            javax.servlet.http.Cookie c = (Cookie) iterator.next();
            BasicClientCookie commonsCookie = new BasicClientCookie(c.getName(), c.getValue());
            commonsCookie.setDomain(c.getDomain());
            commonsCookie.setPath(c.getPath());
            commonsCookie.setAttribute(ClientCookie.MAX_AGE_ATTR, Integer.toString(c.getMaxAge()));
            commonsCookie.setSecure(c.getSecure());

            commonsCookies.add(commonsCookie);

            if (WebUtil.LOGGER.isDebugEnabled()) {
                WebUtil.LOGGER.debug("adding cookie >>>>>");
                WebUtil.LOGGER.debug("name: " + c.getName());
                WebUtil.LOGGER.debug("value: " + c.getValue());
                WebUtil.LOGGER.debug("path: " + c.getPath());
                WebUtil.LOGGER.debug("maxAge: " + c.getMaxAge());
                WebUtil.LOGGER.debug("secure: " + c.getSecure());
                WebUtil.LOGGER.debug("adding cookie done <<<<<");
            }
        }

        return commonsCookies;
    }

    /**
     * copy all http headers from client request into betterForm context map as map HTTP_HEADERS. This map
     * will be picked up in AbstractHttpConnector to configure a proper request for the internal http-client.
     */
    public static void copyHttpHeaders(HttpServletRequest request, XFormsProcessor processor) {
        RequestHeaders httpHeaders = new RequestHeaders();

        Enumeration headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerName = (String) headerNames.nextElement();
            httpHeaders.addHeader(headerName, request.getHeader(headerName));
            if (WebUtil.LOGGER.isDebugEnabled()) {
                WebUtil.LOGGER
                        .debug("keeping httpheader: " + headerName + " value: " + request.getHeader(headerName));
            }

        }
        processor.setContextParam(AbstractHTTPConnector.HTTP_REQUEST_HEADERS, httpHeaders);
    }

    /**
     * returns an URL which is passed as request param and decodes it if necessary
     *
     * @param request the HttpServletRequest
     * @return an unencoded absolute Url or a relative Url
     * @throws java.io.UnsupportedEncodingException
     *                                        in case URL is not correctly encoded
     * @throws java.net.MalformedURLException in case URL is not valid
     */
    public static String getFormUrlAsString(HttpServletRequest request)
            throws UnsupportedEncodingException, MalformedURLException {
        String formPath = request.getParameter(WebFactory.FORM_PARAM_NAME);
        return decodeUrl(formPath, request);
    }

    /**
     * this method is responsible for passing all context information needed by the Adapter and Processor from
     * ServletRequest to Context. Will be called only once when the form-session is inited (GET).
     * <p/>
     * <p/>
     * todo: better logging of context params
     *
     * @param request     the Servlet request to fetch params from
     * @param httpSession the Http Session context
     * @param processor   the XFormsProcessor which receives the context params
     * @param sessionkey  the key to identify the XFormsSession
     */
    public static void setContextParams(HttpServletRequest request, HttpSession httpSession,
            XFormsProcessor processor, String sessionkey) throws XFormsConfigException {
        Map servletMap = new HashMap();
        servletMap.put(WebProcessor.SESSION_ID, sessionkey);
        processor.setContextParam(XFormsProcessor.SUBMISSION_RESPONSE, servletMap);

        //adding requestURI to context
        processor.setContextParam(WebProcessor.REQUEST_URI, WebUtil.getRequestURI(request));

        //adding request URL to context
        String requestURL = request.getRequestURL().toString();
        processor.setContextParam(WebProcessor.REQUEST_URL, requestURL);

        // the web app name with an '/' prepended e.g. '/betterform' by default
        String contextRoot = WebUtil.getContextRoot(request);
        processor.setContextParam(WebProcessor.CONTEXTROOT, contextRoot);
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("context root of webapp: " + processor.getContextParam(WebProcessor.CONTEXTROOT));
        }

        String requestPath = "";
        URL url = null;
        String plainPath = "";
        try {
            url = new URL(requestURL);
            requestPath = url.getPath();
        } catch (MalformedURLException e) {
            e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
        }
        if (requestPath.length() != 0) {
            //adding request path e.g. '/betterform/forms/demo/registration.xhtml'
            processor.setContextParam(WebProcessor.REQUEST_PATH, requestPath);

            //adding filename of requested doc to context
            String fileName = requestPath.substring(requestPath.lastIndexOf('/') + 1, requestPath.length());//FILENAME xforms
            processor.setContextParam(FILENAME, fileName);

            if (requestURL.contains(contextRoot)) { //case1: contextRoot is a part of the URL
                //adding plainPath which is the part between contextroot and filename e.g. '/forms' for a requestPath of '/betterform/forms/Status.xhtml'
                plainPath = requestPath.substring(contextRoot.length() + 1,
                        requestPath.length() - fileName.length());
                processor.setContextParam(PLAIN_PATH, plainPath);
            } else {//case2: contextRoot is not a part of the URL take the part previous the filename.
                String[] urlParts = requestURL.split("/");
                plainPath = urlParts[urlParts.length - 2];
            }

            //adding contextPath - requestPath without the filename
            processor.setContextParam(CONTEXT_PATH, contextRoot + "/" + plainPath);
        }

        //adding session id to context
        processor.setContextParam(HTTP_SESSION_ID, httpSession.getId());
        //adding context absolute path to context

        //EXIST-WORKAROUND: TODO triple check ...
        //TODO: triple check where this is used.
        if (request.isRequestedSessionIdValid()) {
            processor.setContextParam(EXISTDB_USER, httpSession.getAttribute(EXISTDB_USER));
        }

        //adding pathInfo to context - attention: this is only available when a servlet is requested
        String s1 = request.getPathInfo();
        if (s1 != null) {
            processor.setContextParam(WebProcessor.PATH_INFO, s1);
        }
        processor.setContextParam(WebProcessor.QUERY_STRING,
                (request.getQueryString() != null ? request.getQueryString() : ""));

        //storing the realpath for webapp

        String realPath = WebFactory.getRealPath(".", httpSession.getServletContext());
        File f = new File(realPath);
        URI fileURI = f.toURI();

        processor.setContextParam(WebProcessor.REALPATH, fileURI.toString());
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("real path of webapp: " + realPath);
        }

        //storing the TransformerService
        processor.setContextParam(TransformerService.TRANSFORMER_SERVICE,
                httpSession.getServletContext().getAttribute(TransformerService.TRANSFORMER_SERVICE));
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("TransformerService: "
                    + httpSession.getServletContext().getAttribute(TransformerService.TRANSFORMER_SERVICE));
        }

        //[2] read any request params that are *not* betterForm params and pass them into the context map
        Enumeration params = request.getParameterNames();
        String s;
        while (params.hasMoreElements()) {
            s = (String) params.nextElement();
            //store all request-params we don't use in the context map of XFormsProcessorImpl
            String value = request.getParameter(s);
            processor.setContextParam(s, value);
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("added request param '" + s + "' added to context");
                LOGGER.debug("param value'" + value);
            }
        }

    }

    public static boolean isMediaTypeXML(String mediatype) {
        boolean isXML;
        isXML = mediatype.startsWith("text") || mediatype.startsWith("application");
        if (isXML) {
            isXML = isXML && (mediatype.contains("xml") || mediatype.contains("xhtml+xml"));
        }

        return isXML;
    }

    /**
     * transforms an input document and writes it to the ServletOutputStream.
     *
     * @param context the servlet context
     * @param response the servlet response
     * @param input an DOM input document to transform
     * @param stylesheetName the name of the stylesheet to use. This must be preloaded in CachingTransformerService. See WebFactory
     * @param params transformation parameters as a piece of DOM if any. The params object is passed as param 'params' to the stylesheets
     * @throws java.io.IOException
     */
    public static void doTransform(ServletContext context, HttpServletResponse response, Document input,
            String stylesheetName, Object params) throws IOException {
        CachingTransformerService transformerService = (CachingTransformerService) context
                .getAttribute(TransformerService.TRANSFORMER_SERVICE);
        Source xmlSource = new DOMSource(input);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            Transformer transformer = transformerService.getTransformerByName(stylesheetName);
            if (params != null) {
                if (params instanceof Node) {
                    transformer.setParameter("params", params);
                }
            }
            transformer.transform(xmlSource, new StreamResult(outputStream));

        } catch (TransformerException e) {
            e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
        }
        response.setContentType(WebUtil.HTML_CONTENT_TYPE);
        response.setContentLength(outputStream.toByteArray().length);
        response.getOutputStream().write(outputStream.toByteArray());
        response.getOutputStream().close();
    }

}