org.silverpeas.core.web.http.HttpRequest.java Source code

Java tutorial

Introduction

Here is the source code for org.silverpeas.core.web.http.HttpRequest.java

Source

/*
 * Copyright (C) 2000 - 2018 Silverpeas
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * As a special exception to the terms and conditions of version 3.0 of
 * the GPL, you may redistribute this Program in connection with Free/Libre
 * Open Source Software ("FLOSS") applications as described in Silverpeas's
 * FLOSS exception.  You should have received a copy of the text describing
 * the FLOSS exception, and it is also available here:
 * "https://www.silverpeas.org/legal/floss_exception.html"
 *
 * 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.silverpeas.core.web.http;

import org.apache.commons.fileupload.FileItem;
import org.silverpeas.core.SilverpeasRuntimeException;
import org.silverpeas.core.admin.user.model.User;
import org.silverpeas.core.cache.service.CacheServiceProvider;
import org.silverpeas.core.i18n.I18NHelper;
import org.silverpeas.core.io.upload.FileUploadManager;
import org.silverpeas.core.io.upload.UploadedFile;
import org.silverpeas.core.util.StringUtil;
import org.silverpeas.core.util.file.FileUploadUtil;
import org.silverpeas.core.util.logging.SilverLogger;

import javax.servlet.ServletRequest;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static java.util.Arrays.asList;
import static java.util.Arrays.stream;
import static org.apache.commons.lang3.StringUtils.split;
import static org.silverpeas.core.web.http.RequestParameterDecoder.*;

/**
 * An HTTP request decorating an HTTP servlet request with some additional methods and by changing
 * the implementation of some of its methods to take into account some Silverpeas specificities or
 * needs. For example, the <code>getParameter(java.lang.String)</code> method has been modified to
 * take into account also the parameters passed in a multipart/form-data stream.
 *
 * @author mmoquillon
 */
public class HttpRequest extends HttpServletRequestWrapper {

    private static final String SECURE_PROPERTY = "secure";

    private List<FileItem> fileItems = null;

    private HttpRequest(HttpServletRequest request) {
        super(request);
        // The decorated request is put into attributes in order to provide it to the REST web
        // services that deals with proxies...
        request.setAttribute(HttpRequest.class.getName(), this);
    }

    /**
     * Decorates the specified HTTP servlet request with an HttpRequest instance. If the request is
     * already an HttpRequest instance, then it is simply returned.
     *
     * @param request the Http servlet request to decorate.
     * @return an HttpRequest instance decorating the specified request.
     */
    public static HttpRequest decorate(final HttpServletRequest request) {
        CacheServiceProvider.getRequestCacheService().getCache().put(SECURE_PROPERTY, request.isSecure());
        return request instanceof HttpRequest ? (HttpRequest) request : new HttpRequest(request);
    }

    /**
     * Decorates the specified servlet request with an HttpRequest instance. If the request is already
     * an HttpRequest instance, then it is simply returned.
     *
     * @param request the servlet request to decorate. Must be of type HttpServletRequest.
     * @return an HttpRequest instance decorating the specified request.
     */
    public static HttpRequest decorate(final ServletRequest request) {
        return decorate((HttpServletRequest) request);
    }

    /**
     * Is the HTTP request currently processed is secure (that is carried through a TLS connection)?
     * @return true of the current HTTP request is secure, false otherwise.
     */
    public static boolean isCurrentRequestSecure() {
        Boolean secure = CacheServiceProvider.getRequestCacheService().getCache().get(SECURE_PROPERTY,
                Boolean.class);
        if (secure == null) {
            throw new IllegalStateException(
                    "The current execution context isn't relative to an incoming HTTP request");
        }
        return secure;
    }

    /**
     * Is this request within an anonymous user session?
     *
     * @return true if the request is sent in the context of an opened user session and this session
     * is for an anonymous user.
     */
    public boolean isWithinAnonymousUserSession() {
        User user = User.getCurrentRequester();
        return user != null && user.isAnonymous();
    }

    /**
     * Is this request within an opened user session?
     *
     * @return true if the request is sent in the context of a Silvepreas user session.
     */
    public boolean isWithinUserSession() {
        return User.getCurrentRequester() != null;
    }

    /**
     * Processes an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant
     * multipart/form-data stream.
     *
     * @return a list of FileItem instances parsed from the request, in the order that they were
     * transmitted.
     */
    public List<FileItem> getFileItems() {
        if (fileItems == null) {
            fileItems = FileUploadUtil.parseRequest(this);
        }
        return fileItems;
    }

    /**
     * Processes an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant
     * multipart/form-data stream and returns the item whose the name matches the specified one.
     *
     * @param name the name of the data to fetch.
     * @return the FileItem instance whose the name matches the specified one or null if no such data
     * exists in the multipart/form-data stream. The file item can be either a file or a parameter.
     */
    public FileItem getFileItem(String name) {
        FileItem item = null;
        List<FileItem> items = getFileItems();
        for (FileItem fileItem : items) {
            if (fileItem.getFieldName().equals(name)) {
                item = fileItem;
                break;
            }
        }
        return item;
    }

    /**
     * Processes an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant
     * multipart/form-data stream and returns the item whose the type is a file and the name matches
     * the specified one.
     * @param name the name of the file item to fetch.
     * @return the file item whose the name matches the specified one or null if no such data exists
     * in the multipart stream.
     */
    public FileItem getFile(String name) {
        return FileUploadUtil.getFile(getFileItems(), name);
    }

    /**
     * Processes an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant
     * multipart/form-data stream and returns the item whose the type is a file. If there is several
     * items that represent a file, then only the first one is returned.
     * @return the file item representing a file in the multipart stream or null if no such data
     * exists in the multipart stream.
     */
    public FileItem getSingleFile() {
        return FileUploadUtil.getFile(getFileItems());
    }

    /**
     * Is this request has a cookie with the specified name?
     *
     * @param name the name of the cookie.
     * @return true if a cookie with the specified name is carried by this request, false otherwise.
     */
    public boolean hasCookie(String name) {
        if (StringUtil.isNotDefined(name)) {
            return false;
        }
        boolean found = false;
        Cookie[] cookies = getCookies();
        for (int i = 0; i < cookies.length && !found; i++) {
            found = cookies[i].getName().equals(name);
        }
        return found;
    }

    /**
     * Gets the language of the user behind this request.
     *
     * @return the language of the user as he has chosen in its profile in Silverpeas.
     */
    public String getUserLanguage() {
        String language = I18NHelper.defaultLanguage;
        User user = User.getCurrentRequester();
        if (user != null) {
            language = user.getUserPreferences().getLanguage();
        }
        return language;
    }

    /**
     * Retrieves from {@link HttpServletRequest} a collection of {@link UploadedFile}.
     *
     * @return collection of {@link UploadedFile}. Empty collection if no uploaded file exists.
     */
    public Collection<UploadedFile> getUploadedFiles() {
        Collection<UploadedFile> uploadedFiles = new ArrayList<>();
        User user = User.getCurrentRequester();
        if (user != null) {
            uploadedFiles = FileUploadManager.getUploadedFiles(this, user);
        }
        return uploadedFiles;
    }

    /**
     * Get a parameter value as a Long.
     *
     * @param attributeName the name of the attribute.
     * @return the value of the attribute as a long.
     */
    public Long getAttributeAsLong(String attributeName) {
        return asLong(getAttribute(attributeName));
    }

    /**
     * Returns an array of String objects containing all of the values the given request parameter
     * has, or null if the parameter does not exist. The parameters from a multipart/form-data stream
     * are also considered by this method, unlike of the default behavior of the decorated request.
     *
     * If the parameter has a single value, the array has a length of 1.
     *
     * @param name the name of the parameter whose value is requested.
     * @return an array of String objects containing the parameter's values.
     */
    @Override
    public String[] getParameterValues(String name) {
        String[] values = super.getParameterValues(name);
        if (values == null && isContentInMultipart()) {
            List<String> listOfValues = FileUploadUtil.getParameterValues(getFileItems(), name,
                    getCharacterEncoding());
            values = listOfValues.toArray(new String[listOfValues.size()]);
        }
        return values;
    }

    /**
     * Returns an Enumeration of String objects containing the names of the parameters contained in
     * this request. If the request has no parameters, the method returns an empty Enumeration. The
     * parameters from a multipart/form-data stream are also considered by this method, unlike of the
     * default behavior of the decorated request.
     *
     * @return an Enumeration of String objects, each String containing the name of a request
     * parameter; or an empty Enumeration if the request has no parameters.
     */
    @Override
    public Enumeration<String> getParameterNames() {
        Enumeration<String> names = super.getParameterNames();
        if (!names.hasMoreElements() && isContentInMultipart()) {
            List<FileItem> items = getFileItems();
            List<String> itemNames = new ArrayList<>(items.size());
            for (FileItem item : items) {
                if (item.isFormField()) {
                    itemNames.add(item.getFieldName());
                }
            }
            names = Collections.enumeration(itemNames);
        }
        return names;
    }

    /**
     * Returns a java.util.Map of the parameters of this request.
     *
     * Request parameters are extra information sent with the request. For HTTP servlets, parameters
     * are contained in the query string or posted form data. The parameters from a
     * multipart/form-data stream are also considered by this method, unlike of the default behavior
     * of the decorated request.
     *
     * @return an immutable java.util.Map containing parameter names as keys and parameter values as
     * map values. The keys in the parameter map are of type String. The values in the parameter map
     * are of type String array.
     */
    @Override
    public Map<String, String[]> getParameterMap() {
        Map<String, String[]> map = super.getParameterMap();
        if (map.isEmpty() && isContentInMultipart()) {
            List<FileItem> items = getFileItems();
            map = new HashMap<>(items.size());
            for (FileItem item : items) {
                if (item.isFormField()) {
                    String[] value;
                    try {
                        value = new String[] { item.getString(getCharacterEncoding()) };
                    } catch (UnsupportedEncodingException ex) {
                        SilverLogger.getLogger(this).warn(ex);
                        value = new String[] { item.getString() };
                    }
                    map.put(item.getFieldName(), value);
                }
            }
            map = Collections.unmodifiableMap(map);
        }
        return map;
    }

    /**
     * Merges into given collection of identifiers the selected and unselected identifiers
     * extracted from the current request.
     * <p>
     * Default parameter names are used:
     * <ul>
     * <li>selectedIds: parameter name to retrieve selected identifiers.</li>
     * <li>unselectedIds: parameter name to retrieve unselected identifiers.</li>
     * </ul>
     * </p>
     * @param selectedIds the collection of selected identifiers.
     */
    public void mergeSelectedItemsInto(Collection<String> selectedIds) {
        mergeSelectedItemsInto(selectedIds, "selectedIds", "unselectedIds");
    }

    /**
     * Merges into given collection of identifiers the selected and unselected identifiers
     * extracted from the current request.
     * @param selectedIds the collection of selected identifiers.
     * @param selectedParamName the parameter name of selected identifiers.
     * @param unselectedParamName the parameter name of unselected identifiers.
     */
    public void mergeSelectedItemsInto(Collection<String> selectedIds, String selectedParamName,
            String unselectedParamName) {
        final String[] selected = getParameterMap().get(selectedParamName);
        final String[] unselected = getParameterMap().get(unselectedParamName);
        if (selected != null) {
            stream(selected).flatMap(i -> stream(i.split(","))).forEach(selectedIds::add);
        }
        if (unselected != null) {
            stream(unselected).flatMap(i -> stream(i.split(","))).forEach(selectedIds::remove);
        }
    }

    /**
     * Returns the value of a request parameter as a String, or null if the parameter does not exist.
     * Request parameters are extra information sent with the request. For HTTP servlets, parameters
     * are contained in the query string or posted form data. The parameters from a
     * multipart/form-data stream are also considered by this method, unlike of the default behavior
     * of the decorated request.
     *
     * You should only use this method when you are sure the parameter has only one value. If the
     * parameter might have more than one value, use getParameterValues(java.lang.String).
     *
     * If you use this method with a multivalued parameter, the value returned is equal to the first
     * value in the array returned by getParameterValues.
     *
     * If the parameter data was sent in the request body, such as occurs with an HTTP POST request,
     * then reading the body directly via getInputStream() or getReader() can interfere with the
     * execution of this method.
     *
     * @param name the name of the parameter.
     * @return the single value of the parameter.
     */
    @Override
    public String getParameter(String name) {
        String value = super.getParameter(name);
        if (value == null && isContentInMultipart()) {
            value = FileUploadUtil.getParameter(getFileItems(), name, null, getCharacterEncoding());
        }
        return value;
    }

    /**
     * Is the specified parameter defined?
     * @param name the name of the parameter.
     * @return true if the value of the parameter isn't null and not empty.
     */
    public boolean isParameterDefined(final String name) {
        return StringUtil.isDefined(super.getParameter(name));
    }

    /**
     * Is the specified parameter not null?
     * @param name the name of the parameter.
     * @return true if the parameter is valued, even if this value is empty.
     */
    public boolean isParameterNotNull(final String name) {
        return super.getParameter(name) != null;
    }

    /**
     * Get a parameter value as a {@link List} of string.
     *
     * @param parameterName the name of the parameter.
     * @return the value of the parameter as a {@link List} of string.
     */
    public List<String> getParameterAsList(String parameterName) {
        String[] values = getParameterMap().get(parameterName);
        if (values == null) {
            values = new String[0];
        }
        if (values.length == 1) {
            return asList(split(values[0], ","));
        }
        return Arrays.stream(values).collect(Collectors.toList());
    }

    /**
     * Get a parameter value as a {@link RequestFile}.
     *
     * @param parameterName the name of the parameter.
     * @return the value of the parameter as a {@link RequestFile}.
     */
    public RequestFile getParameterAsRequestFile(String parameterName) {
        RequestFile requestFile = null;
        FileItem fileItem = FileUploadUtil.getFile(getFileItems(), parameterName);
        if (fileItem != null) {
            requestFile = new RequestFile(fileItem);
        }
        return requestFile;
    }

    /**
     * Get a parameter value as a boolean.
     *
     * @param parameterName the name of the parameter.
     * @return the value of the parameter as a boolean.
     */
    public boolean getParameterAsBoolean(String parameterName) {
        return asBoolean(getParameter(parameterName));
    }

    /**
     * Get a parameter value as a list of boolean.
     *
     * @param parameterName the name of the parameter.
     * @return the value of the parameter as a list of boolean.
     */
    public List<Boolean> getParameterAsBooleanList(String parameterName) {
        return getParameterAsList(parameterName).stream().map(RequestParameterDecoder::asBoolean)
                .collect(Collectors.toList());
    }

    /**
     * Get a parameter value as a Long.
     *
     * @param parameterName the name of the parameter.
     * @return the value of the parameter as a long.
     */
    public Long getParameterAsLong(String parameterName) {
        return asLong(getParameter(parameterName));
    }

    /**
     * Get a parameter value as a list of long.
     *
     * @param parameterName the name of the parameter.
     * @return the value of the parameter as a list of long.
     */
    public List<Long> getParameterAsLongList(String parameterName) {
        return getParameterAsList(parameterName).stream().map(RequestParameterDecoder::asLong)
                .collect(Collectors.toList());
    }

    /**
     * Get a parameter value as a Integer.
     *
     * @param parameterName the name of the parameter.
     * @return the value of the parameter as an integer.
     */
    public Integer getParameterAsInteger(String parameterName) {
        return asInteger(getParameter(parameterName));
    }

    /**
     * Get a parameter value as a list of integer.
     *
     * @param parameterName the name of the parameter.
     * @return the value of the parameter as a list of integer.
     */
    public List<Integer> getParameterAsIntegerList(String parameterName) {
        return getParameterAsList(parameterName).stream().map(RequestParameterDecoder::asInteger)
                .collect(Collectors.toList());
    }

    /**
     * Get a date from a date parameter.
     *
     * @param dateParameterName the name of the parameter.
     * @return the value of the parameter as a date.
     * @throws java.text.ParseException if the parameter value isn't a date.
     */
    public Date getParameterAsDate(String dateParameterName) throws ParseException {
        return asDate(getParameter(dateParameterName), null, getUserLanguage());
    }

    /**
     * Get a parameter value as a list of date.
     *
     * @param dateParameterName the name of the parameter.
     * @return the value of the parameter as a list of date.
     */
    public List<Date> getParameterAsDateList(String dateParameterName) {
        return getParameterAsList(dateParameterName).stream().map(p -> {
            try {
                return asDate(p, null, getUserLanguage());
            } catch (ParseException e) {
                throw new SilverpeasRuntimeException(e);
            }
        }).collect(Collectors.toList());
    }

    /**
     * Get a date from one date parameter and one hour parameter.
     *
     * @param dateParameterName the name of the date parameter.
     * @param hourParameterName the name of the time parameter.
     * @return the value of the parameter as a date.
     * @throws java.text.ParseException if the parameter value isn't a date.
     */
    public Date getParameterAsDate(String dateParameterName, String hourParameterName) throws ParseException {
        return asDate(getParameter(dateParameterName), getParameter(hourParameterName), getUserLanguage());
    }

    /**
     * Get an enum instance from one parameter.
     *
     * @param enumValue the string value of the expected enum instance.
     * @param enumClass the class of the expected enum instance.
     * @param <E> the type of the expected enum instance.
     * @return the expected enum instance or null if enum has not been well decoded
     */
    public <E extends Enum> E getParameterAsEnum(String enumValue, Class<E> enumClass) {
        return asEnum(getParameter(enumValue), enumClass);
    }

    /**
     * Get a parameter value as a list of enum.
     *
     * @param parameterName the name of the parameter.
     * @param enumClass the class of the expected enum instance.
     * @return the value of the parameter as a list of enum.
     */
    public <E extends Enum> List<E> getParameterAsEnumList(String parameterName, Class<E> enumClass) {
        return getParameterAsList(parameterName).stream().map(p -> asEnum(p, enumClass))
                .collect(Collectors.toList());
    }

    /**
     * Is the content in this request is encoded in a multipart stream.
     * @return true if the content type of this request is a compilant multipart/form-data stream,
     * false otherwise.
     */
    public boolean isContentInMultipart() {
        return FileUploadUtil.isRequestMultipart(this);
    }

    @Override
    public String getCharacterEncoding() {
        String encoding = super.getCharacterEncoding();
        if (StringUtil.isNotDefined(encoding)) {
            encoding = FileUploadUtil.DEFAULT_ENCODING;
        }
        return encoding;
    }
}