FELIX-55 Upgrade to Jetty 6.1.7 and export Servlet API 2.5 from within the bundle
FELIX-434 Support remote user and authentication type API when using handleSecurity()

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@641218 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/http.jetty/pom.xml b/http.jetty/pom.xml
index 259cdb0..ad5e7f4 100644
--- a/http.jetty/pom.xml
+++ b/http.jetty/pom.xml
@@ -1,81 +1,110 @@
 <!--
- 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
-
+    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.
+    
+    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.
 -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-  <parent>
-    <groupId>org.apache.felix</groupId>
-    <artifactId>felix</artifactId>
-    <version>1.0.2</version>
-    <relativePath>../pom/pom.xml</relativePath>
-  </parent>
-  <modelVersion>4.0.0</modelVersion>
-  <packaging>bundle</packaging>
-  <name>Apache Felix HTTP Service</name>
-  <version>0.9.0-SNAPSHOT</version>
-  <artifactId>org.apache.felix.http.jetty</artifactId>
-  <dependencies>
-    <dependency>
-      <groupId>${pom.groupId}</groupId>
-      <artifactId>org.osgi.core</artifactId>
-      <version>1.1.0-SNAPSHOT</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>${pom.groupId}</groupId>
-      <artifactId>org.osgi.compendium</artifactId>
-      <version>1.0.0</version>
-    </dependency>
-    <dependency>
-      <groupId>${pom.groupId}</groupId>
-      <artifactId>javax.servlet</artifactId>
-      <version>1.0.0</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>jetty</groupId>
-      <artifactId>org.mortbay.jetty-jdk1.2</artifactId>
-      <version>4.2.25</version>
-    </dependency>
-  </dependencies>
-  <build>
-    <plugins>
-      <plugin>
+    <parent>
         <groupId>org.apache.felix</groupId>
-        <artifactId>maven-bundle-plugin</artifactId>
-        <version>1.4.0</version>
-        <extensions>true</extensions>
-        <configuration>
-          <instructions>
-            <Bundle-Name>HTTP Service</Bundle-Name>
-            <Bundle-Description>An implementation of the OSGi HTTP Service using Jetty.</Bundle-Description>
-            <Bundle-Activator>${pom.artifactId}.Activator</Bundle-Activator>
-            <Bundle-DocURL>http://oscar-osgi.sf.net/obr2/${pom.artifactId}/</Bundle-DocURL>
-            <Bundle-URL>http://oscar-osgi.sf.net/obr2/${pom.artifactId}/${pom.artifactId}-${pom.version}.jar</Bundle-URL>
-            <Bundle-Source>http://oscar-osgi.sf.net/obr2/${pom.artifactId}/${pom.artifactId}-${pom.version}-src.jar</Bundle-Source>
-            <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
-            <DynamicImport-Package>javax.net.ssl</DynamicImport-Package>
-            <Export-Package>org.osgi.service.http; version=1.1</Export-Package>
-            <Import-Package>!com.sun.net.ssl.internal.ssl,!com.sun.net.ssl,!org.xml.sax,!org.xml.sax.helpers,!javax.xml.parsers,!javax.security.cert,javax.servlet;version=1.1, javax.servlet.http;version=1.1,*</Import-Package>
-	        <Private-Package>org.apache.felix.http.jetty, org.mortbay.*;-split-package:=merge-first</Private-Package>
-            <Export-Service>org.osgi.service.http.HttpService</Export-Service>
-          </instructions>
-        </configuration>
-      </plugin>
-    </plugins>
-  </build>
+        <artifactId>felix</artifactId>
+        <version>1.0.2</version>
+        <relativePath>../pom/pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>bundle</packaging>
+    <name>Apache Felix HTTP Service</name>
+    <version>0.9.0-SNAPSHOT</version>
+    <artifactId>org.apache.felix.http.jetty</artifactId>
+    <dependencies>
+        <dependency>
+            <groupId>${pom.groupId}</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <version>1.0.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>${pom.groupId}</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <version>1.0.0</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>${pom.groupId}</groupId>
+                    <artifactId>javax.servlet</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.mortbay.jetty</groupId>
+            <artifactId>servlet-api-2.5</artifactId>
+            <version>6.1.7</version>
+        </dependency>
+        <dependency>
+            <groupId>org.mortbay.jetty</groupId>
+            <artifactId>jetty</artifactId>
+            <version>6.1.7</version>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Bundle-Name>HTTP Service</Bundle-Name>
+                        <Bundle-Description>
+                            An implementation of the OSGi HTTP Service
+                            using Jetty.
+                        </Bundle-Description>
+                        <Bundle-Activator>
+                            ${pom.artifactId}.Activator
+                        </Bundle-Activator>
+                        <Bundle-DocURL>
+                            http://oscar-osgi.sf.net/obr2/${pom.artifactId}/
+                        </Bundle-DocURL>
+                        <Bundle-URL>
+                            http://oscar-osgi.sf.net/obr2/${pom.artifactId}/${pom.artifactId}-${pom.version}.jar
+                        </Bundle-URL>
+                        <Bundle-Source>
+                            http://oscar-osgi.sf.net/obr2/${pom.artifactId}/${pom.artifactId}-${pom.version}-src.jar
+                        </Bundle-Source>
+                        <Bundle-SymbolicName>
+                            ${pom.artifactId}
+                        </Bundle-SymbolicName>
+                        <Export-Package>
+                            org.osgi.service.http; version=1.1,
+                            javax.servlet;javax.servlet.http;version=2.5
+                        </Export-Package>
+                        <Private-Package>
+                            org.apache.felix.http.jetty,
+                            org.mortbay.*;-split-package:=merge-first
+                        </Private-Package>
+                        <Import-Package>
+                            javax.net.ssl; javax.security.cert;
+                            javax.xml.parsers; org.xml.sax;
+                            org.xml.sax.helpers;
+                            org.slf4j;resolution:=optional, *
+                        </Import-Package>
+                        <Export-Service>
+                            org.osgi.service.http.HttpService
+                        </Export-Service>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
 </project>
diff --git a/http.jetty/src/main/java/org/apache/felix/http/jetty/Activator.java b/http.jetty/src/main/java/org/apache/felix/http/jetty/Activator.java
index 20bd058..c1a91f6 100644
--- a/http.jetty/src/main/java/org/apache/felix/http/jetty/Activator.java
+++ b/http.jetty/src/main/java/org/apache/felix/http/jetty/Activator.java
@@ -19,16 +19,18 @@
 package org.apache.felix.http.jetty;
 
 
-import java.lang.reflect.Constructor;
-
-import org.mortbay.http.HashUserRealm;
-import org.mortbay.http.HttpServer;
-import org.mortbay.http.JsseListener;
-import org.mortbay.http.SocketListener;
+import org.mortbay.jetty.Connector;
+import org.mortbay.jetty.Server;
+import org.mortbay.jetty.handler.HandlerCollection;
+import org.mortbay.jetty.nio.SelectChannelConnector;
+import org.mortbay.jetty.security.HashUserRealm;
+import org.mortbay.jetty.security.SslSocketConnector;
+import org.mortbay.jetty.servlet.Context;
 import org.mortbay.jetty.servlet.OsgiServletHandler;
-import org.mortbay.jetty.servlet.ServletHttpContext;
-import org.mortbay.util.Code;
-import org.mortbay.util.InetAddrPort;
+import org.mortbay.jetty.servlet.SessionHandler;
+import org.mortbay.log.Log;
+import org.mortbay.log.Logger;
+import org.mortbay.log.StdErrLog;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
@@ -36,6 +38,8 @@
 import org.osgi.framework.ServiceFactory;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.http.HttpService;
+import org.osgi.service.log.LogService;
+import org.osgi.util.tracker.ServiceTracker;
 
 
 /**
@@ -63,11 +67,12 @@
 public class Activator implements BundleActivator
 {
     protected static boolean debug = false;
+    private static ServiceTracker m_logTracker = null;
 
     private BundleContext m_bundleContext = null;
     private ServiceRegistration m_svcReg = null;
     private HttpServiceFactory m_httpServ = null;
-    private HttpServer m_server = null;
+    private Server m_server = null;
     private OsgiServletHandler m_hdlr = null;
 
     private int m_httpPort;
@@ -84,10 +89,9 @@
         String optDebug = m_bundleContext.getProperty( "org.apache.felix.http.jetty.debug" );
         if ( optDebug != null && optDebug.toLowerCase().equals( "true" ) )
         {
-            Code.setDebug( true );
             debug = true;
         }
-
+        
         // get default HTTP and HTTPS ports as per the OSGi spec
         try
         {
@@ -110,6 +114,12 @@
             m_httpsPort = 443;
         }
 
+        m_logTracker = new ServiceTracker( bundleContext, LogService.class.getName(), null );
+        m_logTracker.open();
+
+        // set the Jetty logger to be LogService based
+        initializeJettyLogger();
+
         try
         {
             initializeJetty();
@@ -118,7 +128,7 @@
         catch ( Exception ex )
         {
             //TODO: maybe throw a bundle exception in here?
-            System.out.println( "Http2: " + ex );
+            log( LogService.LOG_INFO, "Http2", ex );
             return;
         }
 
@@ -144,9 +154,29 @@
         {
             //TODO: log some form of error
         }
+
+        // replace non-LogService logger for jetty
+        Log.setLog( new StdErrLog() );
+        
+        m_logTracker.close();
     }
 
 
+    protected void initializeJettyLogger() {
+        String oldProperty = System.getProperty( "org.mortbay.log.class" );
+        System.setProperty( "org.mortbay.log.class", LogServiceLog.class.getName() );
+        
+        if (!(Log.getLog() instanceof LogServiceLog)) {
+            Log.setLog( new LogServiceLog() );
+        }
+        
+        Log.getLog().setDebugEnabled( debug );
+        
+        if (oldProperty != null) {
+            System.setProperty( "org.mortbay.log.class", oldProperty );
+        }
+    }
+    
     protected void initializeJetty() throws Exception
     {
         //TODO: Maybe create a separate "JettyServer" object here?
@@ -154,13 +184,14 @@
         HashUserRealm realm = new HashUserRealm( "OSGi HTTP Service Realm" );
 
         // Create server
-        m_server = new HttpServer();
-        m_server.addRealm( realm );
+        m_server = new Server();
+        m_server.addUserRealm( realm );
 
         // Add a regular HTTP listener
-        SocketListener listener = null;
-        listener = ( SocketListener ) m_server.addListener( new InetAddrPort( m_httpPort ) );
-        listener.setMaxIdleTimeMs( 60000 );
+        Connector connector = new SelectChannelConnector();
+        connector.setPort( m_httpPort );
+        connector.setMaxIdleTime( 60000 );
+        m_server.addConnector( connector );
 
         // See if we need to add an HTTPS listener
         String enableHTTPS = m_bundleContext.getProperty( "org.ungoverned.osgi.bundle.https.enable" );
@@ -169,20 +200,13 @@
             initializeHTTPS();
         }
 
-        m_server.start();
-
         // setup the Jetty web application context shared by all Http services
-        ServletHttpContext hdlrContext = new ServletHttpContext();
-        hdlrContext.setContextPath( "/" );
-        //TODO: was in original code, but seems we shouldn't serve
-        //      resources in servlet context
-        //hdlrContext.setServingResources(true);
-        hdlrContext.setClassLoader( getClass().getClassLoader() );
-        debug( " adding handler context : " + hdlrContext );
-        m_server.addContext( hdlrContext );
-
         m_hdlr = new OsgiServletHandler();
-        hdlrContext.addHandler( m_hdlr );
+
+        Context hdlrContext = new Context( m_server, new SessionHandler(), null, m_hdlr, null );
+        hdlrContext.setClassLoader( getClass().getClassLoader() );
+        hdlrContext.setContextPath( "/" );
+        debug( " adding handler context : " + hdlrContext );
 
         try
         {
@@ -191,9 +215,10 @@
         catch ( Exception e )
         {
             // make sure we unwind the adding process
-            System.err.println( "Exception Starting Jetty Handler Context: " + e );
-            e.printStackTrace( System.err );
+            log( LogService.LOG_ERROR, "Exception Starting Jetty Handler Context", e );
         }
+
+        m_server.start();
     }
 
 
@@ -208,45 +233,60 @@
             sslProvider = "org.mortbay.http.SunJsseListener";
         }
 
+
+        SslSocketConnector s_listener = new SslSocketConnector();
+        s_listener.setPort( m_httpsPort );
+        s_listener.setMaxIdleTime( 60000 );
+
         // Set default jetty properties for supplied values. For any not set,
         // Jetty will fallback to checking system properties.
         String keystore = m_bundleContext.getProperty( "org.ungoverned.osgi.bundle.https.keystore" );
         if ( keystore != null )
         {
-            System.setProperty( JsseListener.KEYSTORE_PROPERTY, keystore );
+            s_listener.setKeystore( keystore );
         }
 
         String passwd = m_bundleContext.getProperty( "org.ungoverned.osgi.bundle.https.password" );
         if ( passwd != null )
         {
-            System.setProperty( JsseListener.PASSWORD_PROPERTY, passwd );
+            System.setProperty( SslSocketConnector.PASSWORD_PROPERTY, passwd );
+            s_listener.setPassword( passwd );
         }
 
         String keyPasswd = m_bundleContext.getProperty( "org.ungoverned.osgi.bundle.https.key.password" );
         if ( keyPasswd != null )
         {
-            System.setProperty( JsseListener.KEYPASSWORD_PROPERTY, keyPasswd );
+            System.setProperty( SslSocketConnector.KEYPASSWORD_PROPERTY, keyPasswd );
+            s_listener.setKeyPassword( keyPasswd );
         }
 
-        //SunJsseListener s_listener = new SunJsseListener(new InetAddrPort(m_httpsPort));
-        Object args[] =
-            { new InetAddrPort( m_httpsPort ) };
-        Class argTypes[] =
-            { args[0].getClass() };
-        Class clazz = Class.forName( sslProvider );
-        Constructor cstruct = clazz.getDeclaredConstructor( argTypes );
-        JsseListener s_listener = ( JsseListener ) cstruct.newInstance( args );
-
-        m_server.addListener( s_listener );
-        s_listener.setMaxIdleTimeMs( 60000 );
+        m_server.addConnector( s_listener );
     }
 
 
-    protected static void debug( String txt )
+    public static void debug( String txt )
     {
         if ( debug )
         {
-            System.err.println( ">>Oscar HTTP: " + txt );
+            log( LogService.LOG_DEBUG, ">>Felix HTTP: " + txt, null );
+        }
+    }
+
+
+    public static void log( int level, String message, Throwable throwable )
+    {
+        LogService log = ( LogService ) m_logTracker.getService();
+        if ( log != null )
+        {
+            log.log( level, message, throwable );
+        }
+        else
+        {
+            System.out.println( message );
+            if ( throwable != null )
+            {
+                throwable.printStackTrace( System.out );
+            }
         }
     }
 
diff --git a/http.jetty/src/main/java/org/apache/felix/http/jetty/HttpServiceImpl.java b/http.jetty/src/main/java/org/apache/felix/http/jetty/HttpServiceImpl.java
index 29dfa6f..02995a7 100644
--- a/http.jetty/src/main/java/org/apache/felix/http/jetty/HttpServiceImpl.java
+++ b/http.jetty/src/main/java/org/apache/felix/http/jetty/HttpServiceImpl.java
@@ -30,12 +30,16 @@
 import javax.servlet.Servlet;
 import javax.servlet.ServletException;
 
-import org.mortbay.http.HttpServer;
+import org.mortbay.jetty.Server;
+import org.mortbay.jetty.servlet.Context;
+import org.mortbay.jetty.servlet.OsgiResourceHolder;
 import org.mortbay.jetty.servlet.OsgiServletHandler;
+import org.mortbay.jetty.servlet.SessionHandler;
 import org.osgi.framework.Bundle;
 import org.osgi.service.http.HttpContext;
 import org.osgi.service.http.HttpService;
 import org.osgi.service.http.NamespaceException;
+import org.osgi.service.log.LogService;
 
 
 public class HttpServiceImpl implements HttpService
@@ -48,11 +52,11 @@
 
     /** Bundle which "got" this service instance from the service factory */
     private Bundle m_bundle = null;
-    private HttpServer m_server = null;
+    private Server m_server = null;
     private OsgiServletHandler m_serverServletHandler = null;
 
 
-    public HttpServiceImpl( Bundle bundle, HttpServer server, OsgiServletHandler serverServletHandler )
+    public HttpServiceImpl( Bundle bundle, Server server, OsgiServletHandler serverServletHandler )
     {
         m_bundle = bundle;
         m_server = server;
@@ -139,38 +143,25 @@
         addAlias( alias, null );
 
         //make sure alias is unique, and create
-        org.mortbay.http.HttpContext hdlrContext = null;
+        Context hdlrContext = null;
 
         if ( osgiHttpContext == null )
         {
             osgiHttpContext = createDefaultHttpContext();
         }
 
-        hdlrContext = m_server.addContext( alias );
+        // servlets using same context must get same handler to ensure
+        // they share a common ServletContext
+        Activator.debug( "looking for context: " + osgiHttpContext );
+        ServletContextGroup grp = ServletContextGroup.getServletContextGroup( m_serverServletHandler, osgiHttpContext );
 
-        // update alias namespace with reference to context object for later
+        grp.addResource( alias, name );
+
+        // update alias namespace with reference to group object for later
         // unregistering
-        updateAlias( alias, hdlrContext );
+        updateAlias( alias, grp );
 
-        // create resource handler, observing any access controls
-        AccessControlContext acc = null;
-        if ( System.getSecurityManager() != null )
-        {
-            acc = AccessController.getContext();
-        }
-        OsgiResourceHandler hdlr = new OsgiResourceHandler( osgiHttpContext, name, acc );
-
-        hdlrContext.addHandler( hdlr );
-        try
-        {
-            hdlrContext.start();
-        }
-        catch ( Exception e )
-        {
-            System.err.println( "Oscar exception adding resource: " + e );
-            e.printStackTrace( System.err );
-            // maybe we should remove alias here?
-        }
+        // maybe should remove alias/servlet entries if exceptions?
     }
 
 
@@ -196,34 +187,11 @@
 
     protected void doUnregister( String alias, boolean forced )
     {
-        Object obj = removeAlias( alias );
-
-        if ( obj instanceof org.mortbay.http.HttpContext )
+        Activator.debug( "** http unregister servlet :" + m_bundle + ", alias: " + alias + ",forced:" + forced );
+        ServletContextGroup grp = removeAlias( alias );
+        if ( grp != null )
         {
-            Activator.debug( "** http unregister resource :" + m_bundle + ", alias: " + alias );
-
-            org.mortbay.http.HttpContext ctxt = ( org.mortbay.http.HttpContext ) obj;
-            try
-            {
-                ctxt.stop();
-                m_server.removeContext( ctxt );
-            }
-            catch ( Exception e )
-            {
-                System.err.println( "Oscar exception removing resource: " + e );
-                e.printStackTrace();
-            }
-        }
-        else if ( obj instanceof ServletContextGroup )
-        {
-            Activator.debug( "** http unregister servlet :" + m_bundle + ", alias: " + alias + ",forced:" + forced );
-
-            ServletContextGroup grp = ( ServletContextGroup ) obj;
-            grp.removeServlet( alias, forced );
-        }
-        else
-        {
-            // oops - this shouldn't happen !
+            grp.remove( alias, forced );
         }
     }
 
@@ -243,14 +211,14 @@
     }
 
 
-    protected Object removeAlias( String alias )
+    protected ServletContextGroup removeAlias( String alias )
     {
         synchronized ( m_aliasNamespace )
         {
             // remove alias, don't worry if doesn't exist
             Object obj = m_aliasNamespace.remove( alias );
             m_localAliasSet.remove( alias );
-            return obj;
+            return ( ServletContextGroup ) obj;
         }
     }
 
diff --git a/http.jetty/src/main/java/org/apache/felix/http/jetty/LogServiceLog.java b/http.jetty/src/main/java/org/apache/felix/http/jetty/LogServiceLog.java
new file mode 100644
index 0000000..f439a30
--- /dev/null
+++ b/http.jetty/src/main/java/org/apache/felix/http/jetty/LogServiceLog.java
@@ -0,0 +1,128 @@
+/*
+ * 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.jetty;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.mortbay.log.Logger;
+import org.osgi.service.log.LogService;
+
+
+public class LogServiceLog implements Logger
+{
+    private static Map loggers = new HashMap();
+
+    private final String m_name;
+
+    private boolean m_debugEnabled;
+
+
+    public LogServiceLog()
+    {
+        this( "org.mortbay.log" );
+    }
+
+
+    public LogServiceLog( String name )
+    {
+        this.m_name = name;
+    }
+
+
+    public Logger getLogger( String name )
+    {
+        Logger logger = ( Logger ) loggers.get( name );
+        if ( logger == null )
+        {
+            logger = new LogServiceLog( name );
+            logger.setDebugEnabled( isDebugEnabled() );
+            loggers.put( name, logger );
+        }
+        return logger;
+    }
+
+
+    public boolean isDebugEnabled()
+    {
+        return m_debugEnabled;
+    }
+
+
+    public void setDebugEnabled( boolean enabled )
+    {
+        this.m_debugEnabled = enabled;
+    }
+
+
+    public void debug( String msg, Throwable throwable )
+    {
+        log( LogService.LOG_DEBUG, msg, throwable );
+    }
+
+
+    public void debug( String msg, Object arg0, Object arg1 )
+    {
+        log( LogService.LOG_DEBUG, format( msg, arg0, arg1 ), null );
+    }
+
+
+    public void info( String msg, Object arg0, Object arg1 )
+    {
+        log( LogService.LOG_INFO, format( msg, arg0, arg1 ), null );
+    }
+
+
+    public void warn( String msg, Throwable throwable )
+    {
+        log( LogService.LOG_WARNING, msg, throwable );
+    }
+
+
+    public void warn( String msg, Object arg0, Object arg1 )
+    {
+        log( LogService.LOG_WARNING, format( msg, arg0, arg1 ), null );
+    }
+
+
+    public String toString()
+    {
+        return m_name;
+    }
+
+
+    private String format( String msg, Object arg0, Object arg1 )
+    {
+        int i0 = msg.indexOf( "{}" );
+        int i1 = i0 < 0 ? -1 : msg.indexOf( "{}", i0 + 2 );
+
+        if ( arg1 != null && i1 >= 0 )
+            msg = msg.substring( 0, i1 ) + arg1 + msg.substring( i1 + 2 );
+        if ( arg0 != null && i0 >= 0 )
+            msg = msg.substring( 0, i0 ) + arg0 + msg.substring( i0 + 2 );
+        return msg;
+    }
+
+
+    private void log( int level, String message, Throwable throwable )
+    {
+        Activator.log( level, m_name + ":" + message, throwable );
+    }
+}
diff --git a/http.jetty/src/main/java/org/apache/felix/http/jetty/OsgiResourceHandler.java b/http.jetty/src/main/java/org/apache/felix/http/jetty/OsgiResourceHandler.java
deleted file mode 100644
index f6c81b3..0000000
--- a/http.jetty/src/main/java/org/apache/felix/http/jetty/OsgiResourceHandler.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * 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.jetty;
-
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.URL;
-import java.security.AccessControlContext;
-import java.security.AccessController;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
-
-import org.mortbay.http.HttpException;
-import org.mortbay.http.HttpRequest;
-import org.mortbay.http.HttpResponse;
-import org.mortbay.http.handler.AbstractHttpHandler;
-import org.mortbay.jetty.servlet.DummyServletHttpRequest;
-import org.mortbay.jetty.servlet.DummyServletHttpResponse;
-import org.mortbay.jetty.servlet.OsgiServletHandler;
-import org.mortbay.jetty.servlet.ServletHttpRequest;
-import org.mortbay.jetty.servlet.ServletHttpResponse;
-import org.osgi.service.http.HttpContext;
-
-
-public class OsgiResourceHandler extends AbstractHttpHandler
-{
-    protected HttpContext m_osgiHttpContext;
-    protected String m_name;
-    protected OsgiServletHandler m_dummyHandler;
-    protected AccessControlContext m_acc;
-
-
-    public OsgiResourceHandler( HttpContext osgiHttpContext, String name, AccessControlContext acc )
-    {
-        m_osgiHttpContext = osgiHttpContext;
-        m_name = name;
-        // needed for OSGi security handling
-        m_dummyHandler = new OsgiServletHandler();
-        m_acc = acc;
-    }
-
-
-    public void initialize( org.mortbay.http.HttpContext context )
-    {
-        super.initialize( context );
-        m_dummyHandler.initialize( context );
-    }
-
-
-    public void handle( String pathInContext, String pathParams, HttpRequest request, HttpResponse response )
-        throws HttpException, IOException
-    {
-        Activator.debug( "handle for name:" + m_name + "(path=" + pathInContext + ")" );
-
-        ServletHttpRequest servletRequest = new DummyServletHttpRequest( m_dummyHandler, pathInContext, request );
-        ServletHttpResponse servletResponse = new DummyServletHttpResponse( servletRequest, response );
-
-        if ( !m_osgiHttpContext.handleSecurity( servletRequest, servletResponse ) )
-        {
-            // spec doesn't state specific processing here apart from
-            // "send the response back to the client". We take this to mean
-            // any response generated in the context, and so all we do here
-            // is set handled to "true" to ensure any output is sent back
-            request.setHandled( true );
-            return;
-        }
-
-        // Create resource based name and see if we can resolve it
-        String resName = m_name + pathInContext;
-        Activator.debug( "** looking for: " + resName );
-        URL url = m_osgiHttpContext.getResource( resName );
-
-        if ( url == null )
-        {
-            return;
-        }
-
-        Activator.debug( "serving up:" + resName );
-
-        // It doesn't state so in the OSGi spec, but can't see how anything
-        // other than GET and variants would be supported
-        String method = request.getMethod();
-        if ( method.equals( HttpRequest.__GET ) || method.equals( HttpRequest.__POST )
-            || method.equals( HttpRequest.__HEAD ) )
-        {
-            handleGet( request, response, url, resName );
-        }
-        else
-        {
-            try
-            {
-                response.sendError( HttpResponse.__501_Not_Implemented );
-            }
-            catch ( Exception e )
-            {/*TODO: include error logging*/
-            }
-        }
-    }
-
-
-    public void handleGet( HttpRequest request, final HttpResponse response, final URL url, String resName )
-        throws IOException
-    {
-        String encoding = m_osgiHttpContext.getMimeType( resName );
-
-        if ( encoding == null )
-        {
-            encoding = getHttpContext().getMimeByExtension( resName );
-        }
-
-        if ( encoding == null )
-        {
-            encoding = getHttpContext().getMimeByExtension( ".default" );
-        }
-
-        //TODO: not sure why this is needed, but sometimes get "IllegalState"
-        // errors if not included
-        response.setAcceptTrailer( true );
-        response.setContentType( encoding );
-
-        //TODO: check other http fields e.g. ranges, timestamps etc.
-
-        // make sure we access the resource inside the bundle's access control
-        // context if supplied
-        if ( System.getSecurityManager() != null )
-        {
-            try
-            {
-                AccessController.doPrivileged( new PrivilegedExceptionAction()
-                {
-                    public Object run() throws Exception
-                    {
-                        copyResourceBytes( url, response );
-                        return null;
-                    }
-                }, m_acc );
-            }
-            catch ( PrivilegedActionException ex )
-            {
-                IOException ioe = ( IOException ) ex.getException();
-                throw ioe;
-            }
-        }
-        else
-        {
-            copyResourceBytes( url, response );
-        }
-
-        request.setHandled( true );
-        //TODO: set other http fields e.g. __LastModified, __ContentLength
-    }
-
-
-    private void copyResourceBytes( URL url, HttpResponse response ) throws IOException
-    {
-        OutputStream os = null;
-        InputStream is = null;
-
-        try
-        {
-            os = response.getOutputStream();
-            is = url.openStream();
-
-            int len = 0;
-            byte[] buf = new byte[1024];
-            int n = 0;
-
-            while ( ( n = is.read( buf, 0, buf.length ) ) >= 0 )
-            {
-                os.write( buf, 0, n );
-                len += n;
-            }
-
-            try
-            {
-                response.setContentLength( len );
-            }
-            catch ( IllegalStateException ex )
-            {
-                System.err.println( "OsgiResourceHandler: " + ex );
-            }
-        }
-        finally
-        {
-            if ( is != null )
-            {
-                is.close();
-            }
-            if ( os != null )
-            {
-                os.close();
-            }
-        }
-    }
-}
diff --git a/http.jetty/src/main/java/org/apache/felix/http/jetty/ServletContextGroup.java b/http.jetty/src/main/java/org/apache/felix/http/jetty/ServletContextGroup.java
index 069de1f..3872123 100644
--- a/http.jetty/src/main/java/org/apache/felix/http/jetty/ServletContextGroup.java
+++ b/http.jetty/src/main/java/org/apache/felix/http/jetty/ServletContextGroup.java
@@ -18,6 +18,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
+import java.util.Collections;
 import java.util.Dictionary;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -31,6 +32,8 @@
 import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
 
+import org.mortbay.jetty.servlet.Holder;
+import org.mortbay.jetty.servlet.OsgiResourceHolder;
 import org.mortbay.jetty.servlet.OsgiServletHandler;
 import org.mortbay.jetty.servlet.OsgiServletHolder;
 import org.mortbay.jetty.servlet.ServletHolder;
@@ -116,22 +119,45 @@
     }
 
 
-    void removeServlet( String alias, boolean destroy )
+    void addResource( String alias, String path )
     {
         String wAlias = aliasWildcard( alias );
-        OsgiServletHolder holder = m_hdlr.removeOsgiServletHolder( wAlias );
-        Servlet servlet = holder.getOsgiServlet();
-        Activator.debug( " removing servlet instance: " + servlet );
-        m_servletSet.remove( servlet );
+        ServletHolder holder = new OsgiResourceHolder( m_hdlr, path, this );
+        m_hdlr.addOsgiServletHolder( wAlias, holder );
+        Activator.debug( " adding resources for " + wAlias + " at: " + path );
+    }
 
-        if ( destroy )
-        {
-            servlet.destroy();
-        }
 
-        if ( m_servletSet.isEmpty() )
+    void remove( String alias, boolean destroy )
+    {
+        String wAlias = aliasWildcard( alias );
+        ServletHolder holder = m_hdlr.removeOsgiServletHolder( wAlias );
+
+        if ( holder != null )
         {
-            destroy();
+            try
+            {
+                Servlet servlet = holder.getServlet();
+                if ( servlet != null )
+                {
+                    Activator.debug( " removing servlet instance: " + servlet );
+                    m_servletSet.remove( servlet );
+
+                    if ( destroy )
+                    {
+                        servlet.destroy();
+                    }
+
+                    if ( m_servletSet.isEmpty() )
+                    {
+                        destroy();
+                    }
+                }
+            }
+            catch ( ServletException se )
+            {
+                // may only be thrown if servlet in holder is null
+            }
         }
     }
 
@@ -164,6 +190,12 @@
     }
 
 
+    public String getContextPath()
+    {
+        return m_hdlr.getServletContext().getContextPath();
+    }
+
+
     public String getMimeType( String file )
     {
         String type = m_osgiHttpContext.getMimeType( file );
@@ -209,18 +241,47 @@
     }
 
 
+    public Set getResourcePaths( String path )
+    {
+        // This is not implemented yet, might want to access the bundle entries
+        return null;
+    }
+
+
     public RequestDispatcher getRequestDispatcher( String uri )
     {
         return m_hdlr.getServletContext().getRequestDispatcher( uri );
     }
 
 
+    public RequestDispatcher getNamedDispatcher( String name )
+    {
+        if ( getMinorVersion() >= 2 )
+        {
+            return m_hdlr.getServletContext().getNamedDispatcher( name );
+        }
+
+        return null;
+    }
+
+
     public String getServerInfo()
     {
         return m_hdlr.getServletContext().getServerInfo();
     }
 
 
+    public String getServletContextName()
+    {
+        if ( getMinorVersion() >= 3 )
+        {
+            return m_hdlr.getServletContext().getServletContextName();
+        }
+
+        return null;
+    }
+
+
     public Servlet getServlet( String servletName ) throws ServletException
     {
         return m_hdlr.getServletContext().getServlet( servletName );
@@ -233,6 +294,28 @@
     }
 
 
+    public String getInitParameter( String name )
+    {
+        if ( getMinorVersion() >= 2 )
+        {
+            return m_hdlr.getServletContext().getInitParameter( name );
+        }
+
+        return null;
+    }
+
+
+    public Enumeration getInitParameterNames()
+    {
+        if ( getMinorVersion() >= 2 )
+        {
+            return m_hdlr.getServletContext().getInitParameterNames();
+        }
+
+        return Collections.enumeration( Collections.EMPTY_LIST );
+    }
+
+
     /* (non-Javadoc)
      * @see javax.servlet.ServletContext#getServlets()
      */
diff --git a/http.jetty/src/main/java/org/mortbay/jetty/servlet/DummyServletHttpRequest.java b/http.jetty/src/main/java/org/mortbay/jetty/servlet/DummyServletHttpRequest.java
deleted file mode 100644
index b01c415..0000000
--- a/http.jetty/src/main/java/org/mortbay/jetty/servlet/DummyServletHttpRequest.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/* 
- * 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.mortbay.jetty.servlet;
-
-
-import org.mortbay.http.HttpRequest;
-
-
-public class DummyServletHttpRequest extends ServletHttpRequest
-{
-    public DummyServletHttpRequest( ServletHandler servletHandler, String pathInContext, HttpRequest request )
-    {
-        super( servletHandler, pathInContext, request );
-    }
-
-}
\ No newline at end of file
diff --git a/http.jetty/src/main/java/org/mortbay/jetty/servlet/DummyServletHttpResponse.java b/http.jetty/src/main/java/org/mortbay/jetty/servlet/DummyServletHttpResponse.java
deleted file mode 100644
index 7f0d368..0000000
--- a/http.jetty/src/main/java/org/mortbay/jetty/servlet/DummyServletHttpResponse.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/* 
- * 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.mortbay.jetty.servlet;
-
-
-import org.mortbay.http.HttpResponse;
-
-
-public class DummyServletHttpResponse extends ServletHttpResponse
-{
-    public DummyServletHttpResponse( ServletHttpRequest request, HttpResponse response )
-    {
-        super( request, response );
-    }
-
-}
diff --git a/http.jetty/src/main/java/org/mortbay/jetty/servlet/OsgiResourceHolder.java b/http.jetty/src/main/java/org/mortbay/jetty/servlet/OsgiResourceHolder.java
new file mode 100644
index 0000000..241acb1
--- /dev/null
+++ b/http.jetty/src/main/java/org/mortbay/jetty/servlet/OsgiResourceHolder.java
@@ -0,0 +1,237 @@
+/*
+ * 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.mortbay.jetty.servlet;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.felix.http.jetty.Activator;
+import org.apache.felix.http.jetty.ServletContextGroup;
+import org.mortbay.jetty.HttpConnection;
+import org.mortbay.jetty.HttpMethods;
+import org.mortbay.jetty.Request;
+import org.osgi.service.http.HttpContext;
+import org.osgi.service.log.LogService;
+
+
+public class OsgiResourceHolder extends ServletHolder
+{
+    private ServletContextGroup m_servletContextGroup;
+    private HttpContext m_osgiHttpContext;
+    private AccessControlContext m_acc;
+
+
+    public OsgiResourceHolder( ServletHandler handler, String name, ServletContextGroup servletContextGroup )
+    {
+        super();
+
+        setServletHandler( handler );
+        setName( name );
+
+        m_servletContextGroup = servletContextGroup;
+        m_osgiHttpContext = servletContextGroup.getOsgiHttpContext();
+
+        if ( System.getSecurityManager() != null )
+        {
+            m_acc = AccessController.getContext();
+        }
+    }
+
+
+    public synchronized Servlet getServlet()
+    {
+        return null;
+    }
+
+
+    // override "Holder" method to prevent instantiation
+    public synchronized Object newInstance()
+    {
+        return null;
+    }
+
+
+    public void handle( ServletRequest sRequest, ServletResponse sResponse ) throws ServletException, IOException
+    {
+        HttpServletRequest request = ( HttpServletRequest ) sRequest;
+        HttpServletResponse response = ( HttpServletResponse ) sResponse;
+        String target = request.getPathInfo();
+
+        Activator.debug( "handle for name:" + getName() + "(path=" + target + ")" );
+
+        if ( !m_osgiHttpContext.handleSecurity( request, response ) )
+        {
+            return;
+        }
+
+        // Create resource based name and see if we can resolve it
+        String resName = getName() + target;
+        Activator.debug( "** looking for: " + resName );
+        URL url = m_osgiHttpContext.getResource( resName );
+
+        if ( url == null )
+        {
+            Request base_request = sRequest instanceof Request ? ( Request ) sRequest : HttpConnection
+                .getCurrentConnection().getRequest();
+            base_request.setHandled( false );
+            return;
+        }
+
+        Activator.debug( "serving up:" + resName );
+
+        String method = request.getMethod();
+        if ( method.equals( HttpMethods.GET ) || method.equals( HttpMethods.POST ) || method.equals( HttpMethods.HEAD ) )
+        {
+            handleGet( request, response, url, resName );
+        }
+        else
+        {
+            try
+            {
+                response.sendError( HttpServletResponse.SC_NOT_IMPLEMENTED );
+            }
+            catch ( Exception e )
+            {/*TODO: include error logging*/
+            }
+        }
+    }
+
+
+    public void handleGet( HttpServletRequest request, final HttpServletResponse response, final URL url, String resName )
+        throws IOException
+    {
+        String encoding = m_osgiHttpContext.getMimeType( resName );
+
+        if ( encoding == null )
+        {
+            encoding = m_servletContextGroup.getMimeType( resName );
+        }
+
+        if ( encoding == null )
+        {
+            encoding = m_servletContextGroup.getMimeType( ".default" );
+        }
+
+        //TODO: not sure why this is needed, but sometimes get "IllegalState"
+        // errors if not included
+        response.setContentType( encoding );
+
+        //TODO: check other http fields e.g. ranges, timestamps etc.
+
+        // make sure we access the resource inside the bundle's access control
+        // context if supplied
+        if ( m_acc != null )
+        {
+            try
+            {
+                AccessController.doPrivileged( new PrivilegedExceptionAction()
+                {
+                    public Object run() throws Exception
+                    {
+                        copyResourceBytes( url, response );
+                        return null;
+                    }
+                }, m_acc );
+            }
+            catch ( PrivilegedActionException ex )
+            {
+                IOException ioe = ( IOException ) ex.getException();
+                throw ioe;
+            }
+        }
+        else
+        {
+            copyResourceBytes( url, response );
+        }
+
+        //TODO: set other http fields e.g. __LastModified, __ContentLength
+    }
+
+
+    private void copyResourceBytes( URL url, HttpServletResponse response ) throws IOException
+    {
+        OutputStream os = null;
+        InputStream is = null;
+
+        try
+        {
+            os = response.getOutputStream();
+            is = url.openStream();
+
+            int len = 0;
+            byte[] buf = new byte[1024];
+            int n = 0;
+
+            while ( ( n = is.read( buf, 0, buf.length ) ) >= 0 )
+            {
+                os.write( buf, 0, n );
+                len += n;
+            }
+
+            try
+            {
+                response.setContentLength( len );
+            }
+            catch ( IllegalStateException ex )
+            {
+                Activator.log( LogService.LOG_ERROR, "OsgiResourceHandler", ex );
+            }
+        }
+        finally
+        {
+            if ( is != null )
+            {
+                is.close();
+            }
+            if ( os != null )
+            {
+                os.close();
+            }
+        }
+    }
+
+
+    // override "Holder" method to prevent attempt to load
+    // the servlet class.
+    public void doStart() throws Exception
+    {
+    }
+
+
+    // override "Holder" method to prevent destroy, which is only called
+    // when a bundle manually unregisters
+    public void doStop()
+    {
+    }
+
+}
diff --git a/http.jetty/src/main/java/org/mortbay/jetty/servlet/OsgiServletHandler.java b/http.jetty/src/main/java/org/mortbay/jetty/servlet/OsgiServletHandler.java
index 49da73d..beaae87 100644
--- a/http.jetty/src/main/java/org/mortbay/jetty/servlet/OsgiServletHandler.java
+++ b/http.jetty/src/main/java/org/mortbay/jetty/servlet/OsgiServletHandler.java
@@ -21,13 +21,14 @@
 
 import java.io.IOException;
 import java.net.URL;
+
 import javax.servlet.ServletException;
-import javax.servlet.UnavailableException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.mortbay.http.PathMap;
-import org.mortbay.util.Code;
+import org.apache.felix.http.jetty.Activator;
+import org.mortbay.util.LazyList;
+import org.osgi.service.log.LogService;
 
 
 public class OsgiServletHandler extends ServletHandler
@@ -35,20 +36,68 @@
     // allow external adding of osgi servlet holder
     public void addOsgiServletHolder( String pathSpec, ServletHolder holder )
     {
-        super.addServletHolder( pathSpec, holder );
+        super.addServletWithMapping( holder, pathSpec );
     }
 
 
-    public OsgiServletHolder removeOsgiServletHolder( String pathSpec )
+    public ServletHolder removeOsgiServletHolder( String pathSpec )
     {
-        OsgiServletHolder holder = ( OsgiServletHolder ) super.getServletHolder( pathSpec );
-        PathMap map = super.getServletMap();
-        map.remove( pathSpec );
+        ServletMapping oldMapping = null;
+        ServletMapping[] mappings = getServletMappings();
+        for ( int i = 0; i < mappings.length && oldMapping == null; i++ )
+        {
+            String[] pathSpecs = mappings[i].getPathSpecs();
+            for ( int j = 0; j < pathSpecs.length && oldMapping == null; j++ )
+            {
+                if ( pathSpec.equals( pathSpecs[j] ) )
+                {
+                    oldMapping = mappings[i];
+                }
+            }
+        }
 
-        // Remove holder from handler name map to allow re-registration.
-        super._nameMap.remove( holder.getName() );
+        if ( oldMapping == null )
+        {
+            return null;
+        }
 
-        return holder;
+        ServletHolder[] holders = getServlets();
+        if ( holders != null )
+        {
+            holders = ( ServletHolder[] ) holders.clone();
+        }
+
+        ServletHolder oldHolder = null;
+        for ( int i = 0; i < holders.length; i++ )
+        {
+            if ( oldMapping.getServletName().equals( holders[i].getName() ) )
+            {
+                oldHolder = holders[i];
+            }
+        }
+        if ( oldHolder == null )
+        {
+            return null;
+        }
+
+        try
+        {
+            setServlets( ( ServletHolder[] ) LazyList.removeFromArray( holders, oldHolder ) );
+            setServletMappings( ( ServletMapping[] ) LazyList.removeFromArray( mappings, oldMapping ) );
+
+            if (oldHolder.isStarted() && isStopped()) {
+                oldHolder.stop();
+            }
+
+            return ( ServletHolder ) oldHolder;
+        }
+        catch ( Exception e )
+        {
+            setServlets( holders );
+            if ( e instanceof RuntimeException )
+                throw ( RuntimeException ) e;
+            throw new RuntimeException( e );
+        }
     }
 
 
@@ -56,16 +105,16 @@
     // HttpContext
     public URL getResource( String uriInContext )
     {
-        Code.debug( "OSGI ServletHandler getResource:" + uriInContext );
+        Activator.debug( "OSGI ServletHandler getResource:" + uriInContext );
         return null;
     }
 
 
-    // override standard behaviour to check context first
-    protected void dispatch( String pathInContext, HttpServletRequest request, HttpServletResponse response,
-        ServletHolder servletHolder ) throws ServletException, UnavailableException, IOException
+    public void handle( String target, HttpServletRequest request, HttpServletResponse response, int type )
+        throws IOException, ServletException
     {
-        Code.debug( "dispatch path = " + pathInContext );
-        super.dispatch( pathInContext, request, response, servletHolder );
+        Activator.debug( "dispatch path = " + target );
+        super.handle( target, request, response, type );
     }
+
 }
diff --git a/http.jetty/src/main/java/org/mortbay/jetty/servlet/OsgiServletHolder.java b/http.jetty/src/main/java/org/mortbay/jetty/servlet/OsgiServletHolder.java
index 8b384e5..07b5aa1 100644
--- a/http.jetty/src/main/java/org/mortbay/jetty/servlet/OsgiServletHolder.java
+++ b/http.jetty/src/main/java/org/mortbay/jetty/servlet/OsgiServletHolder.java
@@ -31,9 +31,11 @@
 import javax.servlet.ServletResponse;
 import javax.servlet.UnavailableException;
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.felix.http.jetty.ServletContextGroup;
+import org.osgi.service.http.HttpContext;
 
 
 public class OsgiServletHolder extends ServletHolder
@@ -46,7 +48,10 @@
     public OsgiServletHolder( ServletHandler handler, Servlet servlet, String name,
         ServletContextGroup servletContextGroup, Dictionary params )
     {
-        super( handler, name, servlet.getClass().getName() );
+        super(servlet);
+        setServletHandler( handler );
+        setName( name );
+        
         m_servlet = servlet;
         m_servletContextGroup = servletContextGroup;
 
@@ -58,7 +63,7 @@
             while ( e.hasMoreElements() )
             {
                 Object key = e.nextElement();
-                super.put( key, params.get( key ) );
+                setInitParameter( String.valueOf( key ), String.valueOf( params.get( key ) ) );
             }
         }
     }
@@ -89,6 +94,9 @@
         if ( m_servletContextGroup.getOsgiHttpContext().handleSecurity( ( HttpServletRequest ) request,
             ( HttpServletResponse ) response ) )
         {
+            // wrap request to get auth type and remote user
+            request = new HttpServiceHttpServletRequest( ( HttpServletRequest ) request );
+
             // service request
             super.handle( request, response );
         }
@@ -112,7 +120,7 @@
 
     // override "Holder" method to prevent attempt to load
     // the servlet class.
-    public void start() throws Exception
+    public void doStart() throws Exception
     {
         _class = m_servlet.getClass();
 
@@ -130,7 +138,41 @@
 
     // override "Holder" method to prevent destroy, which is only called
     // when a bundle manually unregisters
-    public void stop()
+    public void doStop()
     {
     }
+
+    // Simple wrapper class returning the authentication type and remote user
+    // from the respective request attribute as specificed by the HttpService
+    // spec (step 4 in Section 102.7, Authentication)
+    private static class HttpServiceHttpServletRequest extends HttpServletRequestWrapper {
+
+        HttpServiceHttpServletRequest(HttpServletRequest request) {
+            super(request);
+        }
+
+        public String getAuthType()
+        {
+            // use the auth type attribute; should not be null, but
+            // better check and use wrapped result if missing
+            Object name = getAttribute( HttpContext.AUTHENTICATION_TYPE );
+            if (name != null) {
+                return name.toString();
+            }
+
+            return super.getAuthType();
+        }
+
+        public String getRemoteUser()
+        {
+            // use the remote user attribute; should not be null, but
+            // better check and use wrapped result if missing
+            Object name = getAttribute( HttpContext.REMOTE_USER );
+            if (name != null) {
+                return name.toString();
+            }
+
+            return super.getRemoteUser();
+        }
+    }
 }