blob: c7353a7ab596120e0406eb517797e811f9d4ded3 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2019 Raymond Augé and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Raymond Augé - bug fixes and enhancements
******************************************************************************/
package org.eclipse.equinox.http.servlet.internal.util;
import static org.osgi.service.http.whiteboard.HttpWhiteboardConstants.*;
import java.lang.reflect.Array;
import java.util.*;
import org.eclipse.equinox.http.servlet.internal.dto.ExtendedErrorPageDTO;
import org.eclipse.equinox.http.servlet.internal.dto.ExtendedErrorPageDTO.ErrorCodeType;
import org.eclipse.equinox.http.servlet.internal.error.HttpWhiteboardFailureException;
import org.osgi.dto.DTO;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.service.http.runtime.dto.*;
/**
* @author Raymond Augé
*/
public class DTOUtil {
public static ExtendedErrorPageDTO assembleErrorPageDTO(ServiceReference<?> serviceReference, long contextId, boolean validated) {
Object errorPageObj = serviceReference.getProperty(HTTP_WHITEBOARD_SERVLET_ERROR_PAGE);
if (errorPageObj == null) {
return null;
}
ExtendedErrorPageDTO errorPageDTO = new ExtendedErrorPageDTO();
errorPageDTO.asyncSupported = false;
Object asyncSupportedObj = serviceReference.getProperty(HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED);
if (asyncSupportedObj == null) {
// ignored
}
else if (Boolean.class.isInstance(asyncSupportedObj)) {
errorPageDTO.asyncSupported = ((Boolean)asyncSupportedObj).booleanValue();
}
else if (String.class.isInstance(asyncSupportedObj)) {
errorPageDTO.asyncSupported = Boolean.valueOf((String)asyncSupportedObj);
}
// There is no validation for this scenario, truthiness of any other input is false
List<String> errorPages = StringPlus.from(errorPageObj);
if (errorPages.isEmpty()) {
throw new HttpWhiteboardFailureException("'errorPage' expects String, String[] or Collection<String>", DTOConstants.FAILURE_REASON_VALIDATION_FAILED); //$NON-NLS-1$
}
List<String> exceptions = new ArrayList<String>();
Set<Long> errorCodeSet = new LinkedHashSet<Long>();
for(String errorPage : errorPages) {
try {
if ("4xx".equals(errorPage)) { //$NON-NLS-1$
errorPageDTO.errorCodeType = ErrorCodeType.RANGE_4XX;
for (long code = 400; code < 500; code++) {
errorCodeSet.add(code);
}
} else if ("5xx".equals(errorPage)) { //$NON-NLS-1$
errorPageDTO.errorCodeType = ErrorCodeType.RANGE_5XX;
for (long code = 500; code < 600; code++) {
errorCodeSet.add(code);
}
} else if (errorPage.matches("\\d{3}")) { //$NON-NLS-1$
errorPageDTO.errorCodeType = ErrorCodeType.SPECIFIC;
long code = Long.parseLong(errorPage);
errorCodeSet.add(code);
} else {
exceptions.add(errorPage);
}
}
catch (NumberFormatException nfe) {
exceptions.add(errorPage);
}
}
errorPageDTO.errorCodes = new long[errorCodeSet.size()];
int i = 0;
for(Long code : errorCodeSet) {
errorPageDTO.errorCodes[i] = code;
i++;
}
errorPageDTO.exceptions = exceptions.toArray(new String[0]);
errorPageDTO.initParams = ServiceProperties.parseInitParams(
serviceReference, HTTP_WHITEBOARD_SERVLET_INIT_PARAM_PREFIX);
Object servletNameObj = serviceReference.getProperty(HTTP_WHITEBOARD_SERVLET_NAME);
if (servletNameObj == null) {
// ignore
}
else if (String.class.isInstance(servletNameObj)) {
errorPageDTO.name = (String)servletNameObj;
}
else if (validated) {
throw new HttpWhiteboardFailureException("'name' expects String", DTOConstants.FAILURE_REASON_VALIDATION_FAILED); //$NON-NLS-1$
}
errorPageDTO.serviceId = (Long)serviceReference.getProperty(Constants.SERVICE_ID);
errorPageDTO.servletContextId = contextId;
return errorPageDTO;
}
@SuppressWarnings("deprecation")
public static org.eclipse.equinox.http.servlet.dto.ExtendedServletDTO assembleServletDTO(ServiceReference<?> serviceReference, long contextId, boolean validated) {
org.eclipse.equinox.http.servlet.dto.ExtendedServletDTO servletDTO = new org.eclipse.equinox.http.servlet.dto.ExtendedServletDTO();
servletDTO.asyncSupported = false;
Object asyncSupportedObj = serviceReference.getProperty(HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED);
if (asyncSupportedObj == null) {
// ignored
}
else if (Boolean.class.isInstance(asyncSupportedObj)) {
servletDTO.asyncSupported = ((Boolean)asyncSupportedObj).booleanValue();
}
else if (String.class.isInstance(asyncSupportedObj)) {
servletDTO.asyncSupported = Boolean.valueOf((String)asyncSupportedObj);
}
// There is no validation for this scenario, truthiness of any other input is false
servletDTO.initParams = ServiceProperties.parseInitParams(
serviceReference, HTTP_WHITEBOARD_SERVLET_INIT_PARAM_PREFIX);
servletDTO.multipartEnabled = false;
Object multipartEnabledObj = serviceReference.getProperty(HTTP_WHITEBOARD_SERVLET_MULTIPART_ENABLED);
if (multipartEnabledObj == null) {
multipartEnabledObj = serviceReference.getProperty(Const.EQUINOX_HTTP_MULTIPART_ENABLED);
}
if (multipartEnabledObj == null) {
// ignore
}
else if (Boolean.class.isInstance(multipartEnabledObj)) {
servletDTO.multipartEnabled = ((Boolean)multipartEnabledObj).booleanValue();
}
else if (String.class.isInstance(multipartEnabledObj)) {
servletDTO.multipartEnabled = Boolean.valueOf((String)multipartEnabledObj);
}
// There is no validation for this scenario, truthiness of any other input is false
servletDTO.multipartFileSizeThreshold = 0;
servletDTO.multipartLocation = Const.BLANK;
servletDTO.multipartMaxFileSize = -1L;
servletDTO.multipartMaxRequestSize = -1L;
if (servletDTO.multipartEnabled) {
Object multipartFileSizeThresholdObj = serviceReference.getProperty(HTTP_WHITEBOARD_SERVLET_MULTIPART_FILESIZETHRESHOLD);
if (multipartFileSizeThresholdObj == null) {
multipartFileSizeThresholdObj = serviceReference.getProperty(Const.EQUINOX_HTTP_MULTIPART_FILESIZETHRESHOLD);
}
if (multipartFileSizeThresholdObj == null) {
// ignore
}
else if (Integer.class.isInstance(multipartFileSizeThresholdObj)) {
servletDTO.multipartFileSizeThreshold = ((Integer)multipartFileSizeThresholdObj).intValue();
}
else if (validated) {
throw new HttpWhiteboardFailureException("'multipartFileSizeThreshold' expects int or Integer", DTOConstants.FAILURE_REASON_VALIDATION_FAILED); //$NON-NLS-1$
}
Object multipartLocationObj = serviceReference.getProperty(HTTP_WHITEBOARD_SERVLET_MULTIPART_LOCATION);
if (multipartLocationObj == null) {
multipartLocationObj = serviceReference.getProperty(Const.EQUINOX_HTTP_MULTIPART_LOCATION);
}
if (multipartLocationObj == null) {
// ignore
}
else if (String.class.isInstance(multipartLocationObj)) {
servletDTO.multipartLocation = (String)multipartLocationObj;
}
else if (validated) {
throw new HttpWhiteboardFailureException("'multipartLocation' expects String", DTOConstants.FAILURE_REASON_VALIDATION_FAILED); //$NON-NLS-1$
}
Object multipartMaxFileSizeObj = serviceReference.getProperty(HTTP_WHITEBOARD_SERVLET_MULTIPART_MAXFILESIZE);
if (multipartMaxFileSizeObj == null) {
multipartMaxFileSizeObj = serviceReference.getProperty(Const.EQUINOX_HTTP_MULTIPART_MAXFILESIZE);
}
if (multipartMaxFileSizeObj == null) {
// ignore
}
else if (Long.class.isInstance(multipartMaxFileSizeObj)) {
servletDTO.multipartMaxFileSize = ((Long)multipartMaxFileSizeObj).longValue();
}
else if (validated) {
throw new HttpWhiteboardFailureException("'multipartMaxFileSize' expects [L|l]ong", DTOConstants.FAILURE_REASON_VALIDATION_FAILED); //$NON-NLS-1$
}
Object multipartMaxRequestSizeObj = serviceReference.getProperty(HTTP_WHITEBOARD_SERVLET_MULTIPART_MAXREQUESTSIZE);
if (multipartMaxRequestSizeObj == null) {
multipartMaxRequestSizeObj = serviceReference.getProperty(Const.EQUINOX_HTTP_MULTIPART_MAXREQUESTSIZE);
}
if (multipartMaxRequestSizeObj == null) {
// ignore
}
else if (Long.class.isInstance(multipartMaxRequestSizeObj)) {
servletDTO.multipartMaxRequestSize = ((Long)multipartMaxRequestSizeObj).longValue();
}
else if (validated) {
throw new HttpWhiteboardFailureException("'multipartMaxRequestSize' expects [L|l]ong", DTOConstants.FAILURE_REASON_VALIDATION_FAILED); //$NON-NLS-1$
}
}
Object servletNameObj = serviceReference.getProperty(HTTP_WHITEBOARD_SERVLET_NAME);
if (servletNameObj == null) {
// ignore
}
else if (String.class.isInstance(servletNameObj)) {
servletDTO.name = (String)servletNameObj;
}
else if (validated) {
throw new HttpWhiteboardFailureException("'name' expects String", DTOConstants.FAILURE_REASON_VALIDATION_FAILED); //$NON-NLS-1$
}
Object patternObj = serviceReference.getProperty(HTTP_WHITEBOARD_SERVLET_PATTERN);
if (patternObj == null) {
servletDTO.patterns = new String[0];
}
else {
servletDTO.patterns = sort(StringPlus.from(patternObj).toArray(new String[0]));
if (validated && (servletDTO.patterns.length > 0)) {
for (String pattern : servletDTO.patterns) {
checkPattern(pattern);
}
}
}
servletDTO.serviceId = (Long)serviceReference.getProperty(Constants.SERVICE_ID);
servletDTO.servletContextId = contextId;
return servletDTO;
}
public static ErrorPageDTO clone(ErrorPageDTO original) {
ErrorPageDTO clone = new ErrorPageDTO();
clone.asyncSupported = copy(original.asyncSupported);
clone.errorCodes = copy(original.errorCodes);
clone.exceptions = copy(original.exceptions);
clone.initParams = copyStringMap(original.initParams);
clone.name = copy(original.name);
clone.serviceId = copy(original.serviceId);
clone.servletContextId = copy(original.servletContextId);
clone.servletInfo = copy(original.servletInfo);
return clone;
}
public static FailedErrorPageDTO clone(FailedErrorPageDTO original) {
FailedErrorPageDTO clone = new FailedErrorPageDTO();
clone.asyncSupported = copy(original.asyncSupported);
clone.errorCodes = copy(original.errorCodes);
clone.exceptions = copy(original.exceptions);
clone.failureReason = copy(original.failureReason);
clone.initParams = copyStringMap(original.initParams);
clone.name = copy(original.name);
clone.serviceId = copy(original.serviceId);
clone.servletContextId = copy(original.servletContextId);
clone.servletInfo = copy(original.servletInfo);
return clone;
}
public static FailedFilterDTO clone(FailedFilterDTO original) {
FailedFilterDTO clone = new FailedFilterDTO();
clone.asyncSupported = copy(original.asyncSupported);
clone.dispatcher = copy(original.dispatcher);
clone.failureReason = copy(original.failureReason);
clone.initParams = copyStringMap(original.initParams);
clone.name = copy(original.name);
clone.patterns = copy(original.patterns);
clone.regexs = copy(original.regexs);
clone.serviceId = copy(original.serviceId);
clone.servletContextId = copy(original.servletContextId);
clone.servletNames = copy(original.servletNames);
return clone;
}
public static FailedListenerDTO clone(FailedListenerDTO original) {
FailedListenerDTO clone = new FailedListenerDTO();
clone.failureReason = copy(original.failureReason);
clone.serviceId = copy(original.serviceId);
clone.servletContextId = copy(original.servletContextId);
clone.types = copy(original.types);
return clone;
}
public static FailedPreprocessorDTO clone(FailedPreprocessorDTO original) {
FailedPreprocessorDTO clone = new FailedPreprocessorDTO();
clone.failureReason = copy(original.failureReason);
clone.initParams = copyStringMap(original.initParams);
clone.serviceId = copy(original.serviceId);
return clone;
}
public static FailedResourceDTO clone(FailedResourceDTO original) {
FailedResourceDTO clone = new FailedResourceDTO();
clone.failureReason = copy(original.failureReason);
clone.patterns = copy(original.patterns);
clone.prefix = copy(original.prefix);
clone.serviceId = copy(original.serviceId);
clone.servletContextId = copy(original.servletContextId);
return clone;
}
public static FailedServletContextDTO clone(FailedServletContextDTO original) {
FailedServletContextDTO clone = new FailedServletContextDTO();
clone.attributes = copyGenericMap(original.attributes);
clone.contextPath = copy(original.contextPath);
clone.errorPageDTOs = copy(original.errorPageDTOs);
clone.failureReason = copy(original.failureReason);
clone.filterDTOs = copy(original.filterDTOs);
clone.initParams = copyStringMap(original.initParams);
clone.listenerDTOs = copy(original.listenerDTOs);
clone.name = copy(original.name);
clone.resourceDTOs = copy(original.resourceDTOs);
clone.serviceId = copy(original.serviceId);
clone.servletDTOs = copy(original.servletDTOs);
return clone;
}
public static FailedServletDTO clone(FailedServletDTO original) {
FailedServletDTO clone = new FailedServletDTO();
clone.asyncSupported = copy(original.asyncSupported);
clone.failureReason = copy(original.failureReason);
clone.initParams = copyStringMap(clone.initParams);
clone.multipartEnabled = copy(original.multipartEnabled);
clone.multipartFileSizeThreshold = copy(original.multipartFileSizeThreshold);
clone.multipartLocation = copy(original.multipartLocation);
clone.multipartMaxFileSize = copy(original.multipartMaxFileSize);
clone.multipartMaxRequestSize = copy(original.multipartMaxRequestSize);
clone.name = copy(original.name);
clone.patterns = copy(original.patterns);
clone.serviceId = copy(original.serviceId);
clone.servletContextId = copy(original.servletContextId);
clone.servletInfo = copy(original.servletInfo);
return clone;
}
public static FilterDTO clone(FilterDTO original) {
FilterDTO clone = new FilterDTO();
clone.asyncSupported = copy(original.asyncSupported);
clone.dispatcher = copy(original.dispatcher);
clone.initParams = copyStringMap(original.initParams);
clone.name = copy(original.name);
clone.patterns = copy(original.patterns);
clone.regexs = copy(original.regexs);
clone.serviceId = copy(original.serviceId);
clone.servletContextId = copy(original.servletContextId);
clone.servletNames = copy(original.servletNames);
return clone;
}
public static ListenerDTO clone(ListenerDTO original) {
ListenerDTO clone = new ListenerDTO();
clone.serviceId = copy(original.serviceId);
clone.servletContextId = copy(original.servletContextId);
clone.types = copy(original.types);
return clone;
}
public static ResourceDTO clone(ResourceDTO original) {
ResourceDTO clone = new ResourceDTO();
clone.patterns = copy(original.patterns);
clone.prefix = copy(original.prefix);
clone.serviceId = copy(original.serviceId);
clone.servletContextId = copy(original.servletContextId);
return clone;
}
public static ServletDTO clone(ServletDTO original) {
ServletDTO clone = new ServletDTO();
clone.asyncSupported = copy(original.asyncSupported);
clone.initParams = copyStringMap(original.initParams);
clone.multipartEnabled = copy(original.multipartEnabled);
clone.multipartFileSizeThreshold = copy(original.multipartFileSizeThreshold);
clone.multipartLocation = copy(original.multipartLocation);
clone.multipartMaxFileSize = copy(original.multipartMaxFileSize);
clone.multipartMaxRequestSize = copy(original.multipartMaxRequestSize);
clone.name = copy(original.name);
clone.patterns = copy(original.patterns);
clone.serviceId = copy(original.serviceId);
clone.servletContextId = copy(original.servletContextId);
clone.servletInfo = copy(original.servletInfo);
return clone;
}
private static long[] copy(long[] array) {
if (array == null) {
return new long[0];
}
if (array.length == 0) {
return array;
}
return Arrays.copyOf(array, array.length);
}
private static String[] copy(String[] array) {
if (array == null) {
return new String[0];
}
if (array.length == 0) {
return array;
}
return Arrays.copyOf(array, array.length);
}
private static <T> T[] copy(T[] array) {
if (array == null) {
return null;
}
if (array.length == 0) {
return array;
}
return Arrays.copyOf(array, array.length);
}
private static int copy(int value) {
return value;
}
private static long copy(long value) {
return value;
}
private static boolean copy(boolean value) {
return value;
}
private static String copy(String value) {
return value;
}
private static Map<String, String> copyStringMap(Map<String, String> initParams) {
if (initParams == null) {
return Collections.emptyMap();
}
return new HashMap<String, String>(initParams);
}
public static <V> Map<String, Object> copyGenericMap(Map<String, V> value) {
if ((value == null) || value.isEmpty()) {
return Collections.emptyMap();
}
HashMap<String, Object> result = new HashMap<String, Object>();
for (Map.Entry<String, V> entry : value.entrySet()) {
result.put(entry.getKey(), mapValue(entry.getValue()));
}
return result;
}
public static Object mapValue(Object v) {
if ((v == null)
|| v instanceof Number
|| v instanceof Boolean
|| v instanceof Character
|| v instanceof String
|| v instanceof DTO) {
return v;
}
if (v instanceof Map) {
Map<?, ?> m = (Map<?, ?>) v;
Map<Object, Object> map = newMap(m.size());
for (Map.Entry<?, ?> e : m.entrySet()) {
map.put(mapValue(e.getKey()), mapValue(e.getValue()));
}
return map;
}
if (v instanceof List) {
List<?> c = (List<?>) v;
List<Object> list = newList(c.size());
for (Object o : c) {
list.add(mapValue(o));
}
return list;
}
if (v instanceof Set) {
Set<?> c = (Set<?>) v;
Set<Object> set = newSet(c.size());
for (Object o : c) {
set.add(mapValue(o));
}
return set;
}
if (v.getClass().isArray()) {
final int length = Array.getLength(v);
final Class<?> componentType = mapComponentType(v.getClass().getComponentType());
Object array = Array.newInstance(componentType, length);
for (int i = 0; i < length; i++) {
Array.set(array, i, mapValue(Array.get(v, i)));
}
return array;
}
return String.valueOf(v);
}
private static void checkPattern(String pattern) {
if (pattern == null) {
throw new HttpWhiteboardFailureException("Pattern cannot be null", DTOConstants.FAILURE_REASON_VALIDATION_FAILED); //$NON-NLS-1$
}
if (pattern.indexOf("*.") == 0) { //$NON-NLS-1$
return;
}
if (Const.BLANK.equals(pattern)) {
return;
}
if (Const.SLASH.equals(pattern)) {
return;
}
if (!pattern.startsWith(Const.SLASH) ||
(pattern.endsWith(Const.SLASH) && !pattern.equals(Const.SLASH)) ||
pattern.contains("**")) { //$NON-NLS-1$
throw new HttpWhiteboardFailureException(
"Invalid pattern '" + pattern + "'", DTOConstants.FAILURE_REASON_VALIDATION_FAILED); //$NON-NLS-1$ //$NON-NLS-2$
}
}
private static Class<?> mapComponentType(Class<?> componentType) {
if (componentType.isPrimitive()
|| componentType.isArray()
|| Object.class.equals(componentType)
|| Number.class.isAssignableFrom(componentType)
|| Boolean.class.isAssignableFrom(componentType)
|| Character.class.isAssignableFrom(componentType)
|| String.class.isAssignableFrom(componentType)
|| DTO.class.isAssignableFrom(componentType)) {
return componentType;
}
if (Map.class.isAssignableFrom(componentType)) {
return Map.class;
}
if (List.class.isAssignableFrom(componentType)) {
return List.class;
}
if (Set.class.isAssignableFrom(componentType)) {
return Set.class;
}
return String.class;
}
private static <E> List<E> newList(int size) {
return new ArrayList<E>(size);
}
private static <E> Set<E> newSet(int size) {
return new HashSet<E>(size);
}
private static <K, V> Map<K, V> newMap(int size) {
return new HashMap<K, V>(size);
}
private static String[] sort(String[] values) {
if (values == null) {
return null;
}
Arrays.sort(values);
return values;
}
}