| /******************************************************************************* |
| * 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; |
| } |
| |
| } |