blob: ecd7b2c125e71b8267a29a7a32af843e27998513 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2014, 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;
import static org.osgi.service.http.runtime.HttpServiceRuntimeConstants.HTTP_SERVICE_ENDPOINT;
import static org.osgi.service.http.whiteboard.HttpWhiteboardConstants.*;
import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.*;
import javax.servlet.Filter;
import javax.servlet.http.*;
import org.eclipse.equinox.http.servlet.context.ContextPathCustomizer;
import org.eclipse.equinox.http.servlet.internal.context.*;
import org.eclipse.equinox.http.servlet.internal.dto.ExtendedErrorPageDTO;
import org.eclipse.equinox.http.servlet.internal.dto.ExtendedFailedServletContextDTO;
import org.eclipse.equinox.http.servlet.internal.error.*;
import org.eclipse.equinox.http.servlet.internal.registration.PreprocessorRegistration;
import org.eclipse.equinox.http.servlet.internal.servlet.HttpSessionTracker;
import org.eclipse.equinox.http.servlet.internal.servlet.Match;
import org.eclipse.equinox.http.servlet.internal.util.*;
import org.eclipse.equinox.http.servlet.session.HttpSessionInvalidator;
import org.osgi.framework.*;
import org.osgi.framework.dto.ServiceReferenceDTO;
import org.osgi.service.http.HttpContext;
import org.osgi.service.http.NamespaceException;
import org.osgi.service.http.context.ServletContextHelper;
import org.osgi.service.http.runtime.HttpServiceRuntime;
import org.osgi.service.http.runtime.dto.*;
import org.osgi.service.http.whiteboard.Preprocessor;
import org.osgi.service.log.Logger;
import org.osgi.service.log.LoggerFactory;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
/**
* @author Raymond Augé
*/
public class HttpServiceRuntimeImpl
implements
HttpServiceRuntime,
ServiceTrackerCustomizer<ServletContextHelper, AtomicReference<ContextController>> {
@SuppressWarnings("unchecked")
public HttpServiceRuntimeImpl(
BundleContext trackingContext, BundleContext consumingContext,
ServletContext parentServletContext, Dictionary<String, Object> attributes) {
this.trackingContext = trackingContext;
this.consumingContext = consumingContext;
this.errorPageServiceFilter = createErrorPageFilter(consumingContext);
this.servletServiceFilter = createServletFilter(consumingContext);
this.resourceServiceFilter = createResourceFilter(consumingContext);
this.filterServiceFilter = createFilterFilter(consumingContext);
this.listenerServiceFilter = createListenerFilter(consumingContext);
this.parentServletContext = parentServletContext;
this.attributes = new UMDictionaryMap<String, Object>(attributes);
this.targetFilter = "(" + Activator.UNIQUE_SERVICE_ID + "=" + this.attributes.get(Activator.UNIQUE_SERVICE_ID) + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
this.httpSessionTracker = new HttpSessionTracker(this);
this.invalidatorReg = trackingContext.registerService(HttpSessionInvalidator.class, this.httpSessionTracker, attributes);
loggerFactoryTracker = new ServiceTracker<>(consumingContext, LoggerFactory.class, new ServiceTrackerCustomizer<LoggerFactory, Logger>() {
@Override
public Logger addingService(ServiceReference<LoggerFactory> reference) {
return getConsumingContext().getService(reference).getLogger(HttpServiceRuntimeImpl.class);
}
@Override
public void modifiedService(ServiceReference<LoggerFactory> reference, Logger service) {
// ignore
}
@Override
public void removedService(ServiceReference<LoggerFactory> reference, Logger service) {
// ignore
}
});
loggerFactoryTracker.open();
contextServiceTracker =
new ServiceTracker<ServletContextHelper, AtomicReference<ContextController>>(
trackingContext, ServletContextHelper.class, this);
preprocessorServiceTracker =
new ServiceTracker<Preprocessor, AtomicReference<PreprocessorRegistration>>(
trackingContext, Preprocessor.class, new PreprocessorCustomizer(this));
contextPathCustomizerHolder = new ContextPathCustomizerHolder(consumingContext, contextServiceTracker);
contextPathAdaptorTracker = new ServiceTracker<ContextPathCustomizer, ContextPathCustomizer>(
consumingContext, ContextPathCustomizer.class, contextPathCustomizerHolder);
Hashtable<String, Object> defaultContextProps = new Hashtable<String, Object>();
defaultContextProps.put(HTTP_WHITEBOARD_CONTEXT_NAME, HTTP_WHITEBOARD_DEFAULT_CONTEXT_NAME);
defaultContextProps.put(Constants.SERVICE_RANKING, Integer.MIN_VALUE);
defaultContextProps.put(HTTP_WHITEBOARD_CONTEXT_PATH, Const.SLASH);
defaultContextProps.put(HTTP_WHITEBOARD_TARGET, this.targetFilter);
defaultContextProps.put(HTTP_SERVICE_CONTEXT_PROPERTY, HTTP_WHITEBOARD_DEFAULT_CONTEXT_NAME);
defaultContextReg = (ServiceRegistration<DefaultServletContextHelper>) consumingContext.registerService(
new String [] {ServletContextHelper.class.getName(), DefaultServletContextHelper.class.getName()}, new DefaultServletContextHelperFactory(), defaultContextProps);
}
public synchronized void open() {
contextPathAdaptorTracker.open();
contextServiceTracker.open();
preprocessorServiceTracker.open();
}
@Override
public synchronized AtomicReference<ContextController> addingService(
ServiceReference<ServletContextHelper> serviceReference) {
AtomicReference<ContextController> result = new AtomicReference<ContextController>();
if (!matches(serviceReference)) {
return result;
}
try {
ContextController contextController = new ContextController(
trackingContext, consumingContext, serviceReference, parentServletContext, this);
controllerMap.put(serviceReference, contextController);
result.set(contextController);
}
catch (HttpWhiteboardFailureException hwfe) {
debug(hwfe.getMessage(), hwfe);
recordFailedServletContextDTO(serviceReference, 0, hwfe.getFailureReason());
}
catch (Throwable t) {
error(t.getMessage(), t);
recordFailedServletContextDTO(serviceReference, 0, DTOConstants.FAILURE_REASON_EXCEPTION_ON_INIT);
}
finally {
incrementServiceChangecount();
}
return result;
}
public String adaptContextPath(String contextPath, ServiceReference<ServletContextHelper> helper) {
ContextPathCustomizer pathAdaptor = contextPathCustomizerHolder.getHighestRanked();
if (pathAdaptor != null) {
String contextPrefix = pathAdaptor.getContextPathPrefix(helper);
if (contextPrefix != null && !contextPrefix.isEmpty() && !contextPrefix.equals(Const.SLASH)) {
if (!contextPrefix.startsWith(Const.SLASH)) {
contextPrefix = Const.SLASH + contextPrefix;
}
// make sure we do not append SLASH context path here
if (contextPath == null || contextPath.equals(Const.SLASH)) {
contextPath = Const.BLANK;
}
return contextPrefix + contextPath;
}
}
return contextPath;
}
public BundleContext getConsumingContext() {
return consumingContext;
}
public String getDefaultContextSelectFilter(ServiceReference<?> httpWhiteBoardService) {
ContextPathCustomizer pathAdaptor = contextPathCustomizerHolder.getHighestRanked();
if (pathAdaptor != null) {
return pathAdaptor.getDefaultContextSelectFilter(httpWhiteBoardService);
}
return null;
}
public boolean isDefaultContext(ContextController contextController) {
ServiceReference<?> thisReference = defaultContextReg.getReference();
ServiceReference<ServletContextHelper> contextReference = contextController.getServiceReference();
if (thisReference == null) throw new NullPointerException("Default Context Service reference is null. " + this); //$NON-NLS-1$
if (contextReference == null) throw new NullPointerException("Context Service reference is null. " + contextController); //$NON-NLS-1$
return thisReference.equals(contextReference);
}
public boolean isFailedResourceDTO(ServiceReference<?> serviceReference) {
return failedResourceDTOs.containsKey(serviceReference);
}
public boolean isFailedServletDTO(ServiceReference<?> serviceReference) {
return failedServletDTOs.containsKey(serviceReference);
}
public boolean isFailedErrorPageDTO(ServiceReference<?> serviceReference) {
return failedErrorPageDTOs.containsKey(serviceReference);
}
@Override
public synchronized RequestInfoDTO calculateRequestInfoDTO(String path) {
RequestInfoDTO requestInfoDTO = new RequestInfoDTO();
requestInfoDTO.path = path;
try {
getDispatchTargets(path, requestInfoDTO);
}
catch (Exception e) {
throw new RuntimeException(e);
}
return requestInfoDTO;
}
public synchronized void destroy() {
invalidatorReg.unregister();
try {
defaultContextReg.unregister();
}
catch (IllegalStateException ise) {
// ignore
}
contextServiceTracker.close();
contextPathAdaptorTracker.close();
preprocessorServiceTracker.close();
controllerMap.clear();
preprocessorMap.clear();
registeredObjects.clear();
legacyContextMap.clear();
failedErrorPageDTOs.clear();
failedFilterDTOs.clear();
failedListenerDTOs.clear();
failedPreprocessorDTOs.clear();
failedResourceDTOs.clear();
failedServletContextDTOs.clear();
failedServletDTOs.clear();
httpSessionTracker.clear();
registeredObjects.clear();
scheduledExecutor.shutdown();
loggerFactoryTracker.close();
}
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 with servlet mapped to '/'
// the servletpath is the requestURI minus the contextpath and the pathinfo is null
dispatchTargets = getDispatchTargets(
requestURI, null, queryString, Match.DEFAULT_SERVLET,
requestInfoDTO);
}
if (dispatchTargets == null && Const.SLASH.equals(pathString)) {
// handle with servlet mapped to '' (empty string)
// the pathinfo is '/' and the servletpath and contextpath are the empty string ("")
dispatchTargets = getDispatchTargets(
requestURI, null, queryString, Match.CONTEXT_ROOT,
requestInfoDTO);
}
return dispatchTargets;
}
public HttpSessionTracker getHttpSessionTracker() {
return httpSessionTracker;
}
public Set<Object> getRegisteredObjects() {
return registeredObjects;
}
public String getTargetFilter() {
return targetFilter;
}
public ServletContext getParentServletContext() {
return parentServletContext;
}
public List<String> getHttpServiceEndpoints() {
return StringPlus.from(
attributes.get(HTTP_SERVICE_ENDPOINT));
}
@Override
public synchronized RuntimeDTO getRuntimeDTO() {
RuntimeDTO runtimeDTO = new RuntimeDTO();
runtimeDTO.failedErrorPageDTOs = getFailedErrorPageDTOs();
runtimeDTO.failedFilterDTOs = getFailedFilterDTOs();
runtimeDTO.failedListenerDTOs = getFailedListenerDTOs();
runtimeDTO.failedPreprocessorDTOs = getFailedPreprocessorDTOs();
runtimeDTO.failedResourceDTOs = getFailedResourceDTOs();
runtimeDTO.failedServletContextDTOs = getFailedServletContextDTO();
runtimeDTO.failedServletDTOs = getFailedServletDTOs();
runtimeDTO.preprocessorDTOs = getPreprocessorDTOs();
runtimeDTO.serviceDTO = getServiceDTO();
runtimeDTO.servletContextDTOs = getServletContextDTOs();
return runtimeDTO;
}
private FailedErrorPageDTO[] getFailedErrorPageDTOs() {
Collection<FailedErrorPageDTO> fepDTOs = failedErrorPageDTOs.values();
List<FailedErrorPageDTO> copies = new ArrayList<FailedErrorPageDTO>();
for (FailedErrorPageDTO failedErrorPageDTO : fepDTOs) {
copies.add(DTOUtil.clone(failedErrorPageDTO));
}
return copies.toArray(new FailedErrorPageDTO[0]);
}
private ServiceReferenceDTO getServiceDTO() {
ServiceReferenceDTO[] services = consumingContext.getBundle().adapt(ServiceReferenceDTO[].class);
for (ServiceReferenceDTO serviceDTO : services) {
String[] serviceTypes = (String[]) serviceDTO.properties.get(Constants.OBJECTCLASS);
for (String type : serviceTypes) {
if (HttpServiceRuntime.class.getName().equals(type)) {
return serviceDTO;
}
}
}
return null;
}
public void debug(String message) {
Logger logger = loggerFactoryTracker.getService();
if (logger == null) {
parentServletContext.log(String.valueOf(message));
}
else {
logger.debug(String.valueOf(message));
}
}
public void debug(String message, Throwable t) {
Logger logger = loggerFactoryTracker.getService();
if (logger == null) {
parentServletContext.log(String.valueOf(message), t);
}
else {
logger.debug(String.valueOf(message), t);
}
}
public void error(String message, Throwable t) {
Logger logger = loggerFactoryTracker.getService();
if (logger == null) {
parentServletContext.log(String.valueOf(message), t);
}
else {
logger.error(String.valueOf(message), t);
}
}
public boolean matches(ServiceReference<?> serviceReference) {
String target = (String)serviceReference.getProperty(HTTP_WHITEBOARD_TARGET);
if (target == null) {
return true;
}
org.osgi.framework.Filter whiteboardTargetFilter;
try {
whiteboardTargetFilter = FrameworkUtil.createFilter(target);
}
catch (InvalidSyntaxException ise) {
throw new IllegalArgumentException(ise);
}
if (whiteboardTargetFilter.matches(attributes)) {
return true;
}
return false;
}
public boolean matchesAnyContext(ServiceReference<?> serviceReference) {
for (ContextController contextController : controllerMap.values()) {
if (contextController.matches(serviceReference)) {
return true;
}
}
return false;
}
@Override
public synchronized void modifiedService(
ServiceReference<ServletContextHelper> serviceReference,
AtomicReference<ContextController> contextController) {
removedService(serviceReference, contextController);
AtomicReference<ContextController> added = addingService(serviceReference);
contextController.set(added.get());
}
@Override
public synchronized void removedService(
ServiceReference<ServletContextHelper> serviceReference,
AtomicReference<ContextController> contextControllerRef) {
try {
ContextController contextController = contextControllerRef.get();
if (contextController != null) {
Iterator<Entry<ServiceReference<ServletContextHelper>, ExtendedFailedServletContextDTO>> iterator = failedServletContextDTOs.entrySet().iterator();
while (iterator.hasNext()) {
if (iterator.next().getValue().shadowingServiceId == contextController.getServiceId()) {
iterator.remove();
}
}
contextController.destroy();
}
failedServletContextDTOs.remove(serviceReference);
controllerMap.remove(serviceReference);
trackingContext.ungetService(serviceReference);
}
finally {
incrementServiceChangecount();
}
}
Collection<ContextController> getContextControllers(String requestURI) {
int pos = requestURI.lastIndexOf('/');
do {
List<ContextController> contextControllers = new ArrayList<ContextController>();
for (ContextController contextController : controllerMap.values()) {
if (contextController.getContextPath().equals(requestURI)) {
contextControllers.add(contextController);
}
}
if (!contextControllers.isEmpty()) {
return contextControllers;
}
if (pos > -1) {
requestURI = requestURI.substring(0, pos);
pos = requestURI.lastIndexOf('/');
continue;
}
break;
}
while (true);
return null;
}
public Collection<ContextController> getContextControllers() {
return controllerMap.values();
}
public DispatchTargets getDispatchTargets(
String requestURI, String extension, String queryString, Match match,
RequestInfoDTO requestInfoDTO) {
Collection<ContextController> contextControllers = getContextControllers(
requestURI);
if ((contextControllers == null) || contextControllers.isEmpty()) {
return null;
}
String contextPath =
contextControllers.iterator().next().getContextPath();
requestURI = requestURI.substring(contextPath.length());
int pos = requestURI.lastIndexOf('/');
String servletPath = requestURI;
String pathInfo = null;
if (match == Match.CONTEXT_ROOT) {
pathInfo = Const.SLASH;
servletPath = Const.BLANK;
}
do {
for (ContextController contextController : contextControllers) {
DispatchTargets dispatchTargets =
contextController.getDispatchTargets(
null, requestURI, servletPath, pathInfo,
extension, queryString, match, requestInfoDTO);
if (dispatchTargets != null) {
return dispatchTargets;
}
}
if ((match == Match.EXACT) || (match == Match.CONTEXT_ROOT) || (match == Match.DEFAULT_SERVLET)) {
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;
}
private FailedFilterDTO[] getFailedFilterDTOs() {
Collection<FailedFilterDTO> ffDTOs = failedFilterDTOs.values();
List<FailedFilterDTO> copies = new ArrayList<FailedFilterDTO>();
for (FailedFilterDTO failedFilterDTO : ffDTOs) {
copies.add(DTOUtil.clone(failedFilterDTO));
}
return copies.toArray(new FailedFilterDTO[0]);
}
private FailedListenerDTO[] getFailedListenerDTOs() {
Collection<FailedListenerDTO> flDTOs = failedListenerDTOs.values();
List<FailedListenerDTO> copies = new ArrayList<FailedListenerDTO>();
for (FailedListenerDTO failedListenerDTO : flDTOs) {
copies.add(DTOUtil.clone(failedListenerDTO));
}
return copies.toArray(new FailedListenerDTO[0]);
}
private FailedResourceDTO[] getFailedResourceDTOs() {
Collection<FailedResourceDTO> frDTOs = failedResourceDTOs.values();
List<FailedResourceDTO> copies = new ArrayList<FailedResourceDTO>();
for (FailedResourceDTO failedResourceDTO : frDTOs) {
copies.add(DTOUtil.clone(failedResourceDTO));
}
return copies.toArray(new FailedResourceDTO[0]);
}
private FailedServletContextDTO[] getFailedServletContextDTO() {
Collection<ExtendedFailedServletContextDTO> fscDTOs = failedServletContextDTOs.values();
List<FailedServletContextDTO> copies = new ArrayList<FailedServletContextDTO>();
for (FailedServletContextDTO failedServletContextDTO : fscDTOs) {
copies.add(DTOUtil.clone(failedServletContextDTO));
}
return copies.toArray(new FailedServletContextDTO[0]);
}
private FailedServletDTO[] getFailedServletDTOs() {
Collection<FailedServletDTO> fsDTOs = failedServletDTOs.values();
List<FailedServletDTO> copies = new ArrayList<FailedServletDTO>();
for (FailedServletDTO failedServletDTO : fsDTOs) {
copies.add(DTOUtil.clone(failedServletDTO));
}
return copies.toArray(new FailedServletDTO[0]);
}
private FailedPreprocessorDTO[] getFailedPreprocessorDTOs() {
Collection<FailedPreprocessorDTO> fpDTOs = failedPreprocessorDTOs.values();
List<FailedPreprocessorDTO> copies = new ArrayList<FailedPreprocessorDTO>();
for (FailedPreprocessorDTO failedPreprocessorDTO : fpDTOs) {
copies.add(DTOUtil.clone(failedPreprocessorDTO));
}
return copies.toArray(new FailedPreprocessorDTO[0]);
}
public ServletContextDTO[] getServletContextDTOs() {
List<ServletContextDTO> servletContextDTOs = new ArrayList<ServletContextDTO>();
for (ContextController contextController : controllerMap.values()) {
servletContextDTOs.add(contextController.getServletContextDTO());
}
return servletContextDTOs.toArray(new ServletContextDTO[0]);
}
public PreprocessorDTO[] getPreprocessorDTOs() {
List<PreprocessorDTO> pDTOs = new ArrayList<PreprocessorDTO>();
for (PreprocessorRegistration registration : preprocessorMap.values()) {
pDTOs.add(registration.getD());
}
return pDTOs.toArray(new PreprocessorDTO[0]);
}
public Map<ServiceReference<Preprocessor>, PreprocessorRegistration> getPreprocessorRegistrations() {
return preprocessorMap;
}
public void registerHttpServiceFilter(
Bundle bundle, String alias, Filter filter, Dictionary<String, String> initparams, HttpContextHolder httpContextHolder) {
if (alias == null) {
throw new IllegalArgumentException("Alias cannot be null"); //$NON-NLS-1$
}
if (filter == null) {
throw new IllegalArgumentException("Filter cannot be null"); //$NON-NLS-1$
}
ContextController.checkPattern(alias);
// need to make sure exact matching aliases are converted to wildcard pattern matches
if (!alias.endsWith(Const.SLASH_STAR) && !alias.startsWith(Const.STAR_DOT) && !alias.contains(Const.SLASH_STAR_DOT)) {
if (alias.endsWith(Const.SLASH)) {
alias = alias + '*';
} else {
alias = alias + Const.SLASH_STAR;
}
}
synchronized (legacyMappings) {
if (getRegisteredObjects().contains(filter)) {
throw new RegisteredFilterException(filter);
}
HttpServiceObjectRegistration existing = legacyMappings.get(filter);
if (existing != null) {
throw new RegisteredFilterException(filter);
}
String filterName = filter.getClass().getName();
if ((initparams != null) && (initparams.get(Const.FILTER_NAME) != null)) {
filterName = initparams.get(Const.FILTER_NAME);
}
HttpServiceObjectRegistration objectRegistration = null;
ServiceRegistration<Filter> registration = null;
try {
Dictionary<String, Object> props = new Hashtable<String, Object>();
props.put(HTTP_WHITEBOARD_TARGET, targetFilter);
props.put(HTTP_WHITEBOARD_FILTER_PATTERN, alias);
props.put(HTTP_WHITEBOARD_FILTER_NAME, filterName);
props.put(HTTP_WHITEBOARD_CONTEXT_SELECT, getFilter(httpContextHolder.getServiceReference()));
props.put(Const.EQUINOX_LEGACY_TCCL_PROP, Thread.currentThread().getContextClassLoader());
props.put(Constants.SERVICE_RANKING, findFilterPriority(initparams));
fillInitParams(props, initparams, HTTP_WHITEBOARD_FILTER_INIT_PARAM_PREFIX);
LegacyFilterFactory filterFactory = new LegacyFilterFactory(filter);
registration = bundle.getBundleContext().registerService(Filter.class, filterFactory, props);
// check that init got called and did not throw an exception
filterFactory.checkForError();
httpContextHolder.incrementUseCount();
objectRegistration = new HttpServiceObjectRegistration(filter, registration, httpContextHolder, bundle);
Set<HttpServiceObjectRegistration> objectRegistrations = bundleRegistrations.get(bundle);
if (objectRegistrations == null) {
objectRegistrations = new HashSet<HttpServiceObjectRegistration>();
bundleRegistrations.put(bundle, objectRegistrations);
}
objectRegistrations.add(objectRegistration);
legacyMappings.put(objectRegistration.serviceKey, objectRegistration);
} finally {
if (objectRegistration == null || !legacyMappings.containsKey(objectRegistration.serviceKey)) {
// something bad happened above (likely going to throw a runtime exception)
decrementFactoryUseCount(httpContextHolder);
if (registration != null) {
registration.unregister();
}
}
}
}
}
private void fillInitParams(
Dictionary<String, Object> props,
Dictionary<?, ?> initparams, String prefix) {
if (initparams != null) {
for (Enumeration<?> eKeys = initparams.keys(); eKeys.hasMoreElements();) {
String key = String.valueOf(eKeys.nextElement());
String value = String.valueOf(initparams.get(key));
if (value != null) {
props.put(prefix + key, value);
}
}
}
}
private static int findFilterPriority(Dictionary<String, String> initparams) {
if (initparams == null) {
return 0;
}
String filterPriority = initparams.get(Const.FILTER_PRIORITY);
if (filterPriority == null) {
return 0;
}
try {
int result = Integer.parseInt(filterPriority);
if (result >= -1000 && result <= 1000) {
return result;
}
}
catch (NumberFormatException e) {
// fall through
}
throw new IllegalArgumentException(
"filter-priority must be an integer between -1000 and 1000 but " + //$NON-NLS-1$
"was: " + filterPriority); //$NON-NLS-1$
}
public void registerHttpServiceResources(
Bundle bundle, String alias, String name, HttpContextHolder httpContextHolder) throws NamespaceException {
if (alias == null) {
throw new IllegalArgumentException("Alias cannot be null"); //$NON-NLS-1$
}
if (name == null) {
throw new IllegalArgumentException("Name cannot be null"); //$NON-NLS-1$
}
String pattern = alias;
if (pattern.startsWith(Const.SLASH_STAR_DOT)) {
pattern = pattern.substring(1);
}
// need to make sure exact matching aliases are converted to wildcard pattern matches
if (!pattern.endsWith(Const.SLASH_STAR) && !pattern.startsWith(Const.STAR_DOT) && !pattern.contains(Const.SLASH_STAR_DOT)) {
if (pattern.endsWith(Const.SLASH)) {
pattern = pattern + '*';
} else {
pattern = pattern + Const.SLASH_STAR;
}
}
// check the pattern against the original input
ContextController.checkPattern(alias);
synchronized (legacyMappings) {
HttpServiceObjectRegistration objectRegistration = null;
ServiceRegistration<?> registration = null;
try {
String fullAlias = getFullAlias(alias, httpContextHolder);
HttpServiceObjectRegistration existing = legacyMappings.get(fullAlias);
if (existing != null) {
throw new PatternInUseException(alias);
}
Dictionary<String, Object> props = new Hashtable<String, Object>();
props.put(HTTP_WHITEBOARD_TARGET, targetFilter);
props.put(HTTP_WHITEBOARD_RESOURCE_PATTERN, pattern);
props.put(HTTP_WHITEBOARD_RESOURCE_PREFIX, name);
props.put(HTTP_WHITEBOARD_CONTEXT_SELECT, getFilter(httpContextHolder.getServiceReference()));
props.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE);
props.put(Const.EQUINOX_LEGACY_TCCL_PROP, Thread.currentThread().getContextClassLoader());
registration = bundle.getBundleContext().registerService(String.class, "resource", props); //$NON-NLS-1$
httpContextHolder.incrementUseCount();
objectRegistration = new HttpServiceObjectRegistration(fullAlias, registration, httpContextHolder, bundle);
Set<HttpServiceObjectRegistration> objectRegistrations = bundleRegistrations.get(bundle);
if (objectRegistrations == null) {
objectRegistrations = new HashSet<HttpServiceObjectRegistration>();
bundleRegistrations.put(bundle, objectRegistrations);
}
objectRegistrations.add(objectRegistration);
Map<String, String> aliasCustomizations = bundleAliasCustomizations.get(bundle);
if (aliasCustomizations == null) {
aliasCustomizations = new HashMap<String, String>();
bundleAliasCustomizations.put(bundle, aliasCustomizations);
}
aliasCustomizations.put(alias, fullAlias);
legacyMappings.put(objectRegistration.serviceKey, objectRegistration);
} finally {
if (objectRegistration == null || !legacyMappings.containsKey(objectRegistration.serviceKey)) {
// something bad happened above (likely going to throw a runtime exception)
// need to clean up the factory reference
decrementFactoryUseCount(httpContextHolder);
if (registration != null) {
registration.unregister();
}
}
}
}
}
private Object getFilter(ServiceReference<? extends ServletContextHelper> serviceReference) {
String ctxName = (String)serviceReference.getProperty(HTTP_WHITEBOARD_CONTEXT_NAME);
return String.format("(&(%s=%s)(%s=%s))", HTTP_SERVICE_CONTEXT_PROPERTY, ctxName, HTTP_WHITEBOARD_CONTEXT_NAME, ctxName); //$NON-NLS-1$
}
public void registerHttpServiceServlet(
Bundle bundle, String alias, Servlet servlet, Dictionary<?, ?> initparams, HttpContextHolder httpContextHolder) throws NamespaceException, ServletException{
if (alias == null) {
throw new IllegalArgumentException("Alias cannot be null"); //$NON-NLS-1$
}
if (servlet == null) {
throw new IllegalArgumentException("Servlet cannot be null"); //$NON-NLS-1$
}
// check the pattern against the original input
ContextController.checkPattern(alias);
Object pattern = alias;
// need to make sure exact matching aliases are converted to exact matching + wildcard pattern matching
if (!alias.endsWith(Const.SLASH_STAR) && !alias.startsWith(Const.STAR_DOT) && !alias.contains(Const.SLASH_STAR_DOT)) {
if (alias.endsWith(Const.SLASH)) {
pattern = new String[] {alias, alias + '*'};
} else {
pattern = new String[] {alias, alias + Const.SLASH_STAR};
}
}
synchronized (legacyMappings) {
LegacyServlet legacyServlet = new LegacyServlet(servlet);
if (getRegisteredObjects().contains(legacyServlet)) {
throw new ServletAlreadyRegisteredException(servlet);
}
HttpServiceObjectRegistration objectRegistration = null;
ServiceRegistration<Servlet> registration = null;
try {
String fullAlias = getFullAlias(alias, httpContextHolder);
HttpServiceObjectRegistration existing = legacyMappings.get(fullAlias);
if (existing != null) {
throw new PatternInUseException(alias);
}
String servletName = servlet.getClass().getName();
if ((initparams != null) && (initparams.get(Const.SERVLET_NAME) != null)) {
servletName = String.valueOf(initparams.get(Const.SERVLET_NAME));
}
Dictionary<String, Object> props = new Hashtable<String, Object>();
props.put(HTTP_WHITEBOARD_TARGET, targetFilter);
props.put(HTTP_WHITEBOARD_SERVLET_PATTERN, pattern);
props.put(HTTP_WHITEBOARD_SERVLET_NAME, servletName);
props.put(HTTP_WHITEBOARD_CONTEXT_SELECT, getFilter(httpContextHolder.getServiceReference()));
props.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE);
props.put(Const.EQUINOX_LEGACY_TCCL_PROP, Thread.currentThread().getContextClassLoader());
fillInitParams(props, initparams, HTTP_WHITEBOARD_SERVLET_INIT_PARAM_PREFIX);
registration = bundle.getBundleContext().registerService(Servlet.class, legacyServlet, props);
// check that init got called and did not throw an exception
legacyServlet.checkForError();
httpContextHolder.incrementUseCount();
objectRegistration = new HttpServiceObjectRegistration(fullAlias, registration, httpContextHolder, bundle);
Set<HttpServiceObjectRegistration> objectRegistrations = bundleRegistrations.get(bundle);
if (objectRegistrations == null) {
objectRegistrations = new HashSet<HttpServiceObjectRegistration>();
bundleRegistrations.put(bundle, objectRegistrations);
}
objectRegistrations.add(objectRegistration);
Map<String, String> aliasCustomizations = bundleAliasCustomizations.get(bundle);
if (aliasCustomizations == null) {
aliasCustomizations = new HashMap<String, String>();
bundleAliasCustomizations.put(bundle, aliasCustomizations);
}
aliasCustomizations.put(alias, fullAlias);
legacyMappings.put(objectRegistration.serviceKey, objectRegistration);
} finally {
if (objectRegistration == null || !legacyMappings.containsKey(objectRegistration.serviceKey)) {
// something bad happened above (likely going to throw a runtime exception)
// need to clean up the factory reference
decrementFactoryUseCount(httpContextHolder);
if (registration != null) {
registration.unregister();
}
}
}
}
}
private String getFullAlias(String alias, HttpContextHolder httpContextHolder) {
@SuppressWarnings("unchecked")
AtomicReference<ContextController> controllerRef = contextServiceTracker.getService((ServiceReference<ServletContextHelper>)httpContextHolder.getServiceReference());
if (controllerRef != null) {
ContextController controller = controllerRef.get();
if (controller != null) {
return controller.getContextPath() + alias;
}
}
return alias;
}
public void unregisterHttpServiceAlias(Bundle bundle, String alias) {
synchronized (legacyMappings) {
Map<String, String> aliasCustomizations = bundleAliasCustomizations.get(bundle);
String aliasCustomization = aliasCustomizations == null ? null : aliasCustomizations.remove(alias);
if (aliasCustomization == null) {
throw new IllegalArgumentException("The bundle did not register the alias: " + alias); //$NON-NLS-1$
}
HttpServiceObjectRegistration objectRegistration = legacyMappings.get(aliasCustomization);
if (objectRegistration == null) {
throw new IllegalArgumentException("No registration found for alias: " + alias); //$NON-NLS-1$
}
Set<HttpServiceObjectRegistration> objectRegistrations = bundleRegistrations.get(bundle);
if (objectRegistrations == null || !objectRegistrations.remove(objectRegistration))
{
throw new IllegalArgumentException("The bundle did not register the alias: " + alias); //$NON-NLS-1$
}
try {
objectRegistration.registration.unregister();
} catch (IllegalStateException e) {
// ignore; already unregistered
}
decrementFactoryUseCount(objectRegistration.httpContextHolder);
legacyMappings.remove(aliasCustomization);
}
}
public void unregisterHttpServiceFilter(Bundle bundle, Filter filter) {
synchronized (legacyMappings) {
HttpServiceObjectRegistration objectRegistration = legacyMappings.get(filter);
if (objectRegistration == null) {
throw new IllegalArgumentException("No registration found for filter: " + filter); //$NON-NLS-1$
}
Set<HttpServiceObjectRegistration> objectRegistrations = bundleRegistrations.get(bundle);
if (objectRegistrations == null || !objectRegistrations.remove(objectRegistration))
{
throw new IllegalArgumentException("The bundle did not register the filter: " + filter); //$NON-NLS-1$
}
try {
objectRegistration.registration.unregister();
} catch (IllegalStateException e) {
// ignore; already unregistered
}
decrementFactoryUseCount(objectRegistration.httpContextHolder);
legacyMappings.remove(filter);
}
}
public void unregisterHttpServiceObjects(Bundle bundle) {
synchronized (legacyMappings) {
bundleAliasCustomizations.remove(bundle);
Set<HttpServiceObjectRegistration> objectRegistrations = bundleRegistrations.remove(bundle);
if (objectRegistrations != null) {
for (HttpServiceObjectRegistration objectRegistration : objectRegistrations) {
try {
objectRegistration.registration.unregister();
} catch (IllegalStateException e) {
// ignore; already unregistered
}
decrementFactoryUseCount(objectRegistration.httpContextHolder);
legacyMappings.remove(objectRegistration.serviceKey);
}
}
}
}
private void decrementFactoryUseCount(HttpContextHolder holder) {
synchronized (legacyContextMap) {
if (holder.decrementUseCount() == 0) {
legacyContextMap.remove(holder.getHttpContext());
}
}
}
private static org.osgi.framework.Filter createErrorPageFilter(BundleContext context) {
StringBuilder sb = new StringBuilder();
sb.append("("); //$NON-NLS-1$
sb.append(HTTP_WHITEBOARD_SERVLET_ERROR_PAGE);
sb.append("=*)"); //$NON-NLS-1$
try {
return context.createFilter(sb.toString());
}
catch (InvalidSyntaxException ise) {
throw new IllegalArgumentException(ise);
}
}
private static org.osgi.framework.Filter createResourceFilter(BundleContext context) {
StringBuilder sb = new StringBuilder();
sb.append("(&("); //$NON-NLS-1$
sb.append(HTTP_WHITEBOARD_RESOURCE_PREFIX);
sb.append("=*)("); //$NON-NLS-1$
sb.append(HTTP_WHITEBOARD_RESOURCE_PATTERN);
sb.append("=*))"); //$NON-NLS-1$
try {
return context.createFilter(sb.toString());
}
catch (InvalidSyntaxException ise) {
throw new IllegalArgumentException(ise);
}
}
private static org.osgi.framework.Filter createServletFilter(BundleContext context) {
StringBuilder sb = new StringBuilder();
sb.append("(&(objectClass="); //$NON-NLS-1$
sb.append(Servlet.class.getName());
sb.append(")(|("); //$NON-NLS-1$
sb.append(HTTP_WHITEBOARD_SERVLET_NAME);
sb.append("=*)("); //$NON-NLS-1$
sb.append(HTTP_WHITEBOARD_SERVLET_PATTERN);
sb.append("=*)))"); //$NON-NLS-1$
try {
return context.createFilter(sb.toString());
}
catch (InvalidSyntaxException ise) {
throw new IllegalArgumentException(ise);
}
}
private static org.osgi.framework.Filter createFilterFilter(BundleContext context) {
StringBuilder sb = new StringBuilder();
sb.append("(&(objectClass="); //$NON-NLS-1$
sb.append(Filter.class.getName());
sb.append(")(|("); //$NON-NLS-1$
sb.append(HTTP_WHITEBOARD_FILTER_PATTERN);
sb.append("=*)("); //$NON-NLS-1$
sb.append(HTTP_WHITEBOARD_FILTER_REGEX);
sb.append("=*)("); //$NON-NLS-1$
sb.append(HTTP_WHITEBOARD_FILTER_SERVLET);
sb.append("=*)))"); //$NON-NLS-1$
try {
return context.createFilter(sb.toString());
}
catch (InvalidSyntaxException ise) {
throw new IllegalArgumentException(ise);
}
}
private static org.osgi.framework.Filter createListenerFilter(BundleContext context) {
StringBuilder sb = new StringBuilder();
sb.append("(&"); //$NON-NLS-1$
sb.append("(").append(HTTP_WHITEBOARD_LISTENER).append("=*)"); //$NON-NLS-1$ //$NON-NLS-2$
sb.append("(|"); //$NON-NLS-1$
sb.append("(objectClass=").append(ServletContextListener.class.getName()).append(")"); //$NON-NLS-1$ //$NON-NLS-2$
sb.append("(objectClass=").append(ServletContextAttributeListener.class.getName()).append(")"); //$NON-NLS-1$ //$NON-NLS-2$
sb.append("(objectClass=").append(ServletRequestListener.class.getName()).append(")"); //$NON-NLS-1$ //$NON-NLS-2$
sb.append("(objectClass=").append(ServletRequestAttributeListener.class.getName()).append(")"); //$NON-NLS-1$ //$NON-NLS-2$
sb.append("(objectClass=").append(HttpSessionListener.class.getName()).append(")"); //$NON-NLS-1$ //$NON-NLS-2$
sb.append("(objectClass=").append(HttpSessionAttributeListener.class.getName()).append(")"); //$NON-NLS-1$ //$NON-NLS-2$
sb.append("(objectClass=").append(HttpSessionIdListener.class.getName()).append(")"); //$NON-NLS-1$ //$NON-NLS-2$
sb.append(")"); //$NON-NLS-1$
sb.append(")"); //$NON-NLS-1$
try {
return context.createFilter(sb.toString());
}
catch (InvalidSyntaxException ise) {
throw new IllegalArgumentException(ise);
}
}
public org.osgi.framework.Filter getListenerFilter() {
return listenerServiceFilter;
}
public org.osgi.framework.Filter getErrorPageFilter() {
return errorPageServiceFilter;
}
public org.osgi.framework.Filter getFilterFilter() {
return filterServiceFilter;
}
public org.osgi.framework.Filter getServletFilter() {
return servletServiceFilter;
}
public org.osgi.framework.Filter getResourceFilter() {
return resourceServiceFilter;
}
public void recordFailedErrorPageDTO(
ServiceReference<?> serviceReference,
FailedErrorPageDTO failedErrorPageDTO) {
if (failedErrorPageDTOs.containsKey(serviceReference)) {
return;
}
failedErrorPageDTOs.put(serviceReference, failedErrorPageDTO);
}
public void recordFailedFilterDTO(
ServiceReference<Filter> serviceReference,
FailedFilterDTO failedFilterDTO) {
if (failedFilterDTOs.containsKey(serviceReference)) {
return;
}
failedFilterDTOs.put(serviceReference, failedFilterDTO);
}
public void recordFailedListenerDTO(
ServiceReference<EventListener> serviceReference,
FailedListenerDTO failedListenerDTO) {
if (failedListenerDTOs.containsKey(serviceReference)) {
return;
}
failedListenerDTOs.put(serviceReference, failedListenerDTO);
}
public void recordFailedResourceDTO(
ServiceReference<?> serviceReference, FailedResourceDTO failedResourceDTO) {
if (failedResourceDTOs.containsKey(serviceReference)) {
return;
}
failedResourceDTOs.put(serviceReference, failedResourceDTO);
}
public void recordFailedServletContextDTO(
ServiceReference<ServletContextHelper> serviceReference, long shadowingServiceId, int failureReason) {
ExtendedFailedServletContextDTO failedServletContextDTO = new ExtendedFailedServletContextDTO();
failedServletContextDTO.attributes = Collections.emptyMap();
failedServletContextDTO.contextPath = String.valueOf(serviceReference.getProperty(HTTP_WHITEBOARD_CONTEXT_PATH));
failedServletContextDTO.errorPageDTOs = new ExtendedErrorPageDTO[0];
failedServletContextDTO.failureReason = failureReason;
failedServletContextDTO.filterDTOs = new FilterDTO[0];
failedServletContextDTO.initParams = ServiceProperties.parseInitParams(
serviceReference, HTTP_WHITEBOARD_CONTEXT_INIT_PARAM_PREFIX);
failedServletContextDTO.listenerDTOs = new ListenerDTO[0];
failedServletContextDTO.name = String.valueOf(serviceReference.getProperty(HTTP_WHITEBOARD_CONTEXT_NAME));
failedServletContextDTO.resourceDTOs = new ResourceDTO[0];
failedServletContextDTO.serviceId = (Long)serviceReference.getProperty(Constants.SERVICE_ID);
failedServletContextDTO.servletDTOs = new ServletDTO[0];
failedServletContextDTO.shadowingServiceId = shadowingServiceId;
failedServletContextDTOs.put(serviceReference, failedServletContextDTO);
}
public void recordFailedServletDTO(
ServiceReference<?> serviceReference,
FailedServletDTO failedServletDTO) {
if (failedServletDTOs.containsKey(serviceReference)) {
return;
}
failedServletDTOs.put(serviceReference, failedServletDTO);
}
public void recordFailedPreprocessorDTO(
ServiceReference<Preprocessor> serviceReference,
FailedPreprocessorDTO failedPreprocessorDTO) {
if (failedPreprocessorDTOs.containsKey(serviceReference)) {
return;
}
failedPreprocessorDTOs.put(serviceReference, failedPreprocessorDTO);
}
public void removeFailedErrorPageDTO(
ServiceReference<Servlet> serviceReference) {
failedErrorPageDTOs.remove(serviceReference);
}
public void removeFailedFilterDTO(
ServiceReference<Filter> serviceReference) {
failedFilterDTOs.remove(serviceReference);
}
public void removeFailedListenerDTO(
ServiceReference<EventListener> serviceReference) {
failedListenerDTOs.remove(serviceReference);
}
public void removeFailedResourceDTO(
ServiceReference<Object> serviceReference) {
failedResourceDTOs.remove(serviceReference);
}
public void removeFailedServletDTO(
ServiceReference<Servlet> serviceReference) {
failedServletDTOs.remove(serviceReference);
}
public void removeFailedPreprocessorDTO(
ServiceReference<Preprocessor> serviceReference) {
failedPreprocessorDTOs.remove(serviceReference);
}
public synchronized void fireSessionIdChanged(String oldSessionId) {
for (ContextController contextController : controllerMap.values()) {
contextController.fireSessionIdChanged(oldSessionId);
}
}
public void sessionDestroyed(String sessionId) {
httpSessionTracker.invalidate(sessionId, false);
}
public void setHsrRegistration(ServiceRegistration<HttpServiceRuntime> hsrRegistration) {
this.hsrRegistration.set(hsrRegistration);
}
ServiceRegistration<HttpServiceRuntime> getHsrRegistration() {
return hsrRegistration.get();
}
long getServiceChangecount() {
return serviceChangecount.get();
}
public void incrementServiceChangecount() {
serviceChangecount.incrementAndGet();
if (hsrRegistration.get() != null && !scheduledExecutor.isShutdown() && semaphore.tryAcquire()) {
scheduledExecutor.schedule(new ChangeCountTimer(), 100, TimeUnit.MILLISECONDS);
}
}
Semaphore getSemaphore() {
return semaphore;
}
private final Map<String, Object> attributes;
private final String targetFilter;
final ServiceRegistration<DefaultServletContextHelper> defaultContextReg;
private final ServletContext parentServletContext;
private final BundleContext trackingContext;
private final BundleContext consumingContext;
private final org.osgi.framework.Filter errorPageServiceFilter;
private final org.osgi.framework.Filter servletServiceFilter;
private final org.osgi.framework.Filter resourceServiceFilter;
private final org.osgi.framework.Filter filterServiceFilter;
private final org.osgi.framework.Filter listenerServiceFilter;
// BEGIN of old HttpService support
final ConcurrentMap<HttpContext, HttpContextHolder> legacyContextMap =
new ConcurrentHashMap<HttpContext, HttpContextHolder>();
private final Map<Object, HttpServiceObjectRegistration> legacyMappings =
Collections.synchronizedMap(new HashMap<Object, HttpServiceObjectRegistration>());
private final Map<Bundle, Set<HttpServiceObjectRegistration>> bundleRegistrations =
new HashMap<Bundle, Set<HttpServiceObjectRegistration>>();
private final Map<Bundle, Map<String, String>> bundleAliasCustomizations = new HashMap<Bundle, Map<String,String>>();
// END of old HttpService support
private final ConcurrentMap<ServiceReference<ServletContextHelper>, ContextController> controllerMap =
new ConcurrentSkipListMap<ServiceReference<ServletContextHelper>, ContextController>(Collections.reverseOrder());
private final ConcurrentMap<ServiceReference<Preprocessor>, PreprocessorRegistration> preprocessorMap =
new ConcurrentSkipListMap<ServiceReference<Preprocessor>, PreprocessorRegistration>(Collections.reverseOrder());
final ConcurrentMap<ServiceReference<?>, FailedErrorPageDTO> failedErrorPageDTOs =
new ConcurrentHashMap<ServiceReference<?>, FailedErrorPageDTO>();
final ConcurrentMap<ServiceReference<Filter>, FailedFilterDTO> failedFilterDTOs =
new ConcurrentHashMap<ServiceReference<Filter>, FailedFilterDTO>();
final ConcurrentMap<ServiceReference<EventListener>, FailedListenerDTO> failedListenerDTOs =
new ConcurrentHashMap<ServiceReference<EventListener>, FailedListenerDTO>();
final ConcurrentMap<ServiceReference<?>, FailedResourceDTO> failedResourceDTOs =
new ConcurrentHashMap<ServiceReference<?>, FailedResourceDTO>();
final ConcurrentMap<ServiceReference<ServletContextHelper>, ExtendedFailedServletContextDTO> failedServletContextDTOs =
new ConcurrentHashMap<ServiceReference<ServletContextHelper>, ExtendedFailedServletContextDTO>();
final ConcurrentMap<ServiceReference<?>, FailedServletDTO> failedServletDTOs =
new ConcurrentHashMap<ServiceReference<?>, FailedServletDTO>();
final ConcurrentMap<ServiceReference<?>, FailedPreprocessorDTO> failedPreprocessorDTOs =
new ConcurrentHashMap<ServiceReference<?>, FailedPreprocessorDTO>();
private final Set<Object> registeredObjects = Collections.newSetFromMap(new ConcurrentHashMap<Object, Boolean>());
private final ServiceTracker<LoggerFactory, Logger> loggerFactoryTracker;
private final ServiceTracker<ServletContextHelper, AtomicReference<ContextController>> contextServiceTracker;
private final ServiceTracker<Preprocessor, AtomicReference<PreprocessorRegistration>> preprocessorServiceTracker;
private final ServiceTracker<ContextPathCustomizer, ContextPathCustomizer> contextPathAdaptorTracker;
private final ContextPathCustomizerHolder contextPathCustomizerHolder;
private final HttpSessionTracker httpSessionTracker;
private final ServiceRegistration<HttpSessionInvalidator> invalidatorReg;
private final AtomicReference<ServiceRegistration<HttpServiceRuntime>> hsrRegistration = new AtomicReference<>();
private final AtomicLong serviceChangecount = new AtomicLong();
private final ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
private final Semaphore semaphore = new Semaphore(1);
class ChangeCountTimer implements Callable<Void> {
@Override
public Void call() {
try {
Dictionary<String,Object> properties = getHsrRegistration().getReference().getProperties();
properties.put(Constants.SERVICE_CHANGECOUNT, getServiceChangecount());
getHsrRegistration().setProperties(properties);
return null;
}
finally {
getSemaphore().release();
}
}
}
static class LegacyServiceObject {
final AtomicReference<Exception> error = new AtomicReference<Exception>(new ServletException("The init() method was never called.")); //$NON-NLS-1$
public void checkForError() {
Exception result = error.get();
if (result != null) {
Throw.unchecked(result);
}
}
}
public static class LegacyFilterFactory extends LegacyServiceObject implements PrototypeServiceFactory<Filter> {
final Filter filter;
public LegacyFilterFactory(Filter filter) {
this.filter = filter;
}
@Override
public Filter getService(Bundle bundle, ServiceRegistration<Filter> registration) {
return new LegacyFilter();
}
@Override
public void ungetService(
Bundle bundle, ServiceRegistration<Filter> registration, Filter service) {
// do nothing
}
// NOTE we do not do the same equals check here for filter that we do for servlet
// this is because we must allow filter to be applied to all context helpers
// TODO this means it is still possible that init() will get called if the same filter
// is registered multiple times. This is unfortunate but is an error case on the client anyway.
class LegacyFilter implements Filter {
/**
* @throws ServletException
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
try {
filter.init(filterConfig);
error.set(null);
} catch (Exception e){
error.set(e);
Throw.unchecked(e);
}
}
@Override
public void doFilter(
ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
filter.doFilter(request, response, chain);
}
@Override
public void destroy() {
filter.destroy();
}
}
}
static class LegacyServlet extends LegacyServiceObject implements Servlet {
final Servlet servlet;
public LegacyServlet(Servlet servlet) {
this.servlet = servlet;
}
/**
* @throws ServletException
*/
@Override
public void init(ServletConfig config)
throws ServletException {
try {
servlet.init(config);
error.set(null);
} catch (Exception e){
error.set(e);
Throw.unchecked(e);
}
}
@Override
public ServletConfig getServletConfig() {
return servlet.getServletConfig();
}
@Override
public void
service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
servlet.service(req, res);
}
@Override
public String getServletInfo() {
return servlet.getServletInfo();
}
@Override
public void destroy() {
servlet.destroy();
}
@Override
public int hashCode() {
return servlet.hashCode();
}
@Override
public boolean equals(Object other) {
if (other instanceof LegacyServlet) {
other = ((LegacyServlet) other).servlet;
}
return servlet.equals(other);
}
}
static class ContextPathCustomizerHolder implements ServiceTrackerCustomizer<ContextPathCustomizer, ContextPathCustomizer> {
private final BundleContext context;
private final ServiceTracker<ServletContextHelper, AtomicReference<ContextController>> contextServiceTracker;
private final NavigableMap<ServiceReference<ContextPathCustomizer>, ContextPathCustomizer> pathCustomizers =
new TreeMap<ServiceReference<ContextPathCustomizer>, ContextPathCustomizer>(Collections.reverseOrder());
public ContextPathCustomizerHolder(
BundleContext context,
ServiceTracker<ServletContextHelper, AtomicReference<ContextController>> contextServiceTracker) {
super();
this.context = context;
this.contextServiceTracker = contextServiceTracker;
}
@Override
public ContextPathCustomizer addingService(
ServiceReference<ContextPathCustomizer> reference) {
ContextPathCustomizer service = context.getService(reference);
boolean reset = false;
synchronized (pathCustomizers) {
pathCustomizers.put(reference, service);
reset = pathCustomizers.firstKey().equals(reference);
}
if (reset) {
contextServiceTracker.close();
contextServiceTracker.open();
}
return service;
}
@Override
public void modifiedService(
ServiceReference<ContextPathCustomizer> reference,
ContextPathCustomizer service) {
removedService(reference, service);
addingService(reference);
}
@Override
public void removedService(
ServiceReference<ContextPathCustomizer> reference,
ContextPathCustomizer service) {
boolean reset = false;
synchronized (pathCustomizers) {
ServiceReference<ContextPathCustomizer> currentFirst = pathCustomizers.firstKey();
pathCustomizers.remove(reference);
reset = currentFirst.equals(reference);
}
// only reset if the tracker is still open
if (reset && contextServiceTracker.getTrackingCount() >= 0) {
contextServiceTracker.close();
contextServiceTracker.open();
}
context.ungetService(reference);
}
ContextPathCustomizer getHighestRanked() {
synchronized (pathCustomizers) {
Map.Entry<ServiceReference<ContextPathCustomizer>, ContextPathCustomizer> firstEntry = pathCustomizers.firstEntry();
return firstEntry == null ? null : firstEntry.getValue();
}
}
}
}