
com.appslandia.plum.utils.ServletUtils Maven / Gradle / Ivy
// The MIT License (MIT)
// Copyright © 2015 AppsLandia. All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package com.appslandia.plum.utils;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
import java.time.DateTimeException;
import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import com.appslandia.common.base.FormatProvider;
import com.appslandia.common.base.SimpleConfig;
import com.appslandia.common.cdi.BeanInstance;
import com.appslandia.common.crypto.DigesterImpl;
import com.appslandia.common.utils.Asserts;
import com.appslandia.common.utils.HexUtils;
import com.appslandia.common.utils.IOUtils;
import com.appslandia.common.utils.ObjectUtils;
import com.appslandia.common.utils.ParseUtils;
import com.appslandia.common.utils.STR;
import com.appslandia.common.utils.SplitUtils;
import com.appslandia.common.utils.StringUtils;
import com.appslandia.common.utils.URLEncoding;
import com.appslandia.common.utils.ValueUtils;
import com.appslandia.plum.base.ActionDesc;
import com.appslandia.plum.base.ActionDescProvider;
import com.appslandia.plum.base.AppConfig;
import com.appslandia.plum.base.AuthHandler;
import com.appslandia.plum.base.AuthHandlerProvider;
import com.appslandia.plum.base.BadRequestException;
import com.appslandia.plum.base.BeanInstanceContextListener;
import com.appslandia.plum.base.HttpException;
import com.appslandia.plum.base.InstanceKey;
import com.appslandia.plum.base.LanguageProvider;
import com.appslandia.plum.base.Messages;
import com.appslandia.plum.base.ModelState;
import com.appslandia.plum.base.MutexContextListener;
import com.appslandia.plum.base.MutexSessionListener;
import com.appslandia.plum.base.RequestContext;
import com.appslandia.plum.base.Resources;
import com.appslandia.plum.base.TempData;
import com.appslandia.plum.base.UserPrincipal;
import jakarta.enterprise.inject.Instance;
import jakarta.enterprise.inject.spi.CDI;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponseWrapper;
import jakarta.servlet.http.HttpSession;
/**
*
* @author Loc Ha
*
*/
public class ServletUtils {
public static final String ACTION_INDEX = "index";
public static final String REQUEST_ATTRIBUTE_MODEL = "model";
public static final String COOKIE_CLIENT_ZONE_ID = "clientZoneId";
public static final String PARAM_RETURN_URL = "returnUrl";
public static final String PARAM_FROM_PAGE_URL = "fromPageUrl";
public static final String PARAM_FORM_ACTION = "formAction";
public static StringBuilder newUrlBuilder() {
return new StringBuilder(255);
}
public static String getUriQuery(HttpServletRequest request) {
return appendUriQuery(request, newUrlBuilder()).toString();
}
public static StringBuilder appendUriQuery(HttpServletRequest request, StringBuilder sb) {
sb.append(request.getRequestURI());
if (request.getQueryString() != null) {
sb.append('?').append(request.getQueryString());
}
return sb;
}
public static StringBuilder getLoginUrl(HttpServletRequest request) {
LanguageProvider languageProvider = ServletUtils.getAppScoped(request.getServletContext(), LanguageProvider.class);
RequestContext requestContext = getRequestContext(request);
// URL
StringBuilder url = newUrlBuilder();
url.append(request.getContextPath());
// Language
if (languageProvider.isMultiLanguages()) {
url.append('/').append(requestContext.getLanguageId());
}
ActionDescProvider actionDescProvider = getAppScoped(request.getServletContext(), ActionDescProvider.class);
ActionDesc formLogin = actionDescProvider.getFormLogin(requestContext.getModule());
// If @FormLogin
if (formLogin != null) {
url.append('/').append(formLogin.getController());
url.append('/').append(formLogin.getAction());
url.append('/');
} else {
// Use loginPage from the authHandler
AuthHandlerProvider authHandlerProvider = ServletUtils.getAppScoped(request.getServletContext(),
AuthHandlerProvider.class);
AuthHandler authHandler = authHandlerProvider.getAuthHandler(requestContext.getModule());
url.append(authHandler.getLoginPage());
}
return url;
}
public static String getScheme(HttpServletRequest request) {
return ValueUtils.valueOrAlt(request.getHeader("X-Forwarded-Proto"), request.getScheme());
}
public static String getHost(HttpServletRequest request) {
return ValueUtils.valueOrAlt(request.getHeader("X-Forwarded-Host"), request.getServerName());
}
static final Pattern X_FORWARDED_PORTS_PATTERN = Pattern.compile("\\s*\\d+\\s*,\\s*\\d+\\s*");
public static String getPort(HttpServletRequest request) {
// X-Forwarded-Port
String port = request.getHeader("X-Forwarded-Port");
if (port != null) {
return port;
}
// config.x_forwarded_ports
AppConfig appConfig = getAppScoped(request.getServletContext(), AppConfig.class);
String httpPorts = appConfig.getString(AppConfig.CONFIG_X_FORWARDED_PORTS);
if (httpPorts != null) {
Asserts.isTrue(X_FORWARDED_PORTS_PATTERN.matcher(httpPorts).matches(),
STR.fmt("X-Forwarded-Ports '{}' is invalid.", httpPorts));
String[] ports = SplitUtils.splitByComma(httpPorts);
String scheme = getScheme(request);
return "https".equals(scheme) ? ports[1] : ports[0];
} else {
// request.getServerPort()
return Integer.toString(request.getServerPort());
}
}
public static StringBuilder absHostBase(HttpServletRequest request) {
String scheme = getScheme(request);
String host = getHost(request);
String port = getPort(request);
StringBuilder url = newUrlBuilder();
url.append(scheme).append("://").append(host);
if ("https".equals(scheme)) {
if (!"443".equals(port)) {
url.append(':').append(port);
}
} else {
if (!"80".equals(port)) {
url.append(':').append(port);
}
}
return url;
}
public static String getRequestUrl(HttpServletRequest request) {
StringBuilder url = newUrlBuilder();
// URI & QueryString
appendUriQuery(request, url);
return url.toString();
}
/**
* Returns the request URL with the embedded path language removed if it exists
* and pathLang is null.
*
*/
public static String getRequestUrl(HttpServletRequest request, String pathLang) {
StringBuilder url = newUrlBuilder();
// ContextPath
url.append(request.getContextPath());
// Language
if (pathLang != null) {
url.append('/').append(pathLang);
}
// ServletPath
RequestContext requestContext = ServletUtils.getRequestContext(request);
if (requestContext.isPathLanguage()) {
url.append(request.getServletPath().substring(requestContext.getLanguageId().length() + 1));
} else {
url.append(request.getServletPath());
}
// PathInfo
if (request.getPathInfo() != null) {
url.append(request.getPathInfo());
}
// QueryString
if (request.getQueryString() != null) {
url.append('?').append(request.getQueryString());
}
return url.toString();
}
public static String getClientIp(HttpServletRequest request) {
String value = request.getHeader("X-Forwarded-For");
if (value == null) {
return request.getRemoteAddr();
}
int idx = value.indexOf(',');
if (idx < 0) {
return request.getRemoteAddr();
}
value = value.substring(0, idx).strip();
return !value.isEmpty() ? value : request.getRemoteAddr();
}
public static String getUserAgent(HttpServletRequest request) {
return request.getHeader("User-Agent");
}
public static String getAppDir(ServletContext sc) {
String appDir = sc.getRealPath("/");
Asserts.notNull(appDir, "Couldn't determine appDir.");
return appDir;
}
public static boolean isAjaxRequest(HttpServletRequest request) {
String xrw = request.getHeader("X-Requested-With");
return "XMLHttpRequest".equalsIgnoreCase(xrw);
}
public static void setContentDisposition(HttpServletResponse response, String fileName, boolean inline) {
String dispositionType = inline ? "inline" : "attachment";
response.setHeader("Content-Disposition",
dispositionType + "; filename=\"" + URLEncoding.encodePath(fileName) + "\"");
}
public static boolean isContentSupported(String checkingContentType, String supportedType) {
if (checkingContentType == null) {
return false;
}
int idx = checkingContentType.indexOf(';');
String mimeType = (idx < 0) ? checkingContentType : checkingContentType.substring(0, idx);
return supportedType.equalsIgnoreCase(mimeType);
}
public static String getBestLanguage(HttpServletRequest request, Collection supportedLanguages) {
Enumeration requestLocales = request.getLocales();
while (requestLocales.hasMoreElements()) {
Locale requestLocale = requestLocales.nextElement();
String matchedLang = supportedLanguages.stream()
.filter(sl -> Locale.of(sl).getLanguage().equals(requestLocale.getLanguage())).findFirst().orElse(null);
if (matchedLang != null) {
return matchedLang;
}
}
return null;
}
public static String getBestEncoding(HttpServletRequest request, Collection supportedEncodings) {
String acceptEncoding = request.getHeader("Accept-Encoding");
if (acceptEncoding == null || acceptEncoding.isEmpty()) {
return null;
}
String bestEncType = null;
double bestQValue = 0.0;
String[] encodings = acceptEncoding.split(",");
for (String encoding : encodings) {
String[] parts = encoding.split(";");
String encType = parts[0].strip();
double qValue = 1.0;
if (parts.length > 1 && parts[1].strip().startsWith("q=")) {
try {
qValue = Double.parseDouble(parts[1].strip().substring(2));
} catch (NumberFormatException ex) {
}
}
// identity & *
if (("identity".equalsIgnoreCase(encType) || "*".equals(encType)) && (qValue > bestQValue)) {
bestEncType = encType;
bestQValue = qValue;
}
// supportedEncodings
for (String type : supportedEncodings) {
if (type.equals(encType) && qValue > bestQValue) {
bestEncType = encType;
bestQValue = qValue;
}
}
}
if ("identity".equalsIgnoreCase(bestEncType) || "*".equals(bestEncType)) {
bestEncType = null;
}
return bestEncType;
}
public static Object getMutex(HttpSession session) {
Object mutex = session.getAttribute(MutexSessionListener.ATTRIBUTE_MUTEX);
return (mutex != null) ? mutex : session;
}
public static Object getMutex(ServletContext context) {
Object mutex = context.getAttribute(MutexContextListener.ATTRIBUTE_MUTEX);
return (mutex != null) ? mutex : context;
}
public static HttpServletResponse assertNotCommitted(HttpServletResponse response) throws IllegalStateException {
if (response.isCommitted()) {
throw new IllegalStateException("The response is already committed.");
}
return response;
}
public static RequestDispatcher getRequestDispatcher(HttpServletRequest request, String path) {
RequestDispatcher dispatcher = request.getRequestDispatcher(path);
return Asserts.notNull(dispatcher, () -> STR.fmt("Couldn't obtain a dispatcher for the path '{}'.", path));
}
public static void forward(HttpServletRequest request, HttpServletResponse response, String path)
throws ServletException, IOException {
getRequestDispatcher(request, path).forward(request, assertNotCommitted(response));
}
public static void include(HttpServletRequest request, HttpServletResponse response, String path)
throws ServletException, IOException {
getRequestDispatcher(request, path).include(request, response);
}
public static void sendRedirect(HttpServletResponse response, String location) throws IllegalStateException {
sendRedirect(response, location, HttpServletResponse.SC_MOVED_TEMPORARILY);
}
public static void sendRedirect(HttpServletResponse response, String location, int status)
throws IllegalStateException {
assertNotCommitted(response);
response.resetBuffer();
response.setHeader("Location", location);
response.setStatus(status);
}
public static void testErrorStatus(HttpServletRequest request, String parameterName) {
String statusValue = request.getParameter(parameterName);
if (statusValue == null) {
return;
}
// 4XX, 5XX
int status = ParseUtils.parseInt(statusValue, 0);
if (status >= 400 && status < 600) {
throw new HttpException(status, parameterName + "=" + status);
} else {
throw new BadRequestException(parameterName + " is invalid.");
}
}
public static void testOutStream(HttpServletRequest request, HttpServletResponse response, String parameterName)
throws IOException {
String streamType = request.getParameter(parameterName);
if (streamType == null) {
return;
}
if ("writer".equals(streamType)) {
response.getWriter();
} else if ("stream".equals(streamType)) {
response.getOutputStream();
}
}
public static String toWrapperPath(HttpServletRequest request) {
StringBuilder sb = new StringBuilder();
HttpServletRequest req = request;
while (true) {
if (sb.length() == 0) {
sb.append(ObjectUtils.toIdHash(req));
} else {
sb.append(" > ").append(ObjectUtils.toIdHash(req));
}
if (!(req instanceof HttpServletRequestWrapper)) {
return sb.toString();
}
req = (HttpServletRequest) ((HttpServletRequestWrapper) req).getRequest();
}
}
public static String toWrapperPath(HttpServletResponse response) {
StringBuilder sb = new StringBuilder();
HttpServletResponse resp = response;
while (true) {
if (sb.length() == 0) {
sb.append(ObjectUtils.toIdHash(resp));
} else {
sb.append(" > ").append(ObjectUtils.toIdHash(resp));
}
if (!(resp instanceof HttpServletResponseWrapper)) {
return sb.toString();
}
resp = (HttpServletResponse) ((HttpServletResponseWrapper) resp).getResponse();
}
}
public static String getCookieValue(HttpServletRequest request, String name) {
Cookie[] cookies = request.getCookies();
if (cookies == null) {
return null;
}
Cookie cookie = Arrays.stream(cookies).filter(c -> c.getName().equalsIgnoreCase(name)).findFirst().orElse(null);
return (cookie != null) ? cookie.getValue() : null;
}
public static void removeCookie(HttpServletResponse response, String name, String domain, String path) {
Cookie cookie = new Cookie(name, StringUtils.EMPTY_STRING);
cookie.setMaxAge(0);
if (domain != null) {
cookie.setDomain(domain);
}
cookie.setPath(path);
response.addCookie(cookie);
}
public static String getCookiePath(ServletContext sc) {
String path = ValueUtils.valueOrAlt(sc.getSessionCookieConfig().getPath(), sc.getContextPath());
return !path.isEmpty() ? path : "/";
}
public static ZoneOffset getClientZoneId(HttpServletRequest request) {
String clientZoneId = getCookieValue(request, COOKIE_CLIENT_ZONE_ID);
if (clientZoneId == null) {
return null;
}
try {
return ZoneOffset.of(clientZoneId);
} catch (DateTimeException ex) {
return null;
}
}
public static String toEtag(byte[] md5) {
StringBuilder sb = new StringBuilder(34);
sb.append('"');
HexUtils.appendAsHex(sb, md5);
sb.append('"');
return sb.toString();
}
public static String toEtag(Object... args) {
List list = Arrays.stream(args).map(o -> Objects.toString(o)).collect(Collectors.toList());
return toEtag(new DigesterImpl("MD5").digest(String.join("|", list).getBytes(StandardCharsets.UTF_8)));
}
public static boolean checkNotModified(HttpServletRequest request, HttpServletResponse response,
long lastModifiedMs) {
response.setDateHeader("Last-Modified", lastModifiedMs);
long ifModifiedSince = request.getDateHeader("If-Modified-Since");
boolean notModified = ifModifiedSince >= (lastModifiedMs / 1000 * 1000);
if (notModified) {
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
return notModified;
}
public static boolean checkNotModified(HttpServletRequest request, HttpServletResponse response, String etag) {
response.setHeader("ETag", etag);
String ifNoneMatch = request.getHeader("If-None-Match");
boolean notModified = etag.equals(ifNoneMatch);
if (notModified) {
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
return notModified;
}
public static boolean checkPrecondition(HttpServletRequest request, HttpServletResponse response,
long lastModifiedMs) {
response.setDateHeader("Last-Modified", lastModifiedMs);
long ifUnmodifiedSince = request.getDateHeader("If-Unmodified-Since");
return ifUnmodifiedSince == (lastModifiedMs / 1000 * 1000);
}
public static boolean checkPrecondition(HttpServletRequest request, HttpServletResponse response, String etag) {
response.setHeader("ETag", etag);
String ifMatch = request.getHeader("If-Match");
return etag.equals(ifMatch);
}
public static UserPrincipal getUserPrincipal(HttpServletRequest request) {
Principal principal = request.getUserPrincipal();
if (principal == null) {
return null;
}
if (!(principal instanceof UserPrincipal)) {
throw new IllegalStateException("request.getUserPrincipal() must be UserPrincipal.");
}
return (UserPrincipal) principal;
}
public static UserPrincipal getRequiredPrincipal(HttpServletRequest request) {
return Asserts.notNull(getUserPrincipal(request), "request.getUserPrincipal() is required.");
}
public static void setWWWAuthenticate(HttpServletResponse response, String authType, String realmName) {
response.setHeader("WWW-Authenticate", authType + " realm=\"" + realmName + "\"");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
public static Object removeAttribute(HttpServletRequest request, String attribute) {
Object obj = request.getAttribute(attribute);
if (obj != null) {
request.removeAttribute(attribute);
}
return obj;
}
public static String getInitParam(FilterConfig config, String paramName, String defaultValue) {
String paramVal = StringUtils.trimToNull(config.getInitParameter(paramName));
return (paramVal != null) ? paramVal : defaultValue;
}
public static String getInitParam(ServletConfig config, String paramName, String defaultValue) {
String paramVal = StringUtils.trimToNull(config.getInitParameter(paramName));
return (paramVal != null) ? paramVal : defaultValue;
}
public static String getInitParam(ServletContext sc, String paramName, String defaultValue) {
String paramVal = StringUtils.trimToNull(sc.getInitParameter(paramName));
return (paramVal != null) ? paramVal : defaultValue;
}
public static RequestContext getRequestContext(HttpServletRequest request) {
RequestContext obj = (RequestContext) request.getAttribute(RequestContext.REQUEST_ATTRIBUTE_ID);
return Asserts.notNull(obj);
}
public static ModelState getModelState(HttpServletRequest request) {
ModelState obj = (ModelState) request.getAttribute(ModelState.REQUEST_ATTRIBUTE_ID);
if (obj == null) {
obj = new ModelState();
request.setAttribute(ModelState.REQUEST_ATTRIBUTE_ID, obj);
}
return obj;
}
public static Messages getMessages(HttpServletRequest request) {
Messages obj = (Messages) request.getAttribute(Messages.REQUEST_ATTRIBUTE_ID);
if (obj == null) {
obj = new Messages();
request.setAttribute(Messages.REQUEST_ATTRIBUTE_ID, obj);
}
return obj;
}
public static TempData getTempData(HttpServletRequest request) {
TempData obj = (TempData) request.getAttribute(TempData.REQUEST_ATTRIBUTE_ID);
if (obj == null) {
obj = new TempData();
request.setAttribute(TempData.REQUEST_ATTRIBUTE_ID, obj);
}
return obj;
}
public static Resources getResources(HttpServletRequest request) {
return ServletUtils.getRequestContext(request).getResources();
}
public static FormatProvider getFormatProvider(HttpServletRequest request) {
return ServletUtils.getRequestContext(request).getFormatProvider();
}
public static void addError(HttpServletRequest request, String fieldName, String msgKey) {
String msg = getResources(request).get(msgKey);
ServletUtils.getModelState(request).addError(fieldName, msg);
}
public static void addError(HttpServletRequest request, String fieldName, String msgKey, Object[] msgParams) {
String msg = getResources(request).get(msgKey, msgParams);
ServletUtils.getModelState(request).addError(fieldName, msg);
}
public static T getAppScoped(ServletContext sc, Class type) {
return getAppScoped(sc, type, null);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public static T getAppScoped(ServletContext sc, Class type, Annotation qualifier) {
Map> beanInsts = BeanInstanceContextListener.getBeanInstances(sc);
BeanInstance> bi = beanInsts.computeIfAbsent(new InstanceKey(type, qualifier), k -> {
Instance> inst = null;
if (k.getQualifier() == null) {
inst = CDI.current().select(k.getType());
} else {
inst = CDI.current().select(k.getType(), k.getQualifier());
}
return ObjectUtils.cast(new BeanInstance(inst.get(), inst));
});
return ObjectUtils.cast(bi.get());
}
public static boolean loadProps(ServletContext sc, String resourcePath, SimpleConfig config) throws IOException {
InputStream is = sc.getResourceAsStream(resourcePath);
if (is == null) {
return false;
}
try {
config.load(is);
return true;
} finally {
IOUtils.closeQuietly(is);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy