blob: 581c9de96c35d13d5e1628cd5eccb058b533172d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.felix.http.whiteboard.internal.manager;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.EventListener;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.Filter;
import javax.servlet.Servlet;
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionListener;
import org.apache.felix.http.base.internal.logger.SystemLogger;
import org.apache.felix.http.whiteboard.HttpWhiteboardConstants;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.http.HttpContext;
import org.osgi.service.http.context.ServletContextHelper;
@SuppressWarnings("deprecation")
public final class ExtenderManager
{
private static final String MARKER = "org.apache.felix.http.whiteboard";
public enum Type {
CONTEXT,
FILTER,
SERVLET,
LISTENERS
}
private final Map<String, ServiceRegistration<?>> registrations = new ConcurrentHashMap<>();
/**
* Remove a service registration (if existing)
* @param type The type
* @param ref The service reference
*/
private void removeServiceRegistration(final Type type, final ServiceReference<?> ref)
{
final ServiceRegistration<?> reg = this.registrations.remove(type.name() + String.valueOf(ref.getProperty(Constants.SERVICE_ID)));
if ( reg != null )
{
try
{
reg.unregister();
}
catch ( final IllegalStateException ignore)
{
// we ignore this
}
}
}
/**
* Add a service registration
* @param type The type of the registration
* @param ref The service reference
* @param reg The service registration
*/
private void putServiceRegistration(final Type type, final ServiceReference<?> ref, final ServiceRegistration<?> reg)
{
this.registrations.put(type.name() + String.valueOf(ref.getProperty(Constants.SERVICE_ID)), reg);
}
/**
* Get a string property
* @param ref The service reference
* @param key The name of the property
* @return The value of the property if the type is String, {@code null} otherwise
*/
private String getStringProperty(final ServiceReference<?> ref, final String key)
{
Object value = ref.getProperty(key);
return (value instanceof String) ? (String)value : null;
}
/**
* Get the boolean property.
* @param ref The service reference
* @param key The name of the property
* @return The value of the boolean property. Returns {@code false} as default
*/
private boolean getBooleanProperty(final ServiceReference<?> ref, final String key)
{
Object value = ref.getProperty(key);
if (value instanceof String)
{
return Boolean.valueOf((String) value);
}
else if (value instanceof Boolean)
{
return ((Boolean) value).booleanValue();
}
return false;
}
/**
* Add the init parameters
* @param ref The service reference
* @param prefix The prefix to use
* @param props The properties
*/
private void addInitParams(final ServiceReference<?> ref, final String prefix, final Dictionary<String, Object> props)
{
for (final String key : ref.getPropertyKeys()) {
if (key.startsWith(HttpWhiteboardConstants.INIT_PREFIX)) {
final String paramKey = key.substring(HttpWhiteboardConstants.INIT_PREFIX.length());
final Object paramValue = ref.getProperty(paramKey);
if (paramValue != null) {
props.put(prefix + paramKey, paramValue);
}
}
}
}
/**
* Add the service ranking (if available)
* @param ref The service reference
* @param props The new properties
*/
private void addServiceRanking(final ServiceReference<?> ref, final Dictionary<String, Object> props)
{
// we don't care about the type and assume it's correct
final Object val = ref.getProperty(Constants.SERVICE_RANKING);
if ( val != null )
{
props.put(Constants.SERVICE_RANKING, val);
}
}
/**
* Add the context select property
* @param ref The service reference
* @param props The properties
*/
private void addHttpContextSelect(final ServiceReference<?> ref, final Dictionary<String, Object> props)
{
final String contextId = getStringProperty(ref, HttpWhiteboardConstants.CONTEXT_ID);
// if context id is missing, the default context is used
if ( contextId != null )
{
final StringBuilder select = new StringBuilder();
select.append("(!(");
select.append(org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME);
select.append('=');
select.append(getHttpContextName(ref, contextId, false));
select.append(")(");
select.append(org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME);
select.append('=');
select.append(getHttpContextName(ref, contextId, true));
select.append("))");
props.put(org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT, select.toString());
}
else
{
props.put(org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT,
"(" + org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME
+ "=org.osgi.service.http)");
}
}
/**
* Return the http context name
* @param ref The service reference
* @return The name or {@code null}
*/
private String getHttpContextName(final ServiceReference<?> ref,
final String contextId,
final boolean shared)
{
final StringBuilder name = new StringBuilder();
name.append(MARKER);
name.append('.');
if ( shared )
{
name.append("shared");
}
else
{
name.append(ref.getBundle().getBundleId());
}
name.append('.');
name.append(contextId);
return name.toString();
}
/**
* Add a HttpContext
* @param service The HttpContext
* @param ref The service reference
*/
public void addHttpContext(final HttpContext service, final ServiceReference<HttpContext> ref)
{
final String contextId = getStringProperty(ref, HttpWhiteboardConstants.CONTEXT_ID);
if (contextId != null && !contextId.isEmpty())
{
final boolean shared = getBooleanProperty(ref, HttpWhiteboardConstants.CONTEXT_SHARED);
final String contextName = getHttpContextName(ref, contextId, shared);
final Dictionary<String, Object> props = new Hashtable<>();
props.put(org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME, contextName);
props.put(org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH, "/");
props.put(org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_SERVICE_CONTEXT_PROPERTY, "true");
props.put(MARKER + ".id", contextId);
props.put(MARKER, "true");
this.addServiceRanking(ref, props);
final ServiceRegistration<ServletContextHelper> reg = ref.getBundle().getBundleContext().registerService(
ServletContextHelper.class,
new ServletContextHelper() {
@Override
public boolean handleSecurity(final HttpServletRequest request, final HttpServletResponse response)
throws IOException {
return service.handleSecurity(request, response);
}
@Override
public URL getResource(final String name) {
return service.getResource(name);
}
@Override
public String getMimeType(final String name) {
return service.getMimeType(name);
}
},
props);
this.putServiceRegistration(Type.CONTEXT, ref, reg);
}
else
{
SystemLogger.debug("Ignoring HttpContext Service " + ref + ", " + HttpWhiteboardConstants.CONTEXT_ID
+ " is missing or empty");
}
}
/**
* Remove a HttpContext
* @param ref The service reference
*/
public void removeHttpContext(final ServiceReference<HttpContext> ref)
{
this.removeServiceRegistration(Type.CONTEXT, ref);
}
/**
* Add a filter.
* @param service The filter
* @param ref The filter reference
*/
public void addFilter(final Filter service, final ServiceReference<Filter> ref)
{
final String pattern = getStringProperty(ref, HttpWhiteboardConstants.PATTERN);
if (pattern == null || pattern.isEmpty()) {
SystemLogger.debug("Ignoring Filter Service " + ref + ", " + HttpWhiteboardConstants.PATTERN
+ " is missing or empty");
return;
}
final Dictionary<String, Object> props = new Hashtable<>();
props.put(org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_REGEX, pattern);
props.put(MARKER, "true");
this.addHttpContextSelect(ref, props);
this.addInitParams(ref, org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_INIT_PARAM_PREFIX, props);
this.addServiceRanking(ref, props);
final ServiceRegistration<Filter> reg = ref.getBundle().getBundleContext().registerService(
Filter.class,
service,
props);
this.putServiceRegistration(Type.FILTER, ref, reg);
}
/**
* Add a servlet.
* @param service The servlet
* @param ref The service reference
*/
public void addServlet(final Servlet service, final ServiceReference<Servlet> ref)
{
final String alias = getStringProperty(ref, HttpWhiteboardConstants.ALIAS);
if (alias == null || alias.isEmpty() || !alias.startsWith("/"))
{
SystemLogger.debug("Ignoring Servlet Service " + ref + ", " + HttpWhiteboardConstants.ALIAS
+ " is missing, empty or invalid");
return;
}
final Dictionary<String, Object> props = new Hashtable<>();
props.put(org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, alias);
props.put(MARKER, "true");
this.addHttpContextSelect(ref, props);
this.addInitParams(ref, org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_INIT_PARAM_PREFIX, props);
this.addServiceRanking(ref, props);
final ServiceRegistration<Servlet> reg = ref.getBundle().getBundleContext().registerService(
Servlet.class,
service,
props);
this.putServiceRegistration(Type.SERVLET, ref, reg);
}
/**
* Add listeners
* @param service The service
* @param ref The service reference
*/
public void addListeners(final EventListener service, ServiceReference<EventListener> ref)
{
final Dictionary<String, Object> props = new Hashtable<>();
final List<String> names = new ArrayList<>();
if ( service instanceof HttpSessionAttributeListener)
{
names.add(HttpSessionAttributeListener.class.getName());
}
if ( service instanceof HttpSessionListener)
{
names.add(HttpSessionListener.class.getName());
}
if ( service instanceof ServletContextAttributeListener)
{
names.add(ServletContextAttributeListener.class.getName());
}
if ( service instanceof ServletRequestAttributeListener)
{
names.add(ServletRequestAttributeListener.class.getName());
}
if ( service instanceof ServletRequestListener)
{
names.add(ServletRequestListener.class.getName());
}
props.put(MARKER, "true");
props.put(org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_LISTENER, "true");
this.addHttpContextSelect(ref, props);
this.addServiceRanking(ref, props);
final ServiceRegistration<?> reg = ref.getBundle().getBundleContext().registerService(
names.toArray(new String[names.size()]),
service,
props);
this.putServiceRegistration(Type.LISTENERS, ref, reg);
}
/**
* Remove a filter
* @param ref The filter reference
*/
public void removeFilter(final ServiceReference<Filter> ref)
{
this.removeServiceRegistration(Type.FILTER, ref);
}
/**
* Remove a servlet
* @param ref The service reference
*/
public void removeServlet(final ServiceReference<Servlet> ref)
{
this.removeServiceRegistration(Type.SERVLET, ref);
}
/**
* Remove a listener
* @param ref The service reference
*/
public void removeListeners(final ServiceReference<EventListener> ref)
{
this.removeServiceRegistration(Type.LISTENERS, ref);
}
}