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/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()
      */