blob: 86ebf7a5fea44aae6c680e6260f8ec5b66df3c75 [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.base.internal.service;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.servlet.Filter;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.ServletException;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.ServletRequestListener;
import org.apache.felix.http.api.ExtHttpService;
import org.apache.felix.http.base.internal.context.ExtServletContext;
import org.apache.felix.http.base.internal.handler.FilterHandler;
import org.apache.felix.http.base.internal.handler.HttpServiceFilterHandler;
import org.apache.felix.http.base.internal.logger.SystemLogger;
import org.apache.felix.http.base.internal.runtime.FilterInfo;
import org.apache.felix.http.base.internal.runtime.ServletInfo;
import org.apache.felix.http.base.internal.util.PatternUtil;
import org.osgi.framework.Bundle;
import org.osgi.service.http.HttpContext;
import org.osgi.service.http.NamespaceException;
/**
* This implementation of the {@link ExtHttpService} implements the front end
* used by client bundles. It performs the validity checks and passes the
* real operation to the shared http service.
*/
public final class PerBundleHttpServiceImpl implements ExtHttpService
{
private final Bundle bundle;
private final Set<Servlet> localServlets = new HashSet<Servlet>();
private final Set<FilterHandler> localFilters = new HashSet<FilterHandler>();
private final ServletContextManager contextManager;
private final SharedHttpServiceImpl sharedHttpService;
public PerBundleHttpServiceImpl(final Bundle bundle,
final SharedHttpServiceImpl sharedHttpService,
final ServletContext context,
final ServletContextAttributeListener servletAttributeListener,
final boolean sharedContextAttributes,
final ServletRequestListener reqListener,
final ServletRequestAttributeListener reqAttrListener)
{
if (bundle == null)
{
throw new IllegalArgumentException("Bundle cannot be null!");
}
if (context == null)
{
throw new IllegalArgumentException("Context cannot be null!");
}
this.bundle = bundle;
this.contextManager = new ServletContextManager(this.bundle,
context,
servletAttributeListener,
sharedContextAttributes,
reqListener,
reqAttrListener,
sharedHttpService.getHandlerRegistry().getRegistry(HttpServiceFactory.HTTP_SERVICE_CONTEXT_SERVICE_ID));
this.sharedHttpService = sharedHttpService;
}
@Override
public HttpContext createDefaultHttpContext()
{
return new DefaultHttpContext(this.bundle);
}
/**
* @see org.apache.felix.http.api.ExtHttpService#registerFilter(javax.servlet.Filter, java.lang.String, java.util.Dictionary, int, org.osgi.service.http.HttpContext)
*/
@Override
public void registerFilter(final Filter filter,
final String pattern,
final Dictionary initParams,
final int ranking,
final HttpContext context)
throws ServletException
{
if (filter == null)
{
throw new IllegalArgumentException("Filter must not be null");
}
final Map<String, String> paramMap = new HashMap<String, String>();
if (initParams != null && initParams.size() > 0)
{
Enumeration e = initParams.keys();
while (e.hasMoreElements())
{
Object key = e.nextElement();
Object value = initParams.get(key);
if ((key instanceof String) && (value instanceof String))
{
paramMap.put((String) key, (String) value);
}
}
}
final FilterInfo filterInfo = new FilterInfo(String.format("%s_%d", filter.getClass(), this.hashCode()), pattern, ranking, paramMap);
if (!filterInfo.isValid())
{
throw new ServletException("Invalid registration information for filter.");
}
final ExtServletContext httpContext = getServletContext(context);
final FilterHandler holder = new HttpServiceFilterHandler(httpContext, filterInfo, filter);
if ( this.sharedHttpService.registerFilter(holder) )
{
synchronized ( this.localFilters )
{
this.localFilters.add(holder);
}
}
}
/**
* No need to sync this method, syncing is done via {@link #registerServlet(String, Servlet, Dictionary, HttpContext)}
* @see org.osgi.service.http.HttpService#registerResources(java.lang.String, java.lang.String, org.osgi.service.http.HttpContext)
*/
@Override
public void registerResources(final String alias, final String name, final HttpContext context) throws NamespaceException
{
if (!isNameValid(name))
{
throw new IllegalArgumentException("Malformed resource name [" + name + "]");
}
if (!PatternUtil.isValidPattern(alias) || !alias.startsWith("/") )
{
throw new IllegalArgumentException("Malformed resource alias [" + alias + "]");
}
try
{
final Servlet servlet = new ResourceServlet(name);
registerServlet(alias, servlet, null, context);
}
catch (ServletException e)
{
SystemLogger.error("Failed to register resources", e);
}
}
/**
* @see org.osgi.service.http.HttpService#registerServlet(java.lang.String, javax.servlet.Servlet, java.util.Dictionary, org.osgi.service.http.HttpContext)
*/
@Override
public void registerServlet(String alias, Servlet servlet, Dictionary initParams, HttpContext context) throws ServletException, NamespaceException
{
if (servlet == null)
{
throw new IllegalArgumentException("Servlet must not be null");
}
if (!PatternUtil.isValidPattern(alias) || !alias.startsWith("/") )
{
throw new IllegalArgumentException("Malformed servlet alias [" + alias + "]");
}
final Map<String, String> paramMap = new HashMap<String, String>();
if (initParams != null && initParams.size() > 0)
{
Enumeration e = initParams.keys();
while (e.hasMoreElements())
{
Object key = e.nextElement();
Object value = initParams.get(key);
if ((key instanceof String) && (value instanceof String))
{
paramMap.put((String) key, (String) value);
}
}
}
synchronized (this.localServlets)
{
if (this.localServlets.contains(servlet))
{
throw new ServletException("Servlet instance " + servlet + " already registered");
}
this.localServlets.add(servlet);
}
final ServletInfo servletInfo = new ServletInfo(String.format("%s_%d", servlet.getClass(), this.hashCode()), alias, paramMap);
final ExtServletContext httpContext = getServletContext(context);
boolean success = false;
try
{
this.sharedHttpService.registerServlet(alias, httpContext, servlet, servletInfo);
success = true;
}
finally
{
if ( !success )
{
synchronized ( this.localServlets )
{
this.localServlets.remove(servlet);
}
}
}
}
/**
* @see org.osgi.service.http.HttpService#unregister(java.lang.String)
*/
@Override
public void unregister(final String alias)
{
final Servlet servlet = this.sharedHttpService.unregister(alias);
if ( servlet != null )
{
synchronized ( this.localServlets )
{
this.localServlets.remove(servlet);
}
}
}
public void unregisterAll()
{
final Set<Servlet> servlets = new HashSet<Servlet>(this.localServlets);
for (final Servlet servlet : servlets)
{
unregisterServlet(servlet, false);
}
final Set<FilterHandler> filters = new HashSet<FilterHandler>(this.localFilters);
for (final FilterHandler holder : filters)
{
this.sharedHttpService.unregisterFilter(holder, false);
}
}
/**
* Old whiteboard support
* @see org.apache.felix.http.api.ExtHttpService#unregisterFilter(javax.servlet.Filter)
*/
@Override
public void unregisterFilter(final Filter filter)
{
this.unregisterFilter(filter, true);
}
/**
* Old whiteboard support
* @see org.apache.felix.http.api.ExtHttpService#unregisterServlet(javax.servlet.Servlet)
*/
@Override
public void unregisterServlet(final Servlet servlet)
{
this.unregisterServlet(servlet, true);
}
private void unregisterServlet(final Servlet servlet, final boolean destroy)
{
if (servlet != null)
{
synchronized ( this.localServlets )
{
this.localServlets.remove(servlet);
}
this.sharedHttpService.unregisterServlet(servlet, destroy);
}
}
public ExtServletContext getServletContext(HttpContext context)
{
if (context == null)
{
context = createDefaultHttpContext();
}
return this.contextManager.getServletContext(context);
}
private void unregisterFilter(final Filter filter, final boolean destroy)
{
if (filter != null)
{
synchronized ( this.localFilters )
{
final Iterator<FilterHandler> i = this.localFilters.iterator();
while ( i.hasNext() )
{
final FilterHandler h = i.next();
if ( h.getFilter() == filter )
{
this.sharedHttpService.unregisterFilter(h, destroy);
i.remove();
break;
}
}
}
}
}
private boolean isNameValid(final String name)
{
if (name == null)
{
return false;
}
if (!name.equals("/") && name.endsWith("/"))
{
return false;
}
return true;
}
}