/******************************************************************************* * Copyright (c) 2016 Raymond Augé and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Raymond Augé <raymond.auge@liferay.com> - Bug 436698 ******************************************************************************/ package org.eclipse.equinox.http.servlet.internal.context; import java.net.URI; import java.net.URISyntaxException; import java.security.AccessController; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Pattern; import javax.servlet.*; import javax.servlet.Filter; import javax.servlet.http.*; import org.eclipse.equinox.http.servlet.dto.ExtendedServletDTO; import org.eclipse.equinox.http.servlet.internal.HttpServiceRuntimeImpl; import org.eclipse.equinox.http.servlet.internal.customizer.*; import org.eclipse.equinox.http.servlet.internal.error.*; import org.eclipse.equinox.http.servlet.internal.registration.*; import org.eclipse.equinox.http.servlet.internal.registration.FilterRegistration; import org.eclipse.equinox.http.servlet.internal.registration.ServletRegistration; import org.eclipse.equinox.http.servlet.internal.servlet.*; import org.eclipse.equinox.http.servlet.internal.util.*; import org.osgi.framework.*; import org.osgi.service.http.context.ServletContextHelper; import org.osgi.service.http.runtime.dto.*; import org.osgi.service.http.whiteboard.HttpWhiteboardConstants; import org.osgi.util.tracker.ServiceTracker; /** * @author Raymond Augé */ public class ContextController { public static final class ServiceHolder<S> implements Comparable<ServiceHolder<?>> { final ServiceObjects<S> serviceObjects; final S service; final Bundle bundle; final long serviceId; final int serviceRanking; public ServiceHolder(ServiceObjects<S> serviceObjects) { this.serviceObjects = serviceObjects; this.bundle = serviceObjects.getServiceReference().getBundle(); this.service = serviceObjects.getService(); this.serviceId = (Long) serviceObjects.getServiceReference().getProperty(Constants.SERVICE_ID); Integer rankProp = (Integer) serviceObjects.getServiceReference().getProperty(Constants.SERVICE_RANKING); this.serviceRanking = rankProp == null ? 0 : rankProp.intValue(); } public ServiceHolder(S service, Bundle bundle, long serviceId, int serviceRanking) { this.service = service; this.bundle = bundle; this.serviceObjects = null; this.serviceId = serviceId; this.serviceRanking = serviceRanking; } public S get() { return service; } public Bundle getBundle() { return bundle; } public void release() { if (serviceObjects != null && service != null) { try { serviceObjects.ungetService(service); } catch (IllegalStateException e) { // this can happen if the whiteboard bundle is in the process of stopping // and the framework is in the middle of auto-unregistering any services // the bundle forgot to unregister on stop } } } public ServiceReference<S> getServiceReference() { return serviceObjects == null ? null : serviceObjects.getServiceReference(); } @Override public int compareTo(ServiceHolder<?> o) { final int thisRanking = serviceRanking; final int otherRanking = o.serviceRanking; if (thisRanking != otherRanking) { if (thisRanking < otherRanking) { return 1; } return -1; } final long thisId = this.serviceId; final long otherId = o.serviceId; if (thisId == otherId) { return 0; } if (thisId < otherId) { return -1; } return 1; } } public ContextController( BundleContext trackingContextParam, BundleContext consumingContext, ServiceReference<ServletContextHelper> servletContextHelperRef, ProxyContext proxyContext, HttpServiceRuntimeImpl httpServiceRuntime, String contextName, String contextPath) { validate(contextName, contextPath); this.servletContextHelperRef = servletContextHelperRef; long serviceId = (Long)servletContextHelperRef.getProperty(Constants.SERVICE_ID); StringBuilder filterBuilder = new StringBuilder(); filterBuilder.append('('); filterBuilder.append(Constants.SERVICE_ID); filterBuilder.append('='); filterBuilder.append(serviceId); filterBuilder.append(')'); this.servletContextHelperRefFilter = filterBuilder.toString(); this.proxyContext = proxyContext; this.httpServiceRuntime = httpServiceRuntime; this.contextName = contextName; if (contextPath.equals(Const.SLASH)) { contextPath = Const.BLANK; } this.contextPath = contextPath; this.contextServiceId = serviceId; this.initParams = ServiceProperties.parseInitParams( servletContextHelperRef, HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_INIT_PARAM_PREFIX, proxyContext.getServletContext()); this.trackingContext = trackingContextParam; this.consumingContext = consumingContext; listenerServiceTracker = new ServiceTracker<EventListener, AtomicReference<ListenerRegistration>>( trackingContext, httpServiceRuntime.getListenerFilter(), new ContextListenerTrackerCustomizer( trackingContext, httpServiceRuntime, this)); listenerServiceTracker.open(); filterServiceTracker = new ServiceTracker<Filter, AtomicReference<FilterRegistration>>( trackingContext, httpServiceRuntime.getFilterFilter(), new ContextFilterTrackerCustomizer( trackingContext, httpServiceRuntime, this)); filterServiceTracker.open(); servletServiceTracker = new ServiceTracker<Servlet, AtomicReference<ServletRegistration>>( trackingContext, httpServiceRuntime.getServletFilter(), new ContextServletTrackerCustomizer( trackingContext, httpServiceRuntime, this)); servletServiceTracker.open(); resourceServiceTracker = new ServiceTracker<Object, AtomicReference<ResourceRegistration>>( trackingContext, httpServiceRuntime.getResourceFilter(), new ContextResourceTrackerCustomizer( trackingContext, httpServiceRuntime, this)); resourceServiceTracker.open(); } public FilterRegistration addFilterRegistration(ServiceReference<Filter> filterRef) throws ServletException { checkShutdown(); ServiceHolder<Filter> filterHolder = new ServiceHolder<Filter>(consumingContext.getServiceObjects(filterRef)); Filter filter = filterHolder.get(); FilterRegistration registration = null; boolean addedRegisteredObject = false; try { if (filter == null) { throw new IllegalArgumentException("Filter cannot be null"); } addedRegisteredObject = httpServiceRuntime.getRegisteredObjects().add(filter); if (addedRegisteredObject) { registration = doAddFilterRegistration(filterHolder, filterRef); } } finally { if (registration == null) { filterHolder.release(); if (addedRegisteredObject) { httpServiceRuntime.getRegisteredObjects().remove(filter); } } } return registration; } private FilterRegistration doAddFilterRegistration(ServiceHolder<Filter> filterHolder, ServiceReference<Filter> filterRef) throws ServletException { ClassLoader legacyTCCL = (ClassLoader)filterRef.getProperty(Const.EQUINOX_LEGACY_TCCL_PROP); boolean asyncSupported = ServiceProperties.parseBoolean( filterRef, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_ASYNC_SUPPORTED); List<String> dispatcherList = StringPlus.from( filterRef.getProperty( HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_DISPATCHER)); String[] dispatchers = dispatcherList.toArray( new String[dispatcherList.size()]); Long serviceId = (Long)filterRef.getProperty( Constants.SERVICE_ID); if (legacyTCCL != null) { // this is a legacy registration; use a negative id for the DTO serviceId = -serviceId; } Integer filterPriority = (Integer)filterRef.getProperty( Constants.SERVICE_RANKING); if (filterPriority == null) { filterPriority = Integer.valueOf(0); } Map<String, String> filterInitParams = ServiceProperties.parseInitParams( filterRef, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_INIT_PARAM_PREFIX); List<String> patternList = StringPlus.from( filterRef.getProperty( HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN)); String[] patterns = patternList.toArray(new String[patternList.size()]); List<String> regexList = StringPlus.from( filterRef.getProperty( HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_REGEX)); String[] regexs = regexList.toArray(new String[regexList.size()]); List<String> servletList = StringPlus.from( filterRef.getProperty( HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_SERVLET)); String[] servletNames = servletList.toArray(new String[servletList.size()]); String name = ServiceProperties.parseName(filterRef.getProperty( HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_NAME), filterHolder.get()); Filter filter = filterHolder.get(); if (((patterns == null) || (patterns.length == 0)) && ((regexs == null) || (regexs.length == 0)) && ((servletNames == null) || (servletNames.length == 0))) { throw new IllegalArgumentException( "Patterns, regex or servletNames must contain a value."); } if (patterns != null) { for (String pattern : patterns) { checkPattern(pattern); } } if (filter == null) { throw new IllegalArgumentException("Filter cannot be null"); } if (name == null) { name = filter.getClass().getName(); } for (FilterRegistration filterRegistration : filterRegistrations) { if (filterRegistration.getT().equals(filter)) { throw new RegisteredFilterException(filter); } } dispatchers = checkDispatcher(dispatchers); FilterDTO filterDTO = new FilterDTO(); filterDTO.asyncSupported = asyncSupported; filterDTO.dispatcher = sort(dispatchers); filterDTO.initParams = filterInitParams; filterDTO.name = name; filterDTO.patterns = sort(patterns); filterDTO.regexs = regexs; filterDTO.serviceId = serviceId; filterDTO.servletContextId = contextServiceId; filterDTO.servletNames = sort(servletNames); ServletContextHelper curServletContextHelper = getServletContextHelper( filterHolder.getBundle()); ServletContext servletContext = createServletContext( filterHolder.getBundle(), curServletContextHelper); FilterRegistration newRegistration = new FilterRegistration( filterHolder, filterDTO, filterPriority, this, legacyTCCL); FilterConfig filterConfig = new FilterConfigImpl( name, filterInitParams, servletContext); newRegistration.init(filterConfig); filterRegistrations.add(newRegistration); return newRegistration; } public ListenerRegistration addListenerRegistration(ServiceReference<EventListener> listenerRef) throws ServletException { checkShutdown(); ServiceHolder<EventListener> listenerHolder = new ServiceHolder<EventListener>(consumingContext.getServiceObjects(listenerRef)); EventListener listener = listenerHolder.get(); ListenerRegistration registration = null; try { if (listener == null) { throw new IllegalArgumentException("EventListener cannot be null"); } registration = doAddListenerRegistration(listenerHolder, listenerRef); } finally { if (registration == null) { listenerHolder.release(); } } return registration; } private ListenerRegistration doAddListenerRegistration( ServiceHolder<EventListener> listenerHolder, ServiceReference<EventListener> listenerRef) throws ServletException { EventListener eventListener = listenerHolder.get(); List<Class<? extends EventListener>> classes = getListenerClasses(listenerRef); if (classes.isEmpty()) { throw new IllegalArgumentException( "EventListener does not implement a supported type."); } for (ListenerRegistration listenerRegistration : listenerRegistrations) { if (listenerRegistration.getT().equals(eventListener)) { throw new ServletException( "EventListener has already been registered."); } } ListenerDTO listenerDTO = new ListenerDTO(); listenerDTO.serviceId = (Long) listenerRef.getProperty(Constants.SERVICE_ID); listenerDTO.servletContextId = contextServiceId; listenerDTO.types = asStringArray(classes); ServletContextHelper curServletContextHelper = getServletContextHelper( listenerHolder.getBundle()); ServletContext servletContext = createServletContext( listenerHolder.getBundle(), curServletContextHelper); ListenerRegistration listenerRegistration = new ListenerRegistration( listenerHolder, classes, listenerDTO, servletContext, this); if (classes.contains(ServletContextListener.class)) { ServletContextListener servletContextListener = (ServletContextListener)listenerRegistration.getT(); servletContextListener.contextInitialized( new ServletContextEvent(servletContext)); } listenerRegistrations.add(listenerRegistration); eventListeners.put(classes, listenerRegistration); return listenerRegistration; } public ResourceRegistration addResourceRegistration(ServiceReference<?> resourceRef) { checkShutdown(); ClassLoader legacyTCCL = (ClassLoader)resourceRef.getProperty(Const.EQUINOX_LEGACY_TCCL_PROP); Integer rankProp = (Integer) resourceRef.getProperty(Constants.SERVICE_RANKING); int serviceRanking = rankProp == null ? 0 : rankProp.intValue(); List<String> patternList = StringPlus.from( resourceRef.getProperty( HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PATTERN)); String[] patterns = patternList.toArray(new String[patternList.size()]); Long serviceId = (Long)resourceRef.getProperty( Constants.SERVICE_ID); if (legacyTCCL != null) { // this is a legacy registration; use a negative id for the DTO serviceId = -serviceId; } String prefix = (String)resourceRef.getProperty( HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PREFIX); checkPrefix(prefix); if ((patterns == null) || (patterns.length < 1)) { throw new IllegalArgumentException( "Patterns must contain a value."); } for (String pattern : patterns) { checkPattern(pattern); } Bundle bundle = resourceRef.getBundle(); ServletContextHelper curServletContextHelper = getServletContextHelper( bundle); Servlet servlet = new ResourceServlet( prefix, curServletContextHelper, AccessController.getContext()); ResourceDTO resourceDTO = new ResourceDTO(); resourceDTO.patterns = sort(patterns); resourceDTO.prefix = prefix; resourceDTO.serviceId = serviceId; resourceDTO.servletContextId = contextServiceId; ServletContext servletContext = createServletContext( bundle, curServletContextHelper); ResourceRegistration resourceRegistration = new ResourceRegistration( new ServiceHolder<Servlet>(servlet, bundle, serviceId, serviceRanking), resourceDTO, curServletContextHelper, this, legacyTCCL); ServletConfig servletConfig = new ServletConfigImpl( resourceRegistration.getName(), new HashMap<String, String>(), servletContext); try { resourceRegistration.init(servletConfig); } catch (Throwable t) { resourceRegistration.destroy(); return Throw.unchecked(t); } endpointRegistrations.add(resourceRegistration); return resourceRegistration; } public ServletRegistration addServletRegistration(ServiceReference<Servlet> servletRef) throws ServletException { checkShutdown(); ServiceHolder<Servlet> servletHolder = new ServiceHolder<Servlet>(consumingContext.getServiceObjects(servletRef)); Servlet servlet = servletHolder.get(); ServletRegistration registration = null; boolean addedRegisteredObject = false; try { if (servlet == null) { throw new IllegalArgumentException("Servlet cannot be null"); } addedRegisteredObject = httpServiceRuntime.getRegisteredObjects().add(servlet); if (addedRegisteredObject) { registration = doAddServletRegistration(servletHolder, servletRef); } } finally { if (registration == null) { // Always attempt to release here; even though destroy() may have been called // on the registration while failing to add. There are cases where no // ServletRegistration may have even been created at all to call destory() on. // Also, addedRegisteredObject may be false which means we never call doAddServletRegistration servletHolder.release(); if (addedRegisteredObject) { httpServiceRuntime.getRegisteredObjects().remove(servlet); } } } return registration; } private ServletRegistration doAddServletRegistration(ServiceHolder<Servlet> servletHolder, ServiceReference<Servlet> servletRef) throws ServletException { boolean asyncSupported = ServiceProperties.parseBoolean( servletRef, HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED); ClassLoader legacyTCCL = (ClassLoader)servletRef.getProperty(Const.EQUINOX_LEGACY_TCCL_PROP); List<String> errorPageList = StringPlus.from( servletRef.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ERROR_PAGE)); String[] errorPages = errorPageList.toArray(new String[errorPageList.size()]); Map<String, String> servletInitParams = ServiceProperties.parseInitParams( servletRef, HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_INIT_PARAM_PREFIX); List<String> patternList = StringPlus.from( servletRef.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN)); String[] patterns = patternList.toArray(new String[patternList.size()]); Long serviceId = (Long)servletRef.getProperty(Constants.SERVICE_ID); if (legacyTCCL != null) { // this is a legacy registration; use a negative id for the DTO serviceId = -serviceId; } String servletNameFromProperties = (String)servletRef.getProperty( HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME); String generatedServletName = ServiceProperties.parseName( servletRef.getProperty( HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME), servletHolder.get()); boolean multipartEnabled = ServiceProperties.parseBoolean( servletRef, Const.EQUINOX_HTTP_MULTIPART_ENABLED); Integer multipartFileSizeThreshold = (Integer)servletRef.getProperty( Const.EQUINOX_HTTP_MULTIPART_FILESIZETHRESHOLD); String multipartLocation = (String)servletRef.getProperty( Const.EQUINOX_HTTP_MULTIPART_LOCATION); Long multipartMaxFileSize = (Long)servletRef.getProperty( Const.EQUINOX_HTTP_MULTIPART_MAXFILESIZE); Long multipartMaxRequestSize = (Long)servletRef.getProperty( Const.EQUINOX_HTTP_MULTIPART_MAXREQUESTSIZE); if (((patterns == null) || (patterns.length == 0)) && ((errorPages == null) || errorPages.length == 0) && (servletNameFromProperties == null)) { StringBuilder sb = new StringBuilder(); sb.append("One of the service properties "); //$NON-NLS-1$ sb.append(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ERROR_PAGE); sb.append(", "); //$NON-NLS-1$ sb.append(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME); sb.append(", "); //$NON-NLS-1$ sb.append(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN); sb.append(" must contain a value."); //$NON-NLS-1$ throw new IllegalArgumentException(sb.toString()); } if (patterns != null) { for (String pattern : patterns) { checkPattern(pattern); } } ExtendedServletDTO servletDTO = new ExtendedServletDTO(); servletDTO.asyncSupported = asyncSupported; servletDTO.initParams = servletInitParams; servletDTO.multipartEnabled = multipartEnabled; servletDTO.multipartFileSizeThreshold = (multipartFileSizeThreshold != null ? multipartFileSizeThreshold : 0); servletDTO.multipartLocation = (multipartLocation != null ? multipartLocation : Const.BLANK); servletDTO.multipartMaxFileSize = (multipartMaxFileSize != null ? multipartMaxFileSize : -1L); servletDTO.multipartMaxRequestSize = (multipartMaxRequestSize != null ? multipartMaxRequestSize : -1L); servletDTO.name = generatedServletName; servletDTO.patterns = sort(patterns); servletDTO.serviceId = serviceId; servletDTO.servletContextId = contextServiceId; servletDTO.servletInfo = servletHolder.get().getServletInfo(); ErrorPageDTO errorPageDTO = null; if ((errorPages != null) && (errorPages.length > 0)) { errorPageDTO = new ErrorPageDTO(); errorPageDTO.asyncSupported = asyncSupported; List<String> exceptions = new ArrayList<String>(); // Not sure if it is important to maintain order of insertion or natural ordering here. // Using insertion ordering with linked hash set. Set<Long> errorCodeSet = new LinkedHashSet<Long>(); for(String errorPage : errorPages) { try { if ("4xx".equals(errorPage)) { //$NON-NLS-1$ for (long code = 400; code < 500; code++) { errorCodeSet.add(code); } } else if ("5xx".equals(errorPage)) { //$NON-NLS-1$ for (long code = 500; code < 600; code++) { errorCodeSet.add(code); } } else { long code = Long.parseLong(errorPage); errorCodeSet.add(code); } } catch (NumberFormatException nfe) { exceptions.add(errorPage); } } long[] errorCodes = new long[errorCodeSet.size()]; int i = 0; for(Long code : errorCodeSet) { errorCodes[i] = code; i++; } errorPageDTO.errorCodes = errorCodes; errorPageDTO.exceptions = exceptions.toArray(new String[exceptions.size()]); errorPageDTO.initParams = servletInitParams; errorPageDTO.name = generatedServletName; errorPageDTO.serviceId = serviceId; errorPageDTO.servletContextId = contextServiceId; errorPageDTO.servletInfo = servletHolder.get().getServletInfo(); } ServletContextHelper curServletContextHelper = getServletContextHelper( servletHolder.getBundle()); ServletContext servletContext = createServletContext( servletHolder.getBundle(), curServletContextHelper); ServletRegistration servletRegistration = new ServletRegistration( servletHolder, servletDTO, errorPageDTO, curServletContextHelper, this, servletContext, legacyTCCL); ServletConfig servletConfig = new ServletConfigImpl( generatedServletName, servletInitParams, servletContext); try { servletRegistration.init(servletConfig); } catch (Throwable t) { servletRegistration.destroy(); return Throw.unchecked(t); } endpointRegistrations.add(servletRegistration); return servletRegistration; } public void destroy() { flushActiveSessions(); resourceServiceTracker.close(); servletServiceTracker.close(); filterServiceTracker.close(); listenerServiceTracker.close(); endpointRegistrations.clear(); filterRegistrations.clear(); listenerRegistrations.clear(); eventListeners.clear(); proxyContext.destroy(); shutdown = true; } public String getContextName() { checkShutdown(); return contextName; } public String getContextPath() { checkShutdown(); return contextPath; } public DispatchTargets getDispatchTargets( String pathString, RequestInfoDTO requestInfoDTO) { Path path = new Path(pathString); String queryString = path.getQueryString(); String requestURI = path.getRequestURI(); // perfect match DispatchTargets dispatchTargets = getDispatchTargets( requestURI, null, queryString, Match.EXACT, requestInfoDTO); if (dispatchTargets == null) { // extension match dispatchTargets = getDispatchTargets( requestURI, path.getExtension(), queryString, Match.EXTENSION, requestInfoDTO); } if (dispatchTargets == null) { // regex match dispatchTargets = getDispatchTargets( requestURI, null, queryString, Match.REGEX, requestInfoDTO); } if (dispatchTargets == null) { // handle '/' aliases dispatchTargets = getDispatchTargets( requestURI, null, queryString, Match.DEFAULT_SERVLET, requestInfoDTO); } return dispatchTargets; } private DispatchTargets getDispatchTargets( String requestURI, String extension, String queryString, Match match, RequestInfoDTO requestInfoDTO) { int pos = requestURI.lastIndexOf('/'); String servletPath = requestURI; String pathInfo = null; if (match == Match.DEFAULT_SERVLET) { pathInfo = servletPath; servletPath = Const.SLASH; } do { DispatchTargets dispatchTargets = getDispatchTargets( null, requestURI, servletPath, pathInfo, extension, queryString, match, requestInfoDTO); if (dispatchTargets != null) { return dispatchTargets; } if (match == Match.EXACT) { break; } if (pos > -1) { String newServletPath = requestURI.substring(0, pos); pathInfo = requestURI.substring(pos); servletPath = newServletPath; pos = servletPath.lastIndexOf('/'); continue; } break; } while (true); return null; } public DispatchTargets getDispatchTargets( String servletName, String requestURI, String servletPath, String pathInfo, String extension, String queryString, Match match, RequestInfoDTO requestInfoDTO) { checkShutdown(); EndpointRegistration<?> endpointRegistration = null; for (EndpointRegistration<?> curEndpointRegistration : endpointRegistrations) { if (curEndpointRegistration.match(servletName, servletPath, pathInfo, extension, match) != null) { endpointRegistration = curEndpointRegistration; break; } } if (endpointRegistration == null) { return null; } if (match == Match.EXTENSION) { servletPath = servletPath + pathInfo; pathInfo = null; } addEnpointRegistrationsToRequestInfo( endpointRegistration, requestInfoDTO); if (filterRegistrations.isEmpty()) { return new DispatchTargets( this, endpointRegistration, servletName, requestURI, servletPath, pathInfo, queryString); } if (requestURI != null) { int x = requestURI.lastIndexOf('.'); if (x != -1) { extension = requestURI.substring(x + 1); } } List<FilterRegistration> matchingFilterRegistrations = new ArrayList<FilterRegistration>(); collectFilters( matchingFilterRegistrations, endpointRegistration.getName(), requestURI, servletPath, pathInfo, extension); addFilterRegistrationsToRequestInfo( matchingFilterRegistrations, requestInfoDTO); return new DispatchTargets( this, endpointRegistration, matchingFilterRegistrations, servletName, requestURI, servletPath, pathInfo, queryString); } private void collectFilters( List<FilterRegistration> matchingFilterRegistrations, String servletName, String requestURI, String servletPath, String pathInfo, String extension) { for (FilterRegistration filterRegistration : filterRegistrations) { if ((filterRegistration.match( servletName, requestURI, extension, null) != null) && !matchingFilterRegistrations.contains(filterRegistration)) { matchingFilterRegistrations.add(filterRegistration); } } } public Map<String, HttpSessionAdaptor> getActiveSessions() { checkShutdown(); return activeSessions; } public Set<EndpointRegistration<?>> getEndpointRegistrations() { checkShutdown(); return endpointRegistrations; } public EventListeners getEventListeners() { checkShutdown(); return eventListeners; } public Set<FilterRegistration> getFilterRegistrations() { checkShutdown(); return filterRegistrations; } public String getFullContextPath() { List<String> endpoints = httpServiceRuntime.getHttpServiceEndpoints(); if (endpoints.isEmpty()) { return proxyContext.getServletPath().concat(contextPath); } String defaultEndpoint = endpoints.get(0); if ((defaultEndpoint.length() > 0) && defaultEndpoint.endsWith("/")) { defaultEndpoint = defaultEndpoint.substring( 0, defaultEndpoint.length() - 1); } return defaultEndpoint + contextPath; } public HttpServiceRuntimeImpl getHttpServiceRuntime() { checkShutdown(); return httpServiceRuntime; } public Map<String, String> getInitParams() { return initParams; } public Set<ListenerRegistration> getListenerRegistrations() { checkShutdown(); return listenerRegistrations; } public ProxyContext getProxyContext() { checkShutdown(); return proxyContext; } public long getServiceId() { checkShutdown(); return contextServiceId; } public synchronized ServletContextDTO getServletContextDTO(){ checkShutdown(); ServletContextDTO servletContextDTO = new ServletContextDTO(); ServletContext servletContext = getProxyContext().getServletContext(); servletContextDTO.attributes = getDTOAttributes(servletContext); servletContextDTO.contextPath = getContextPath(); servletContextDTO.initParams = new HashMap<String, String>(initParams); servletContextDTO.name = getContextName(); servletContextDTO.serviceId = getServiceId(); collectEndpointDTOs(servletContextDTO); collectFilterDTOs(servletContextDTO); collectListenerDTOs(servletContextDTO); return servletContextDTO; } public boolean matches(ServiceReference<?> whiteBoardService) { String contextSelector = (String) whiteBoardService.getProperty( HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT); // make sure the context helper is either one of the built-in ones registered by this http whiteboard implementation; // or is visible to the whiteboard registering bundle. if (!visibleContextHelper(whiteBoardService)) { return false; } if (contextSelector == null) { contextSelector = httpServiceRuntime.getDefaultContextSelectFilter(whiteBoardService); if (contextSelector == null) { contextSelector = "(" + //$NON-NLS-1$ HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + "=" //$NON-NLS-1$ + HttpWhiteboardConstants.HTTP_WHITEBOARD_DEFAULT_CONTEXT_NAME + ")"; //$NON-NLS-1$ } } if (!contextSelector.startsWith(Const.OPEN_PAREN)) { contextSelector = Const.OPEN_PAREN + HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + Const.EQUAL + contextSelector + Const.CLOSE_PAREN; } org.osgi.framework.Filter targetFilter; try { targetFilter = FrameworkUtil.createFilter(contextSelector); } catch (InvalidSyntaxException ise) { throw new IllegalArgumentException(ise); } return matches(targetFilter); } private boolean visibleContextHelper(ServiceReference<?> whiteBoardService) { if (consumingContext.getBundle().equals(servletContextHelperRef.getBundle())) { return true; } try { if (whiteBoardService.getBundle().getBundleContext().getAllServiceReferences(ServletContextHelper.class.getName(), servletContextHelperRefFilter) != null) { return true; } } catch (InvalidSyntaxException e) { // ignore } return false; } public boolean matches(org.osgi.framework.Filter targetFilter) { return targetFilter.match(servletContextHelperRef); } @Override public String toString() { String value = string; if (value == null) { value = SIMPLE_NAME + '[' + contextName + ", " + trackingContext.getBundle() + ']'; //$NON-NLS-1$ string = value; } return value; } private void addEnpointRegistrationsToRequestInfo( EndpointRegistration<?> endpointRegistration, RequestInfoDTO requestInfoDTO) { if (requestInfoDTO == null) { return; } requestInfoDTO.servletContextId = getServiceId(); if (endpointRegistration instanceof ResourceRegistration) { requestInfoDTO.resourceDTO = (ResourceDTO)endpointRegistration.getD(); } else { requestInfoDTO.servletDTO = (ServletDTO)endpointRegistration.getD(); } } private void addFilterRegistrationsToRequestInfo( List<FilterRegistration> matchedFilterRegistrations, RequestInfoDTO requestInfoDTO) { if (requestInfoDTO == null) { return; } FilterDTO[] filterDTOs = new FilterDTO[matchedFilterRegistrations.size()]; for (int i = 0; i < filterDTOs.length ; i++) { FilterRegistration filterRegistration = matchedFilterRegistrations.get(i); filterDTOs[i] = filterRegistration.getD(); } requestInfoDTO.filterDTOs = filterDTOs; } private String[] asStringArray( List<Class<? extends EventListener>> clazzes) { String[] classesArray = new String[clazzes.size()]; for (int i = 0; i < classesArray.length; i++) { classesArray[i] = clazzes.get(i).getName(); } Arrays.sort(classesArray); return classesArray; } private String[] checkDispatcher(String[] dispatcher) { if ((dispatcher == null) || (dispatcher.length == 0)) { return DISPATCHER; } for (String type : dispatcher) { try { DispatcherType.valueOf(type); } catch (IllegalArgumentException iae) { throw new IllegalArgumentException( "Invalid dispatcher '" + type + "'", iae); } } Arrays.sort(dispatcher); return dispatcher; } public static void checkPattern(String pattern) { if (pattern == null) { throw new IllegalArgumentException("Pattern cannot be null"); } if (pattern.indexOf("*.") == 0) { //$NON-NLS-1$ return; } if (!pattern.startsWith(Const.SLASH) || (pattern.endsWith(Const.SLASH) && !pattern.equals(Const.SLASH))) { throw new IllegalArgumentException( "Invalid pattern '" + pattern + "'"); } } private void checkPrefix(String prefix) { if (prefix == null) { throw new IllegalArgumentException("Prefix cannot be null"); } if (prefix.endsWith(Const.SLASH) && !prefix.equals(Const.SLASH)) { throw new IllegalArgumentException("Invalid prefix '" + prefix + "'"); } } private void checkShutdown() { if (shutdown) { throw new IllegalStateException( "Context is already shutdown"); //$NON-NLS-1$ } } private ServletContext createServletContext( Bundle curBundle, ServletContextHelper curServletContextHelper) { ServletContextAdaptor adaptor = new ServletContextAdaptor( this, curBundle, curServletContextHelper, eventListeners, AccessController.getContext()); return adaptor.createServletContext(); } private void collectEndpointDTOs( ServletContextDTO servletContextDTO) { List<ErrorPageDTO> errorPageDTOs = new ArrayList<ErrorPageDTO>(); List<ResourceDTO> resourceDTOs = new ArrayList<ResourceDTO>(); List<ServletDTO> servletDTOs = new ArrayList<ServletDTO>(); for (EndpointRegistration<?> endpointRegistration : endpointRegistrations) { if (endpointRegistration instanceof ResourceRegistration) { resourceDTOs.add(DTOUtil.clone((ResourceDTO)endpointRegistration.getD())); } else { ServletRegistration servletRegistration = (ServletRegistration)endpointRegistration; servletDTOs.add(DTOUtil.clone(servletRegistration.getD())); ErrorPageDTO errorPageDTO = servletRegistration.getErrorPageDTO(); if (errorPageDTO != null) { errorPageDTOs.add(DTOUtil.clone(errorPageDTO)); } } } servletContextDTO.errorPageDTOs = errorPageDTOs.toArray( new ErrorPageDTO[errorPageDTOs.size()]); servletContextDTO.resourceDTOs = resourceDTOs.toArray( new ResourceDTO[resourceDTOs.size()]); servletContextDTO.servletDTOs = servletDTOs.toArray( new ServletDTO[servletDTOs.size()]); } private void collectFilterDTOs( ServletContextDTO servletContextDTO) { List<FilterDTO> filterDTOs = new ArrayList<FilterDTO>(); for (FilterRegistration filterRegistration : filterRegistrations) { filterDTOs.add(DTOUtil.clone(filterRegistration.getD())); } servletContextDTO.filterDTOs = filterDTOs.toArray( new FilterDTO[filterDTOs.size()]); } private void collectListenerDTOs( ServletContextDTO servletContextDTO) { List<ListenerDTO> listenerDTOs = new ArrayList<ListenerDTO>(); for (ListenerRegistration listenerRegistration : listenerRegistrations) { listenerDTOs.add(DTOUtil.clone(listenerRegistration.getD())); } servletContextDTO.listenerDTOs = listenerDTOs.toArray( new ListenerDTO[listenerDTOs.size()]); } private Map<String, Object> getDTOAttributes(ServletContext servletContext) { Map<String, Object> map = new HashMap<String, Object>(); for (Enumeration<String> names = servletContext.getAttributeNames(); names.hasMoreElements();) { String name = names.nextElement(); map.put(name, DTOUtil.mapValue(servletContext.getAttribute(name))); } return Collections.unmodifiableMap(map); } private List<Class<? extends EventListener>> getListenerClasses( ServiceReference<EventListener> serviceReference) { List<String> objectClassList = StringPlus.from(serviceReference.getProperty(Constants.OBJECTCLASS)); List<Class<? extends EventListener>> classes = new ArrayList<Class<? extends EventListener>>(); if (objectClassList.contains(ServletContextListener.class.getName())) { classes.add(ServletContextListener.class); } if (objectClassList.contains(ServletContextAttributeListener.class.getName())) { classes.add(ServletContextAttributeListener.class); } if (objectClassList.contains(ServletRequestListener.class.getName())) { classes.add(ServletRequestListener.class); } if (objectClassList.contains(ServletRequestAttributeListener.class.getName())) { classes.add(ServletRequestAttributeListener.class); } if (objectClassList.contains(HttpSessionListener.class.getName())) { classes.add(HttpSessionListener.class); } if (objectClassList.contains(HttpSessionAttributeListener.class.getName())) { classes.add(HttpSessionAttributeListener.class); } ServletContext servletContext = proxyContext.getServletContext(); if ((servletContext.getMajorVersion() >= 3) && (servletContext.getMinorVersion() > 0)) { if (objectClassList.contains(javax.servlet.http.HttpSessionIdListener.class.getName())) { classes.add(javax.servlet.http.HttpSessionIdListener.class); } } return classes; } private ServletContextHelper getServletContextHelper(Bundle curBundle) { BundleContext context = curBundle.getBundleContext(); return context.getService(servletContextHelperRef); } public void ungetServletContextHelper(Bundle curBundle) { BundleContext context = curBundle.getBundleContext(); try { context.ungetService(servletContextHelperRef); } catch (IllegalStateException e) { // this can happen if the whiteboard bundle is in the process of stopping // and the framework is in the middle of auto-unregistering any services // the bundle forgot to unregister on stop } } private String[] sort(String[] values) { if (values == null) { return null; } Arrays.sort(values); return values; } private void flushActiveSessions() { Collection<HttpSessionAdaptor> httpSessionAdaptors = activeSessions.values(); Iterator<HttpSessionAdaptor> iterator = httpSessionAdaptors.iterator(); while (iterator.hasNext()) { HttpSessionAdaptor httpSessionAdaptor = iterator.next(); httpSessionAdaptor.invalidate(); iterator.remove(); } } public void removeActiveSession(HttpSession session) { activeSessions.remove(session.getId()); } public void fireSessionIdChanged(String oldSessionId) { ServletContext servletContext = proxyContext.getServletContext(); if ((servletContext.getMajorVersion() <= 3) && (servletContext.getMinorVersion() < 1)) { return; } List<javax.servlet.http.HttpSessionIdListener> listeners = eventListeners.get(javax.servlet.http.HttpSessionIdListener.class); if (listeners.isEmpty()) { return; } for (HttpSessionAdaptor httpSessionAdaptor : activeSessions.values()) { HttpSessionEvent httpSessionEvent = new HttpSessionEvent(httpSessionAdaptor); for (javax.servlet.http.HttpSessionIdListener listener : listeners) { listener.sessionIdChanged(httpSessionEvent, oldSessionId); } } } public HttpSessionAdaptor getSessionAdaptor( HttpSession session, ServletContext servletContext) { String sessionId = session.getId(); HttpSessionAdaptor httpSessionAdaptor = activeSessions.get(sessionId); if (httpSessionAdaptor != null) { return httpSessionAdaptor; } httpSessionAdaptor = HttpSessionAdaptor.createHttpSessionAdaptor( session, servletContext, this); HttpSessionAdaptor previousHttpSessionAdaptor = activeSessions.putIfAbsent(sessionId, httpSessionAdaptor); if (previousHttpSessionAdaptor != null) { return previousHttpSessionAdaptor; } List<HttpSessionListener> listeners = eventListeners.get(HttpSessionListener.class); if (listeners.isEmpty()) { return httpSessionAdaptor; } HttpSessionEvent httpSessionEvent = new HttpSessionEvent( httpSessionAdaptor); for (HttpSessionListener listener : listeners) { listener.sessionCreated(httpSessionEvent); } return httpSessionAdaptor; } private void validate(String preValidationContextName, String preValidationContextPath) { if (!contextNamePattern.matcher(preValidationContextName).matches()) { throw new IllegalContextNameException( "The context name '" + preValidationContextName + "' does not follow Bundle-SymbolicName syntax.", //$NON-NLS-1$ //$NON-NLS-2$ DTOConstants.FAILURE_REASON_VALIDATION_FAILED); } try { @SuppressWarnings("unused") URI uri = new URI(Const.HTTP, Const.LOCALHOST, preValidationContextPath, null); } catch (URISyntaxException use) { throw new IllegalContextPathException( "The context path '" + preValidationContextPath + "' is not valid URI path syntax.", //$NON-NLS-1$ //$NON-NLS-2$ DTOConstants.FAILURE_REASON_VALIDATION_FAILED); } } private static final String[] DISPATCHER = new String[] {DispatcherType.REQUEST.toString()}; private static final String SIMPLE_NAME = ContextController.class.getSimpleName(); private static final Pattern contextNamePattern = Pattern.compile("^([a-zA-Z_0-9\\-]+\\.)*[a-zA-Z_0-9\\-]+$"); //$NON-NLS-1$ private final Map<String, String> initParams; private final BundleContext trackingContext; private final BundleContext consumingContext; private final String contextName; private final String contextPath; private final long contextServiceId; private final Set<EndpointRegistration<?>> endpointRegistrations = new ConcurrentSkipListSet<EndpointRegistration<?>>(); private final EventListeners eventListeners = new EventListeners(); private final Set<FilterRegistration> filterRegistrations = new ConcurrentSkipListSet<FilterRegistration>(); private final ConcurrentMap<String, HttpSessionAdaptor> activeSessions = new ConcurrentHashMap<String, HttpSessionAdaptor>(); private final HttpServiceRuntimeImpl httpServiceRuntime; private final Set<ListenerRegistration> listenerRegistrations = new HashSet<ListenerRegistration>(); private final ProxyContext proxyContext; private final ServiceReference<ServletContextHelper> servletContextHelperRef; private final String servletContextHelperRefFilter; private boolean shutdown; private String string; private final ServiceTracker<Filter, AtomicReference<FilterRegistration>> filterServiceTracker; private final ServiceTracker<EventListener, AtomicReference<ListenerRegistration>> listenerServiceTracker; private final ServiceTracker<Servlet, AtomicReference<ServletRegistration>> servletServiceTracker; private final ServiceTracker<Object, AtomicReference<ResourceRegistration>> resourceServiceTracker; }