PLUTO-619 Fix parsing of escapeXml container runtime option


git-svn-id: https://svn.apache.org/repos/asf/portals/pluto/branches/2.1-spi-cleanup@1377088 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pluto-container-api/src/main/java/org/apache/pluto/container/PortletInvokerService.java b/pluto-container-api/src/main/java/org/apache/pluto/container/PortletInvokerService.java
index 56eca34..161bf27 100644
--- a/pluto-container-api/src/main/java/org/apache/pluto/container/PortletInvokerService.java
+++ b/pluto-container-api/src/main/java/org/apache/pluto/container/PortletInvokerService.java
@@ -69,53 +69,59 @@
      * container to the underlying <code>PortletRquest</code>.
      */
     String METHOD_ID = "org.apache.pluto.core.method";
-
+    
     /**
-     * The unique method identifier for render requests.  Render requests are
-     * requested through a call to the {@link PortletContainer#doRender(org.apache.pluto.container.PortletWindow,
-     * javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
-     * method.
+     * Describes the set of methods that can be invoked by the container
      */
-    Integer METHOD_RENDER = new Integer(1);
+    public enum Method {
 
-    /**
-     * The unique method identifier for render requests.  Render requests are
-     * requested through a call to the {@link PortletContainer#doAction(org.apache.pluto.container.PortletWindow,
-     * javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
-     * method.
-     */
-    Integer METHOD_ACTION = new Integer(3);
+        /**
+         * The unique method identifier for render requests.  Render requests are
+         * requested through a call to the {@link PortletContainer#doRender(org.apache.pluto.container.PortletWindow,
+         * javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
+         * method.
+         */
+        RENDER,
 
-    /**
-     * The unique method identifier for load requests.  Load requests are
-     * requested through a call to the {@link PortletContainer#doLoad(org.apache.pluto.container.PortletWindow,
-     * javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
-     * method.
-     */
-    Integer METHOD_LOAD = new Integer(5);
+        /**
+         * The unique method identifier for render requests.  Render requests are
+         * requested through a call to the {@link PortletContainer#doAction(org.apache.pluto.container.PortletWindow,
+         * javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
+         * method.
+         */
+        ACTION,
 
-    /**
-     * The unique method identifier for resource Serving requests.  Resource requests are
-     * requested through a call to the {@link PortletContainer#doServeResource(PortletWindow,
-     *  javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
-     * method.
-     */
-    Integer METHOD_RESOURCE = new Integer(7);
+        /**
+         * The unique method identifier for load requests.  Load requests are
+         * requested through a call to the {@link PortletContainer#doLoad(org.apache.pluto.container.PortletWindow,
+         * javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
+         * method.
+         */
+        LOAD,
 
-    /**
-     * The unique method identifier for render requests.  Render requests are
-     * requested through a call to the {@link PortletContainer#doEvent(org.apache.pluto.container.PortletWindow,
-     * javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.portlet.Event)}
-     * method.
-     */
-    Integer METHOD_EVENT = new Integer(9);
+        /**
+         * The unique method identifier for resource Serving requests.  Resource requests are
+         * requested through a call to the {@link PortletContainer#doServeResource(PortletWindow,
+         *  javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
+         * method.
+         */
+        RESOURCE,
 
-    /**
-     * The unique method identifier for admin requests.  Admin requests
-     * are requested through a call to the {@link PortletContainer#doAdmin(PortletWindow, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
-     * method.
-     */
-    Integer  METHOD_ADMIN = new Integer(11);
+        /**
+         * The unique method identifier for render requests.  Render requests are
+         * requested through a call to the {@link PortletContainer#doEvent(org.apache.pluto.container.PortletWindow,
+         * javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.portlet.Event)}
+         * method.
+         */
+        EVENT,
+
+        /**
+         * The unique method identifier for admin requests.  Admin requests
+         * are requested through a call to the {@link PortletContainer#doAdmin(PortletWindow, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
+         * method.
+         */
+        ADMIN
+    }
 
     /**
      * The public key, to store the FilterManager in the request.
diff --git a/pluto-container-driver-api/src/main/java/org/apache/pluto/container/driver/PlutoServices.java b/pluto-container-driver-api/src/main/java/org/apache/pluto/container/driver/PlutoServices.java
deleted file mode 100644
index dda84e1..0000000
--- a/pluto-container-driver-api/src/main/java/org/apache/pluto/container/driver/PlutoServices.java
+++ /dev/null
@@ -1,125 +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.pluto.container.driver;
-
-import javax.portlet.PortalContext;
-
-import org.apache.pluto.container.CCPPProfileService;
-import org.apache.pluto.container.EventCoordinationService;
-import org.apache.pluto.container.FilterManagerService;
-import org.apache.pluto.container.NamespaceMapper;
-import org.apache.pluto.container.PortletEnvironmentService;
-import org.apache.pluto.container.PortletInvokerService;
-import org.apache.pluto.container.PortletPreferencesService;
-import org.apache.pluto.container.PortletRequestContextService;
-import org.apache.pluto.container.PortletURLListenerService;
-import org.apache.pluto.container.RequestDispatcherService;
-import org.apache.pluto.container.UserInfoService;
-
-
-public class PlutoServices implements PortalDriverServices
-{
-    private PortalDriverServices driverServices;
-    
-    private static PlutoServices singleton;
-
-    public static PlutoServices getServices()
-    {
-        return singleton;
-    }
-    
-    public PlutoServices(PortalDriverServices driverServices)
-    {
-        singleton = this;
-        this.driverServices = driverServices;
-    }
-    
-    public CCPPProfileService getCCPPProfileService()
-    {
-        return driverServices.getCCPPProfileService();
-    }
-
-    public PortalContext getPortalContext()
-    {
-        return driverServices.getPortalContext();
-    }
-
-    public NamespaceMapper getNamespaceMapper()
-    {
-        return driverServices.getNamespaceMapper();
-    }
-
-    public PortalAdministrationService getPortalAdministrationService()
-    {
-        return driverServices.getPortalAdministrationService();
-    }
-
-    public PortletEnvironmentService getPortletEnvironmentService()
-    {
-        return driverServices.getPortletEnvironmentService();
-    }
-
-    public PortletInvokerService getPortletInvokerService()
-    {
-        return driverServices.getPortletInvokerService();
-    }
-
-    public PortletPreferencesService getPortletPreferencesService()
-    {
-        return driverServices.getPortletPreferencesService();
-    }
-
-    public PortletRegistryService getPortletRegistryService()
-    {
-        return driverServices.getPortletRegistryService();
-    }
-
-    public UserInfoService getUserInfoService()
-    {
-        return driverServices.getUserInfoService();
-    }
-    
-    public PortletContextService getPortletContextService()
-    {
-        return driverServices.getPortletContextService();
-    }
-
-    public PortletRequestContextService getPortletRequestContextService()
-    {
-        return driverServices.getPortletRequestContextService();
-    }
-
-    public EventCoordinationService getEventCoordinationService()
-    {
-        return driverServices.getEventCoordinationService();
-    }
-
-    public FilterManagerService getFilterManagerService()
-    {
-        return driverServices.getFilterManagerService();
-    }
-
-    public PortletURLListenerService getPortletURLListenerService()
-    {
-        return driverServices.getPortletURLListenerService();
-    }
-    
-    public RequestDispatcherService getRequestDispatcherService()
-    {
-        return driverServices.getRequestDispatcherService();
-    }
-}
diff --git a/pluto-container-driver-api/src/main/java/org/apache/pluto/container/driver/PlutoServicesRegistry.java b/pluto-container-driver-api/src/main/java/org/apache/pluto/container/driver/PlutoServicesRegistry.java
new file mode 100644
index 0000000..f1640ba
--- /dev/null
+++ b/pluto-container-driver-api/src/main/java/org/apache/pluto/container/driver/PlutoServicesRegistry.java
@@ -0,0 +1,168 @@
+/*
+ * 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.pluto.container.driver;
+
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+
+import org.apache.pluto.container.PortletContainerException;
+
+/**
+ * Static registry used by the {@link PortletServlet} and {@link PortalDriverServices} implementations to
+ * coordinate registration lifecycle
+ * 
+ * @author Eric Dalquist
+ */
+public final class PlutoServicesRegistry {
+    //Using a weak map keyed on ServletConfig, this should result in the map not causing memory leaks as applications are redpeloyed
+    private static final Map<ServletConfig, RegistrationData> REGISTERED_PORTLETS = new WeakHashMap<ServletConfig, RegistrationData>(); 
+    private static volatile PortalDriverServices portalDriverServices;
+    
+    /**
+     * Called by the portlet container when it initializes to register the {@link PortalDriverServices} implementation
+     */
+    public static void registerPortalDriverServices(PortalDriverServices portalDriverServices) {
+        if (portalDriverServices == null) {
+            throw new IllegalArgumentException("Cannot register a null PortalDriverServices");
+        }
+        
+        synchronized (REGISTERED_PORTLETS) {
+            if (PlutoServicesRegistry.portalDriverServices != null) {
+                throw new IllegalStateException("Cannot register PortalDriverServices, there is already one registered\n" +
+                		"existing: " + PlutoServicesRegistry.portalDriverServices + "\n" +
+        				"     new: " + portalDriverServices);
+            }
+            
+            PlutoServicesRegistry.portalDriverServices = portalDriverServices;
+            
+            //Just registered the driver services, notify it about all portlets that have already called register
+            if (PlutoServicesRegistry.portalDriverServices != null) {
+                final PortletContextService portletContextService = portalDriverServices.getPortletContextService();
+                
+                for (final Map.Entry<ServletConfig, RegistrationData> registeredPortletEntry : REGISTERED_PORTLETS.entrySet()) {
+                    final ServletConfig config = registeredPortletEntry.getKey();
+                    final RegistrationData registrationData = registeredPortletEntry.getValue();
+                    registerPortlet(portletContextService, config, registrationData);
+                }
+            }
+        }
+    }
+    
+    /**
+     * Called by the portlet container when it is destroyed to remove the {@link PortalDriverServices} registration
+     */
+    public static void unregisterPortalDriverServices(PortalDriverServices portalDriverServices) {
+        if (portalDriverServices == null) {
+            throw new IllegalArgumentException("Cannot unregister a null PortalDriverServices");
+        }
+        
+        synchronized (REGISTERED_PORTLETS) {
+            if (PlutoServicesRegistry.portalDriverServices != portalDriverServices) {
+                throw new IllegalStateException("Cannot unregister PortalDriverServices, the provided service does not match the existing registered service\n" +
+                        "existing: " + PlutoServicesRegistry.portalDriverServices + "\n" +
+                        "provided: " + portalDriverServices);
+            }
+            
+            //Just unregistered the driver services, notify each portlet by calling unregister
+            for (final Map.Entry<ServletConfig, RegistrationData> registeredPortletEntry : REGISTERED_PORTLETS.entrySet()) {
+                final RegistrationData registrationData = registeredPortletEntry.getValue();
+                
+                try {
+                    registrationData.lifecycleCallback.unregistered();
+                }
+                catch (Throwable t) {
+                    final ServletConfig config = registeredPortletEntry.getKey();
+                    config.getServletContext().log("Failed to unregister portlet " + config.getInitParameter("portlet-name") + " in " + config.getServletContext().getServletContextName(), t);
+                }
+            }
+            
+            //Clear the services reference
+            PlutoServicesRegistry.portalDriverServices = null;
+        }
+        
+    }
+    
+    /**
+     * Called by the {@link PortletServlet} when it initializes, notifies the portlet container that the portlet needs to be registered
+     * 
+     * @param config The {@link ServletConfig} for the {@link PortletServlet} that is being initialized
+     * @param classLoader The portlet application's {@link ClassLoader}
+     * @param lifecycleCallback The callback the portlet container should call when registering the portlet
+     */
+    public static void register(String portletName, ServletConfig config, ClassLoader classLoader, PortletServletLifecycleCallback lifecycleCallback) {
+        synchronized (REGISTERED_PORTLETS) {
+            final RegistrationData registrationData = new RegistrationData(portletName, classLoader, lifecycleCallback);
+            REGISTERED_PORTLETS.put(config, registrationData);
+            
+            //Driver services exist, notify them of the registration
+            if (portalDriverServices != null) {
+                final PortletContextService portletContextService = portalDriverServices.getPortletContextService();
+                registerPortlet(portletContextService, config, registrationData);
+            }
+        }
+    }
+    
+    /**
+     * Called by the {@link PortletServlet} when it is destroyed, notifies the portlet container that the portlet needs to be unregistered
+     * 
+     * @param config The {@link ServletConfig} for the {@link PortletServlet} that is being destroyed
+     * @param classLoader The portlet application's {@link ClassLoader}
+     * @param lifecycleCallback The callback the portlet container should call when unregistering the portlet
+     */
+    public static void unregister(ServletConfig config, DriverPortletContext portletAppContext, DriverPortletConfig portletContext, PortletServletLifecycleCallback lifecycleCallback) {
+        synchronized (REGISTERED_PORTLETS) {
+            REGISTERED_PORTLETS.remove(config);
+            
+            //Driver services exist, notify them of the de-registration
+            if (portalDriverServices != null) {
+                final PortletContextService portletContextService = portalDriverServices.getPortletContextService();
+                portletContextService.unregister(portletAppContext, portletContext, lifecycleCallback);
+            }
+            else {
+                //Run the unregistered callback immediately since there is no portal to notify
+                lifecycleCallback.unregistered();
+            }
+        }
+    }
+
+    private static void registerPortlet(final PortletContextService portletContextService, final ServletConfig config,
+            final RegistrationData registrationData) {
+
+        try {
+            portletContextService.register(registrationData.portletName, config, registrationData.classLoader, registrationData.lifecycleCallback);
+        }
+        catch (PortletContainerException e) {
+            final ServletContext context = config.getServletContext();
+            context.log(e.getMessage(), e);
+        }
+    }
+    
+    private static final class RegistrationData {
+        private final String portletName;
+        private final ClassLoader classLoader;
+        private final PortletServletLifecycleCallback lifecycleCallback;
+
+        public RegistrationData(String portletName, ClassLoader classLoader, PortletServletLifecycleCallback lifecycleCallback) {
+            this.portletName = portletName;
+            this.classLoader = classLoader;
+            this.lifecycleCallback = lifecycleCallback;
+        }
+    }
+}
diff --git a/pluto-container-driver-api/src/main/java/org/apache/pluto/container/driver/PortletContextService.java b/pluto-container-driver-api/src/main/java/org/apache/pluto/container/driver/PortletContextService.java
index 82d268c..d5a87a5 100644
--- a/pluto-container-driver-api/src/main/java/org/apache/pluto/container/driver/PortletContextService.java
+++ b/pluto-container-driver-api/src/main/java/org/apache/pluto/container/driver/PortletContextService.java
@@ -83,16 +83,17 @@
     /**
      * Register a portlet application
      * 
-     * @param config
-     * @return
+     * @param config the portlet's config
+     * @param classLoader the portlet's classloader
+     * @param registrationCallback A callback to execute when registration is complete
      * @throws PortletContainerException
      */
-    String register(ServletConfig config) throws PortletContainerException;
+    void register(String portletName, ServletConfig config, ClassLoader classLoader, PortletServletLifecycleCallback registrationCallback) throws PortletContainerException;
     
     /**
      * Unregister a portlet application
      * 
      * @param context
      */
-    void unregister(DriverPortletContext context);
+    void unregister(DriverPortletContext portletContext, DriverPortletConfig portletConfig, PortletServletLifecycleCallback lifecycleCallback);
 }
diff --git a/pluto-container-driver-api/src/main/java/org/apache/pluto/container/driver/PortletInvocationEvent.java b/pluto-container-driver-api/src/main/java/org/apache/pluto/container/driver/PortletInvocationEvent.java
index d701085..50114e2 100644
--- a/pluto-container-driver-api/src/main/java/org/apache/pluto/container/driver/PortletInvocationEvent.java
+++ b/pluto-container-driver-api/src/main/java/org/apache/pluto/container/driver/PortletInvocationEvent.java
@@ -18,27 +18,19 @@
 
 import javax.portlet.PortletRequest;
 
-import org.apache.pluto.container.PortletInvokerService;
+import org.apache.pluto.container.PortletInvokerService.Method;
 import org.apache.pluto.container.PortletWindow;
 
 
 public class PortletInvocationEvent {
 
-    public static int LOAD = PortletInvokerService.METHOD_LOAD.intValue();
+    private final PortletRequest portletRequest;
 
-    public static int ACTION = PortletInvokerService.METHOD_ACTION.intValue();
+    private final PortletWindow portletWindow;
 
-    public static int RENDER = PortletInvokerService.METHOD_RENDER.intValue();
+    private final Method invocationMethod;
 
-    public static int ADMIN = PortletInvokerService.METHOD_ADMIN.intValue();
-
-    private PortletRequest portletRequest;
-
-    private PortletWindow portletWindow;
-
-    private int invocationMethod;
-
-    public PortletInvocationEvent(PortletRequest portletRequest, PortletWindow portletWindow, int invocationMethod) {
+    public PortletInvocationEvent(PortletRequest portletRequest, PortletWindow portletWindow, Method invocationMethod) {
         this.portletRequest = portletRequest;
         this.portletWindow = portletWindow;
         this.invocationMethod = invocationMethod;
@@ -48,7 +40,7 @@
         return portletRequest;
     }
 
-    public int getInvocationMethod() {
+    public Method getInvocationMethod() {
         return invocationMethod;
     }
 
diff --git a/pluto-container-driver-api/src/main/java/org/apache/pluto/container/driver/PortletServlet.java b/pluto-container-driver-api/src/main/java/org/apache/pluto/container/driver/PortletServlet.java
index 3962977..b20cc56 100644
--- a/pluto-container-driver-api/src/main/java/org/apache/pluto/container/driver/PortletServlet.java
+++ b/pluto-container-driver-api/src/main/java/org/apache/pluto/container/driver/PortletServlet.java
@@ -17,8 +17,6 @@
 package org.apache.pluto.container.driver;
 
 import java.io.IOException;
-import java.util.Timer;
-import java.util.TimerTask;
 
 import javax.portlet.ActionRequest;
 import javax.portlet.ActionResponse;
@@ -44,7 +42,6 @@
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.pluto.container.FilterManager;
-import org.apache.pluto.container.PortletContainerException;
 import org.apache.pluto.container.PortletInvokerService;
 import org.apache.pluto.container.PortletRequestContext;
 import org.apache.pluto.container.PortletResponseContext;
@@ -58,10 +55,15 @@
  * @version 1.1
  * @since 09/22/2004
  */
-public class PortletServlet extends HttpServlet
+public class PortletServlet extends HttpServlet implements PortletServletLifecycleCallback
 {
     private static final long serialVersionUID = -5096339022539360365L;
+    private static final String CLASSLOADER_CONTEXT_KEY = PortletServlet.class.getName() + ".WEBAPP_CLASSLOADER";
+    private static final Object REGISTRATION_MUTEX = new Object(){};
 
+    /**
+     * Used to support portlets that don't handle event or resource requests
+     */
     static class NullPortlet implements EventPortlet, ResourceServingPortlet, Portlet
     {
         public void processEvent(EventRequest arg0, EventResponse arg1)
@@ -102,31 +104,30 @@
     /**
      * The portlet instance wrapped by this servlet.
      */
-    private Portlet portlet;
+    private volatile Portlet portlet;
+    /**
+     * The Event Portlet instance (the same object as portlet) wrapped by this servlet.
+     */
+    private volatile EventPortlet eventPortlet;
+    /** 
+     * The Resource Portlet instance (the same object as portlet) wrapped by this servlet.
+     */
+    private volatile ResourceServingPortlet resourceServingPortlet;
 
     /**
      * The internal portlet context instance.
      */
-    private DriverPortletContext portletContext;
-
+    private volatile DriverPortletContext portletAppContext;
     /**
      * The internal portlet config instance.
      */
-    private DriverPortletConfig portletConfig;
-
+    private volatile DriverPortletConfig portletContext;
     /**
-     * The Event Portlet instance (the same object as portlet) wrapped by this
-     * servlet.
+     * Reference to the portal provided services API 
      */
-    private EventPortlet eventPortlet;
+    private volatile PortalDriverContainerServices portalDriverServices;
 
-    /** The resource serving portlet instance wrapped by this servlet. */
-    private ResourceServingPortlet resourceServingPortlet;
-
-    private PortletContextService contextService;
-
-    private boolean started = false;
-    Timer   startTimer;
+    private volatile boolean started = false;
 
     // HttpServlet Impl --------------------------------------------------------
 
@@ -141,137 +142,133 @@
      * @throws ServletException
      *             if an error occurs while loading portlet.
      */
-    public void init(ServletConfig config) throws ServletException
+    public void init(final ServletConfig config) throws ServletException
     {
-
+        started = false;
+        
         // Call the super initialization method.
         super.init(config);
 
         // Retrieve portlet name as defined as an initialization parameter.
         portletName = getInitParameter("portlet-name");
-
-        started = false;
-
-        startTimer = new Timer(true);
-        final ServletContext servletContext = getServletContext();
+        
+        //Grab a reference to the portlet's classloader and store it in the servlet context
         final ClassLoader paClassLoader = Thread.currentThread().getContextClassLoader();
-        startTimer.schedule(new TimerTask()
-        {
-            public void run()
-            {
-                synchronized(servletContext)
-                {
-                    if (startTimer != null)
-                    {
-                        if (attemptRegistration(servletContext, paClassLoader ))
-                        {
-                            startTimer.cancel();
-                            startTimer = null;
-                        }
-                    }
-                }
-            }
-        }, 1, 10000);
+        final ServletContext servletContext = config.getServletContext();
+        servletContext.setAttribute(CLASSLOADER_CONTEXT_KEY, paClassLoader);
+        
+        PlutoServicesRegistry.register(portletName, config, paClassLoader, this);
     }
-
-    protected boolean attemptRegistration(ServletContext context, ClassLoader paClassLoader)
-    {
-        if (PlutoServices.getServices() != null)
-        {
-            contextService = PlutoServices.getServices().getPortletContextService();
-            try
-            {
-                ServletConfig sConfig = getServletConfig();
-                if (sConfig == null)
-                {
-                    String msg = "Problem obtaining servlet configuration(getServletConfig() returns null).";
-                    context.log(msg);
-                    return true;
+    
+    public void registered(PortalDriverContainerServices portalDriverServices, DriverPortletContext portletContext, DriverPortletConfig portletConfig) {
+        //sync is to make sure that modification of the portlet/portal references is atomic
+        synchronized (REGISTRATION_MUTEX) {
+            //Get references to pluto services and portlet configuration data
+            this.portalDriverServices = portalDriverServices;
+            this.portletAppContext = portletContext;
+            this.portletContext = portletConfig;
+    
+            final PortletDefinition portletDD = portletConfig.getPortletDefinition();
+            this.portlet = loadPortlet(portletDD.getPortletClass());
+            if (this.portlet != null) {
+                try {
+                    portlet.init(portletConfig);
+                    
+                    //Init event and resource portlet references
+                    initializeEventPortlet();
+                    initializeResourceServingPortlet();
                 }
-
-                String applicationName = contextService.register(sConfig);
-                started = true;
-                portletContext = contextService.getPortletContext(applicationName);
-                portletConfig = contextService.getPortletConfig(applicationName, portletName);
-
+                catch (PortletException e) {
+                    final ServletContext servletContext = this.getServletContext();
+                    servletContext.log("Failed to initialize portlet '" + this.portletName + "' in web application " + this.getServletContext().getServletContextName(), e);
+                    
+                    //failed to init portlet class, do cleanup
+                    unregistered();
+                }
             }
-            catch (PortletContainerException ex)
-            {
-                context.log(ex.getMessage(),ex);
-                return true;
+            else {
+                //failed to load portlet class, do cleanup
+                unregistered();
             }
-
-            PortletDefinition portletDD = portletConfig.getPortletDefinition();
-
-            //          Create and initialize the portlet wrapped in the servlet.
-            try
-            {
-                Class<?> clazz = paClassLoader.loadClass((portletDD.getPortletClass()));
-                portlet = (Portlet) clazz.newInstance();
-                portlet.init(portletConfig);
-                initializeEventPortlet();
-                initializeResourceServingPortlet();
-                return true;
-            }
-            catch (Exception ex)
-            {
-                context.log(ex.getMessage(),ex);
-                // take out of service
-                portlet = null;
-                portletConfig = null;
-                return true;
-            }
+    
+            //Mark the portlet as started
+            this.started = true;
         }
-        return false;
+    }
+    
+    protected Portlet loadPortlet(String portletClass) {
+        final ServletContext servletContext = this.getServletContext();
+        final ClassLoader paClassLoader = (ClassLoader)servletContext.getAttribute(CLASSLOADER_CONTEXT_KEY);
+        final Class<?> clazz;
+        try {
+            clazz = paClassLoader.loadClass(portletClass);
+        }
+        catch (ClassNotFoundException e) {
+            servletContext.log("Failed to load portlet-class '" + portletClass + "' for " + getPortletNameForLogging(), e);
+            return null;
+        }
+        
+        try {
+            return (Portlet) clazz.newInstance();
+        }
+        catch (InstantiationException e) {
+            servletContext.log("Failed to instantiate portlet-class '" + portletClass + "' for " + getPortletNameForLogging(), e);
+            return null;
+        }
+        catch (IllegalAccessException e) {
+            servletContext.log("Failed to instantiate portlet-class '" + portletClass + "' for " + getPortletNameForLogging(), e);
+            return null;
+        }
     }
 
     public void destroy()
     {
-        synchronized(getServletContext())
-        {
-            if ( startTimer != null )
-            {
-                startTimer.cancel();
-                startTimer = null;
+        this.started = false;
+        
+        final ServletConfig servletConfig = this.getServletConfig();
+        PlutoServicesRegistry.unregister(servletConfig, this.portletAppContext, this.portletContext, this);
+    }
+    
+    public void unregistered() {
+        //sync is to make sure that modification of the portlet/portal references is atomic
+        synchronized (REGISTRATION_MUTEX) {
+            this.started = false;
+            
+            try {
+                portlet.destroy();
             }
-            else if ( started && portletContext != null)
-            {
-                started = false;
-                contextService.unregister(portletContext);
-                if (portlet != null)
-                {
-                    try
-                    {
-                        portlet.destroy();
-                    }
-                    catch (Exception e)
-                    {
-                        // ignore
-                    }
-                    portlet = null;
-                }
+            catch (Throwable th) {
+                // Don't care for Exception
+                this.getServletContext().log("Error destroying " + getPortletNameForLogging(), th);
             }
+            
+            this.portalDriverServices = null;
+            this.portletAppContext = null;
+            this.portletContext = null;
+            this.portlet = null;
+            this.resourceServingPortlet = null;
+            this.eventPortlet = null;
+            
             super.destroy();
         }
     }
+    
+    protected String getPortletNameForLogging() {
+        return "portlet '" + this.portletName + "' in web application " + this.getServletContext().getServletContextName();
+    }
 
-    protected void doGet(HttpServletRequest request,
-            HttpServletResponse response) throws ServletException, IOException
-            {
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         dispatch(request, response);
-            }
+    }
 
-    protected void doPost(HttpServletRequest request,
-            HttpServletResponse response) throws ServletException, IOException
-            {
+    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
+            IOException {
         dispatch(request, response);
-            }
+    }
 
-    protected void doPut(HttpServletRequest request,
-            HttpServletResponse response) throws ServletException, IOException
-            {
+    protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         dispatch(request, response);
-            }
+    }
 
     // Private Methods ---------------------------------------------------------
 
@@ -295,13 +292,15 @@
     private void dispatch(HttpServletRequest request,
             HttpServletResponse response) throws ServletException, IOException
             {
-        if (portlet == null)
-        {
-            throw new javax.servlet.UnavailableException("Portlet "+portletName+" unavailable");
+        if (!started) {
+            throw new javax.servlet.UnavailableException(getPortletNameForLogging() + " has not been initialized", 5);
+        }
+        if (portlet == null) {
+            throw new javax.servlet.UnavailableException(getPortletNameForLogging() + " is unavailable, there was a problem during initialization check the container logs for more details");
         }
 
         // Retrieve attributes from the servlet request.
-        Integer methodId = (Integer) request.getAttribute(PortletInvokerService.METHOD_ID);
+        PortletInvokerService.Method method = (PortletInvokerService.Method) request.getAttribute(PortletInvokerService.METHOD_ID);
 
         final PortletRequest portletRequest = (PortletRequest)request.getAttribute(PortletInvokerService.PORTLET_REQUEST);
 
@@ -317,12 +316,12 @@
         request.removeAttribute(PortletInvokerService.PORTLET_RESPONSE);
         request.removeAttribute(PortletInvokerService.FILTER_MANAGER);
 
-        requestContext.init(portletConfig, getServletContext(), request, response);
+        requestContext.init(portletContext, getServletContext(), request, response);
         responseContext.init(request, response);
 
         PortletWindow window = requestContext.getPortletWindow();
 
-        PortletInvocationEvent event = new PortletInvocationEvent(portletRequest, window, methodId.intValue());
+        PortletInvocationEvent event = new PortletInvocationEvent(portletRequest, window, method);
 
         notify(event, true, null);
 
@@ -331,100 +330,87 @@
 
         try
         {
-
-            // The requested method is RENDER: call Portlet.render(..)
-            if (methodId == PortletInvokerService.METHOD_RENDER)
-            {
-                RenderRequest renderRequest = (RenderRequest) portletRequest;
-                RenderResponse renderResponse = (RenderResponse) portletResponse;
-                filterManager.processFilter(renderRequest, renderResponse,
-                        portlet, portletContext);
-            }
-
-            // The requested method is RESOURCE: call
-            // ResourceServingPortlet.serveResource(..)
-            else if (methodId == PortletInvokerService.METHOD_RESOURCE)
-            {
-                ResourceRequest resourceRequest = (ResourceRequest) portletRequest;
-                ResourceResponse resourceResponse = (ResourceResponse) portletResponse;
-                filterManager.processFilter(resourceRequest, resourceResponse,
-                        resourceServingPortlet, portletContext);
-            }
-
-            // The requested method is ACTION: call Portlet.processAction(..)
-            else if (methodId == PortletInvokerService.METHOD_ACTION)
-            {
-                ActionRequest actionRequest = (ActionRequest) portletRequest;
-                ActionResponse actionResponse = (ActionResponse) portletResponse;
-                filterManager.processFilter(actionRequest, actionResponse,
-                        portlet, portletContext);
-            }
-
-            // The request methode is Event: call Portlet.processEvent(..)
-            else if (methodId == PortletInvokerService.METHOD_EVENT)
-            {
-                EventRequest eventRequest = (EventRequest) portletRequest;
-                EventResponse eventResponse = (EventResponse) portletResponse;
-                filterManager.processFilter(eventRequest, eventResponse,
-                        eventPortlet, portletContext);
-            }
-            // The requested method is ADMIN: call handlers.
-            else if (methodId == PortletInvokerService.METHOD_ADMIN)
-            {
-                PortalAdministrationService pas = PlutoServices.getServices().getPortalAdministrationService();
-
-                for (AdministrativeRequestListener l : pas.getAdministrativeRequestListeners())
-                {
-                    l.administer(portletRequest, portletResponse);
+            switch (method) {
+                // The requested method is RENDER: call Portlet.render(..)
+                case RENDER: {
+                    RenderRequest renderRequest = (RenderRequest) portletRequest;
+                    RenderResponse renderResponse = (RenderResponse) portletResponse;
+                    filterManager.processFilter(renderRequest, renderResponse, portlet, portletAppContext);
+                    break;
                 }
-            }
+                // The requested method is RESOURCE: call
+                // ResourceServingPortlet.serveResource(..)
+                case RESOURCE: {
+                    ResourceRequest resourceRequest = (ResourceRequest) portletRequest;
+                    ResourceResponse resourceResponse = (ResourceResponse) portletResponse;
+                    filterManager.processFilter(resourceRequest, resourceResponse,
+                            resourceServingPortlet, portletAppContext);                    
+                    break;
+                }
+                // The requested method is ACTION: call Portlet.processAction(..)
+                case ACTION: {
+                    ActionRequest actionRequest = (ActionRequest) portletRequest;
+                    ActionResponse actionResponse = (ActionResponse) portletResponse;
+                    filterManager.processFilter(actionRequest, actionResponse,
+                            portlet, portletAppContext);
+                    break;
+                }
+                // The request methode is Event: call Portlet.processEvent(..)
+                case EVENT: {
+                    EventRequest eventRequest = (EventRequest) portletRequest;
+                    EventResponse eventResponse = (EventResponse) portletResponse;
+                    filterManager.processFilter(eventRequest, eventResponse,
+                            eventPortlet, portletAppContext);
+                    break;
+                }
+                // The requested method is ADMIN: call handlers.
+                case ADMIN: {
+                    PortalAdministrationService pas = this.portalDriverServices.getPortalAdministrationService();
 
-            // The requested method is LOAD: do nothing.
-            else if (methodId == PortletInvokerService.METHOD_LOAD)
-            {
-                // Do nothing.
+                    for (AdministrativeRequestListener l : pas.getAdministrativeRequestListeners())
+                    {
+                        l.administer(portletRequest, portletResponse);
+                    }
+                    break;
+                }
+                // The requested method is LOAD: do nothing.
+                case LOAD: {
+                    // Nothing to do
+                }
             }
 
             notify(event, false, null);
 
         }
-        catch (UnavailableException ex)
+        catch (UnavailableException e)
         {
-            //
-            // if (e.isPermanent()) { throw new
-            // UnavailableException(e.getMessage()); } else { throw new
-            // UnavailableException(e.getMessage(), e.getUnavailableSeconds());
-            // }
-            //
-
-            // Portlet.destroy() isn't called by Tomcat, so we have to fix it.
-            try
-            {
-                portlet.destroy();
+            if (e.isPermanent()) {
+                this.destroy();
+                this.getServletContext().log(getPortletNameForLogging() + " threw a permanent UnavailableException, taking the portlet out of service", e);
+                final javax.servlet.UnavailableException se = new javax.servlet.UnavailableException(e.getMessage());
+                se.initCause(e); //make sure we don't lose the original stack trace
+                throw se;
             }
-            catch (Throwable th)
-            {
-                // Don't care for Exception
-                this.getServletContext().log("Error during portlet destroy.", th);
-            }
-            // take portlet out of service
-            portlet = null;
-
-            // TODO: Handle everything as permanently for now.
-            throw new javax.servlet.UnavailableException(ex.getMessage());
+            
+            //not permanent, mark the portlet unavailable for the specified time period
+            
+            //Note when the unavailable time ends
+            final javax.servlet.UnavailableException se = new javax.servlet.UnavailableException(e.getMessage(), e.getUnavailableSeconds());
+            se.initCause(e); //make sure we don't lose the original stack trace
+            throw se;
 
         }
         catch (PortletException ex)
         {
             notify(event, false, ex);
-            throw new ServletException(ex);
+            throw new ServletException(getPortletNameForLogging() + " threw an exception when processing a " + method + " request", ex);
 
         }
-            }
+    }
 
     protected void notify(PortletInvocationEvent event, boolean pre, Throwable e)
     {
-        PortalAdministrationService pas = PlutoServices.getServices().getPortalAdministrationService();
+        PortalAdministrationService pas = this.portalDriverServices.getPortalAdministrationService();
 
         for (PortletInvocationListener listener : pas.getPortletInvocationListeners())
         {
@@ -449,10 +435,14 @@
         {
             eventPortlet = (EventPortlet) portlet;
         }
-        else
+        else if (portlet != null)
         {
             eventPortlet = new NullPortlet();
         }
+        else
+        {
+            eventPortlet = null;
+        }
     }
 
     private void initializeResourceServingPortlet()
@@ -461,9 +451,13 @@
         {
             resourceServingPortlet = (ResourceServingPortlet) portlet;
         }
-        else
+        else if (portlet != null)
         {
             resourceServingPortlet = new NullPortlet();
         }
+        else 
+        {
+            resourceServingPortlet = null;
+        }
     }
 }
diff --git a/pluto-container-driver-api/src/main/java/org/apache/pluto/container/driver/PortletServletLifecycleCallback.java b/pluto-container-driver-api/src/main/java/org/apache/pluto/container/driver/PortletServletLifecycleCallback.java
new file mode 100644
index 0000000..208f738
--- /dev/null
+++ b/pluto-container-driver-api/src/main/java/org/apache/pluto/container/driver/PortletServletLifecycleCallback.java
@@ -0,0 +1,38 @@
+/*
+ * 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.pluto.container.driver;
+
+/**
+ * Used to inform a {@link PortletServlet} that registration has been completed
+ * 
+ * @author Eric Dalquist
+ */
+public interface PortletServletLifecycleCallback {
+    /**
+     * Called after the container has completely registered the portlet. This may be called in a different thread than called
+     * {@link PlutoServicesRegistry#register(javax.servlet.ServletConfig, ClassLoader, PortletRegistrationCallback)} meaning
+     * implementations and data structures they modify need to be thread-safe
+     */
+    void registered(PortalDriverContainerServices portalDriverServices, DriverPortletContext portletContext, DriverPortletConfig portletConfig);
+    
+    /**
+     * Called after the container has completely registered the portlet. This may be called in a different thread than called
+     * {@link PlutoServicesRegistry#unregister(javax.servlet.ServletConfig, DriverPortletContext)} meaning implementations and
+     * data structures they modify need to be thread-safe
+     */
+    void unregistered();
+}
diff --git a/pluto-portal-driver-impl/src/main/java/org/apache/pluto/driver/services/container/PortletStateAwareResponseContextImpl.java b/pluto-portal-driver-impl/src/main/java/org/apache/pluto/driver/services/container/PortletStateAwareResponseContextImpl.java
index 1be78a3..abc67e2 100644
--- a/pluto-portal-driver-impl/src/main/java/org/apache/pluto/driver/services/container/PortletStateAwareResponseContextImpl.java
+++ b/pluto-portal-driver-impl/src/main/java/org/apache/pluto/driver/services/container/PortletStateAwareResponseContextImpl.java
@@ -31,7 +31,7 @@
 import org.apache.pluto.container.PortletStateAwareResponseContext;
 import org.apache.pluto.container.PortletURLProvider;
 import org.apache.pluto.container.PortletWindow;
-import org.apache.pluto.container.driver.PlutoServices;
+import org.apache.pluto.container.driver.PortalDriverServices;
 import org.apache.pluto.container.impl.PortletURLImpl;
 import org.apache.pluto.driver.core.PortalRequestContext;
 import org.apache.pluto.driver.url.PortalURL;
@@ -129,6 +129,7 @@
 
     public EventProvider getEventProvider()
     {
-        return isClosed() ? null : new EventProviderImpl(getPortletWindow(), PlutoServices.getServices().getPortletRegistryService());
+        //TODO fix method for getting reference to PortletRegistryService
+        return isClosed() ? null : new EventProviderImpl(getPortletWindow(), ((PortalDriverServices)super.getContainer().getContainerServices()).getPortletRegistryService());
     }
 }
diff --git a/pluto-portal-driver/src/main/java/org/apache/pluto/driver/container/DefaultOptionalContainerServices.java b/pluto-portal-driver/src/main/java/org/apache/pluto/driver/container/DefaultOptionalContainerServices.java
index 83331e9..0419721 100644
--- a/pluto-portal-driver/src/main/java/org/apache/pluto/driver/container/DefaultOptionalContainerServices.java
+++ b/pluto-portal-driver/src/main/java/org/apache/pluto/driver/container/DefaultOptionalContainerServices.java
@@ -27,6 +27,7 @@
 import org.apache.pluto.container.driver.OptionalContainerServices;
 import org.apache.pluto.container.driver.PortalAdministrationService;
 import org.apache.pluto.container.driver.PortalDriverContainerServices;
+import org.apache.pluto.container.driver.PortalDriverServices;
 import org.apache.pluto.container.driver.PortletContextService;
 import org.apache.pluto.container.driver.PortletRegistryService;
 import org.apache.pluto.container.impl.PortletEnvironmentServiceImpl;
@@ -57,10 +58,10 @@
      * Constructs an instance using the default portlet preferences service
      * implementation.
      */
-    public DefaultOptionalContainerServices() {
+    public DefaultOptionalContainerServices(PortalDriverServices portalDriverServices) {
         rdService = new RequestDispatcherServiceImpl();
         portletPreferencesService = new DefaultPortletPreferencesService();
-        portletRegistryService = new PortletContextManager(rdService, new PortletAppDescriptorServiceImpl());
+        portletRegistryService = new PortletContextManager(portalDriverServices, new PortletAppDescriptorServiceImpl());
         portletContextService = (PortletContextManager)portletRegistryService;
         portletInvokerService = new DefaultPortletInvokerService(portletContextService);
         portletEnvironmentService = new PortletEnvironmentServiceImpl();
@@ -77,8 +78,8 @@
      * be used. Otherwise, the default portlet preferences service will be used.
      * @param root  the root optional container services implementation.
      */
-    public DefaultOptionalContainerServices(OptionalContainerServices root, PortalDriverContainerServices driverServices) {
-        this();
+    public DefaultOptionalContainerServices(OptionalContainerServices root, PortalDriverServices driverServices) {
+        this(driverServices);
         if (root.getPortletPreferencesService() != null) {
             portletPreferencesService = root.getPortletPreferencesService();
         }
diff --git a/pluto-portal-driver/src/main/java/org/apache/pluto/driver/container/DefaultPortletInvokerService.java b/pluto-portal-driver/src/main/java/org/apache/pluto/driver/container/DefaultPortletInvokerService.java
index 44d7619..c14fa74 100644
--- a/pluto-portal-driver/src/main/java/org/apache/pluto/driver/container/DefaultPortletInvokerService.java
+++ b/pluto-portal-driver/src/main/java/org/apache/pluto/driver/container/DefaultPortletInvokerService.java
@@ -34,8 +34,6 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.apache.pluto.container.FilterManager;
 import org.apache.pluto.container.PortletContainerException;
 import org.apache.pluto.container.PortletInvokerService;
@@ -44,6 +42,8 @@
 import org.apache.pluto.container.driver.PortletContextService;
 import org.apache.pluto.container.driver.PortletServlet;
 import org.apache.pluto.container.util.StringManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Used internally to invoke/dispatch requests from the container to
@@ -98,7 +98,7 @@
         if (LOG.isDebugEnabled()) {
             LOG.debug("Performing Action Invocation");
         }
-        invoke(context, request, response, filterManager, PortletInvokerService.METHOD_ACTION);
+        invoke(context, request, response, filterManager, Method.ACTION);
     }
 
     /**
@@ -114,7 +114,7 @@
         if (LOG.isDebugEnabled()) {
             LOG.debug("Performing Render Invocation");
         }
-        invoke(context, request, response, filterManager, PortletInvokerService.METHOD_RENDER);
+        invoke(context, request, response, filterManager, Method.RENDER);
     }
 
     /**
@@ -130,7 +130,7 @@
         if (LOG.isDebugEnabled()) {
             LOG.debug("Performing Render Invocation");
         }
-        invoke(context, request, response, filterManager, PortletInvokerService.METHOD_EVENT);
+        invoke(context, request, response, filterManager, Method.EVENT);
     }
 
     /**
@@ -146,7 +146,7 @@
         if (LOG.isDebugEnabled()) {
             LOG.debug("Performing Resource Invocation");
         }
-        invoke(context, request, response, filterManager, PortletInvokerService.METHOD_RESOURCE);
+        invoke(context, request, response, filterManager, Method.RESOURCE);
     }
 
     /**
@@ -161,7 +161,7 @@
         if (LOG.isDebugEnabled()) {
             LOG.debug("Performing Load Invocation.");
         }
-        invoke(context, request, response, PortletInvokerService.METHOD_LOAD);
+        invoke(context, request, response, Method.LOAD);
     }
 
     public void admin(PortletRequestContext context, PortletRequest request, PortletResponse response)
@@ -170,7 +170,7 @@
             LOG.debug("Performing Admin Invocation.");
         }
 
-        invoke(context, request, response, PortletInvokerService.METHOD_ADMIN);
+        invoke(context, request, response, Method.ADMIN);
     }
 
     // Private Invoke Method ---------------------------------------------------
@@ -178,10 +178,10 @@
     private final void invoke(PortletRequestContext context,
             PortletRequest request,
             PortletResponse response,
-            Integer methodID)
+            Method method)
     throws PortletException, IOException, PortletContainerException {
 
-        invoke(context, request, response, null, methodID);
+        invoke(context, request, response, null, method);
     }
 
     /**
@@ -198,7 +198,7 @@
             PortletRequest request,
             PortletResponse response,
             FilterManager filterManager,
-            Integer methodID)
+            Method method)
     throws PortletException, IOException, PortletContainerException {
 
         PortletWindow portletWindow = context.getPortletWindow();
@@ -219,20 +219,20 @@
 
             try {
 
-                containerRequest.setAttribute(PortletInvokerService.METHOD_ID, methodID);
+                containerRequest.setAttribute(PortletInvokerService.METHOD_ID, method);
                 containerRequest.setAttribute(PortletInvokerService.PORTLET_REQUEST, request);
                 containerRequest.setAttribute(PortletInvokerService.PORTLET_RESPONSE, response);
                 containerRequest.setAttribute(PortletInvokerService.FILTER_MANAGER, filterManager);
 
-                if (methodID.equals(PortletInvokerService.METHOD_RESOURCE))
-                {
-                    dispatcher.forward(containerRequest, containerResponse);
+                switch (method) {
+                    case RESOURCE: {
+                        dispatcher.forward(containerRequest, containerResponse);
+                        break;
+                    }
+                    default: {
+                        dispatcher.include(containerRequest, containerResponse);
+                    }
                 }
-                else
-                {
-                    dispatcher.include(containerRequest, containerResponse);
-                }
-
 
             } catch (javax.servlet.UnavailableException ex) {
                 int seconds = ex.isPermanent() ? -1 : ex.getUnavailableSeconds();
diff --git a/pluto-portal-driver/src/main/java/org/apache/pluto/driver/container/PlutoServicesRegistryBean.java b/pluto-portal-driver/src/main/java/org/apache/pluto/driver/container/PlutoServicesRegistryBean.java
new file mode 100644
index 0000000..c5adafb
--- /dev/null
+++ b/pluto-portal-driver/src/main/java/org/apache/pluto/driver/container/PlutoServicesRegistryBean.java
@@ -0,0 +1,22 @@
+package org.apache.pluto.driver.container;
+
+import org.apache.pluto.container.driver.PlutoServicesRegistry;
+import org.apache.pluto.container.driver.PortalDriverServices;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+
+public class PlutoServicesRegistryBean implements InitializingBean, DisposableBean {
+    private PortalDriverServices portalDriverServices;
+
+    public PlutoServicesRegistryBean(PortalDriverServices portalDriverServices) {
+        this.portalDriverServices = portalDriverServices;
+    }
+
+    public void afterPropertiesSet() throws Exception {
+        PlutoServicesRegistry.registerPortalDriverServices(portalDriverServices);
+    }
+
+    public void destroy() throws Exception {
+        PlutoServicesRegistry.unregisterPortalDriverServices(portalDriverServices);        
+    }
+}
diff --git a/pluto-portal-driver/src/main/java/org/apache/pluto/driver/container/PortalDriverServicesImpl.java b/pluto-portal-driver/src/main/java/org/apache/pluto/driver/container/PortalDriverServicesImpl.java
index b569191..594261c 100644
--- a/pluto-portal-driver/src/main/java/org/apache/pluto/driver/container/PortalDriverServicesImpl.java
+++ b/pluto-portal-driver/src/main/java/org/apache/pluto/driver/container/PortalDriverServicesImpl.java
@@ -35,9 +35,9 @@
 import org.apache.pluto.container.driver.PortletContextService;
 import org.apache.pluto.container.driver.PortletRegistryService;
 import org.apache.pluto.container.driver.RequiredContainerServices;
+import org.apache.pluto.container.impl.PortletAppDescriptorServiceImpl;
 import org.apache.pluto.container.impl.PortletEnvironmentServiceImpl;
 import org.apache.pluto.container.impl.RequestDispatcherServiceImpl;
-import org.apache.pluto.container.impl.PortletAppDescriptorServiceImpl;
 
 
 public class PortalDriverServicesImpl implements RequiredContainerServices, OptionalContainerServices, PortalDriverServices
@@ -166,7 +166,7 @@
     protected void createDefaultServicesIfNeeded()
     {
         rdService = rdService == null ? new RequestDispatcherServiceImpl() : rdService;
-        portletRegistryService = portletRegistryService == null ? new PortletContextManager(rdService, new PortletAppDescriptorServiceImpl()) : portletRegistryService;
+        portletRegistryService = portletRegistryService == null ? new PortletContextManager(this, new PortletAppDescriptorServiceImpl()) : portletRegistryService;
         portletContextService = portletContextService == null ? (PortletContextManager)portletRegistryService : portletContextService;
         portalAdministrationService = portalAdministrationService == null ? new DefaultPortalAdministrationService() : portalAdministrationService;
         ccppProfileService = ccppProfileService == null ? new DummyCCPPProfileServiceImpl() : ccppProfileService;
diff --git a/pluto-portal-driver/src/main/java/org/apache/pluto/driver/container/PortletContextManager.java b/pluto-portal-driver/src/main/java/org/apache/pluto/driver/container/PortletContextManager.java
index 9e6cf05..5b893d6 100644
--- a/pluto-portal-driver/src/main/java/org/apache/pluto/driver/container/PortletContextManager.java
+++ b/pluto-portal-driver/src/main/java/org/apache/pluto/driver/container/PortletContextManager.java
@@ -28,21 +28,23 @@
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletContext;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.apache.pluto.container.PortletAppDescriptorService;
 import org.apache.pluto.container.PortletContainerException;
 import org.apache.pluto.container.PortletWindow;
 import org.apache.pluto.container.RequestDispatcherService;
-import org.apache.pluto.container.PortletAppDescriptorService;
 import org.apache.pluto.container.driver.DriverPortletConfig;
 import org.apache.pluto.container.driver.DriverPortletContext;
+import org.apache.pluto.container.driver.PortalDriverServices;
 import org.apache.pluto.container.driver.PortletContextService;
 import org.apache.pluto.container.driver.PortletRegistryEvent;
 import org.apache.pluto.container.driver.PortletRegistryListener;
 import org.apache.pluto.container.driver.PortletRegistryService;
+import org.apache.pluto.container.driver.PortletServletLifecycleCallback;
 import org.apache.pluto.container.om.portlet.PortletApplicationDefinition;
 import org.apache.pluto.container.om.portlet.PortletDefinition;
 import org.apache.pluto.container.util.ClasspathScanner;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Manager used to cache the portlet configurations which have
@@ -59,16 +61,16 @@
     private static final Logger LOG = LoggerFactory.getLogger(PortletContextManager.class);
 
     /**
-     * The PortletContext cache map: key is servlet context, and value is the
-     * associated portlet context.
-     */
-    private Map<String,DriverPortletContext> portletContexts = new HashMap<String,DriverPortletContext>();
-
-    /**
      * List of application id resolvers. *
      */
     private static final List<ApplicationIdResolver> APP_ID_RESOLVERS = new ArrayList<ApplicationIdResolver>();
 
+    /**
+     * The PortletContext cache map: key is servlet context, and value is the
+     * associated portlet context.
+     */
+    private final Map<String,DriverPortletContextHolder> portletContexts = new HashMap<String,DriverPortletContextHolder>();
+
 
     // Private Member Variables ------------------------------------------------
 
@@ -90,18 +92,27 @@
      */
     private final Map<String,ClassLoader> classLoaders = new HashMap<String,ClassLoader>();
     
-    private final RequestDispatcherService rdService;
+    private final PortalDriverServices portalDriverServices;
     private final PortletDescriptorRegistry portletRegistry;
 
     // Constructor -------------------------------------------------------------
 
-    public PortletContextManager(RequestDispatcherService rdService, PortletAppDescriptorService portletAppDescriptorService) {
-        this.rdService = rdService;
+    public PortletContextManager(PortalDriverServices portalDriverServices, PortletAppDescriptorService portletAppDescriptorService) {
+        this.portalDriverServices = portalDriverServices;
         portletRegistry = new PortletDescriptorRegistry(portletAppDescriptorService);
     }
 
 
     // Public Methods ----------------------------------------------------------
+    
+    private static final class DriverPortletContextHolder {
+        private final DriverPortletContext portletAppContext;
+        private int portletCount = 0;
+        
+        public DriverPortletContextHolder(DriverPortletContext portletAppContext) {
+            this.portletAppContext = portletAppContext;
+        }
+    }
 
     /**
      * Retrieves the PortletContext associated with the given ServletContext.
@@ -111,58 +122,93 @@
      * @return the InternalPortletContext associated with the ServletContext.
      * @throws PortletContainerException
      */
-	public String register(ServletConfig config) throws PortletContainerException {
-	    ServletContext servletContext = config.getServletContext();
-	    String contextPath = getContextPath(servletContext);
-        if (!portletContexts.containsKey(contextPath)) {
+    public synchronized void register(String portletName, ServletConfig config, ClassLoader classLoader,
+            PortletServletLifecycleCallback registrationCallback) throws PortletContainerException {
+        
+	    final ServletContext servletContext = config.getServletContext();
+	    final String contextPath = getContextPath(servletContext);
+	    
+	    //Get or register the portlet application
+	    DriverPortletContextHolder portletAppContextHolder = portletContexts.get(contextPath);
+        if (portletAppContextHolder == null) {
+            final PortletApplicationDefinition portletApp = portletRegistry.getPortletAppDD(servletContext, contextPath, contextPath);
+            final RequestDispatcherService requestDispatcherService = this.portalDriverServices.getRequestDispatcherService();
+            final DriverPortletContextImpl portletAppContext = new DriverPortletContextImpl(servletContext, portletApp, requestDispatcherService);
+            
+            //Verify the app loaded correctly
+            final String appName = portletAppContext.getApplicationName();
+            if (appName == null) {
+                throw new PortletContainerException(
+                        "Portlet application name was null when trying to register portlet '" + portletName
+                                + "' from webapp " + contextPath);
+            }
+            
+            portletAppContextHolder = new DriverPortletContextHolder(portletAppContext);
 
-            PortletApplicationDefinition portletApp = portletRegistry.getPortletAppDD(servletContext, contextPath, contextPath);
-
-            DriverPortletContext portletContext = new DriverPortletContextImpl(servletContext, portletApp, rdService);
-
-            portletContexts.put(contextPath, portletContext);
-
-            fireRegistered(portletContext);
+            portletContexts.put(contextPath, portletAppContextHolder);
+            classLoaders.put(portletApp.getName(), classLoader);
+            
+            fireRegistered(portletAppContext);
 
             if (LOG.isInfoEnabled()) {
                 LOG.info("Registered portlet application for context '" + contextPath + "'");
-
-                LOG.info("Registering "+portletApp.getPortlets().size()+" portlets for context /"+portletContext.getApplicationName());
-            }
-
-            classLoaders.put(portletApp.getName(), Thread.currentThread().getContextClassLoader());
-            for (PortletDefinition portlet: portletApp.getPortlets()) {
-                String appName = portletContext.getApplicationName();
-                if (appName == null) {
-                    throw new PortletContainerException("Portlet application name should not be null.");
-                }
-                portletConfigs.put(
-                    portletContext.getApplicationName() + "/" + portlet.getPortletName(),
-                    new DriverPortletConfigImpl(portletContext, portlet)
-                );
-            }
-        } else {
-             if (LOG.isInfoEnabled()) {
-                LOG.info("Portlet application for context '" + contextPath + "' already registered.");
             }
         }
-        return contextPath;
+        
+        
+        //register the portlet
+        final DriverPortletContext portletAppContext = portletAppContextHolder.portletAppContext;
+        final PortletApplicationDefinition portletApp = portletAppContext.getPortletApplicationDefinition();
+        
+        if (LOG.isInfoEnabled()) {
+            LOG.info("Portlet application for context '" + contextPath
+                    + "' already registered, incrementing portlet count");
+        }
+        
+        final PortletDefinition portlet = portletApp.getPortlet(portletName);
+
+        final DriverPortletConfigImpl portletContext = new DriverPortletConfigImpl(portletAppContext, portlet);
+        final String portletAppKey = portletAppContext.getApplicationName() + "/" + portlet.getPortletName();
+        portletConfigs.put(portletAppKey, portletContext);
+
+        //Notify the portlet it has been registered
+        try {
+            registrationCallback.registered(portalDriverServices, portletAppContext, portletContext);
+            portletAppContextHolder.portletCount++;
+        }
+        catch (Throwable t) {
+            //registration failed, cleanup
+            portletConfigs.remove(portletAppKey);
+            
+            throw new PortletContainerException("Failed to register portlet " + portlet.getPortletName() + " from context " + portletAppContext.getApplicationName(), t);
+        }
+
+        //TODO fireRegistered(portletContext);
+
+        if (LOG.isInfoEnabled()) {
+            LOG.info("Registered portlet " + portlet.getPortletName() + " from context " + portletAppContext.getApplicationName() + " which now has " + portletAppContextHolder.portletCount + " registered portlets");
+        }
     }
 
-    /**
-     * @see org.apache.pluto.container.driver.PortletContextService#unregister(org.apache.pluto.container.driver.DriverPortletContext)
-     */
-    public void unregister(DriverPortletContext context) {
-        portletContexts.remove(context.getApplicationName());
-        classLoaders.remove(context.getApplicationName());
-        Iterator<String> configs = portletConfigs.keySet().iterator();
-        while (configs.hasNext()) {
-            String key = configs.next();
-            if (key.startsWith(context.getApplicationName() + "/")) {
-                configs.remove();
+
+    public synchronized void unregister(DriverPortletContext portletAppContext, DriverPortletConfig portletContext, PortletServletLifecycleCallback lifecycleCallback) {
+        lifecycleCallback.unregistered();
+        
+        final String portletAppKey = portletAppContext.getApplicationName() + "/" + portletContext.getPortletName();
+        
+        if (portletConfigs.remove(portletAppKey) != null) {
+            //only bother with unregistration work if the was successfully registered in the first place
+            
+            //TODO fireRemoved(portletAppContext);
+            
+            final DriverPortletContextHolder driverPortletContextHolder = portletContexts.get(portletAppContext.getApplicationName());
+            driverPortletContextHolder.portletCount--;
+            
+            //Last portlet unregistered, remove the app
+            if (driverPortletContextHolder.portletCount == 0) {
+                fireRemoved(portletAppContext);
             }
         }
-        fireRemoved(context);
     }
 
     /**
@@ -175,22 +221,32 @@
     /**
      * @see org.apache.pluto.container.driver.PortletContextService#getPortletContexts()
      */
-    public Iterator<DriverPortletContext> getPortletContexts() {
-        return new HashSet<DriverPortletContext>(portletContexts.values()).iterator();
+    public synchronized Iterator<DriverPortletContext> getPortletContexts() {
+        final List<DriverPortletContext> portletContextsList = new ArrayList<DriverPortletContext>(portletContexts.size());
+        
+        for (final DriverPortletContextHolder portletContextHolder : portletContexts.values()) {
+            portletContextsList.add(portletContextHolder.portletAppContext);
+        }
+        
+        return portletContextsList.iterator();
     }
 
     /**
      * @see org.apache.pluto.container.driver.PortletContextService#getPortletContext(java.lang.String)
      */
     public DriverPortletContext getPortletContext(String applicationName) {
-        return portletContexts.get(applicationName);
+        final DriverPortletContextHolder driverPortletContextHolder = portletContexts.get(applicationName);
+        if (driverPortletContextHolder == null) {
+            return null;
+        }
+        return driverPortletContextHolder.portletAppContext;
     }
 
     /**
      * @see org.apache.pluto.container.driver.PortletContextService#getPortletContext(org.apache.pluto.container.PortletWindow)
      */
     public DriverPortletContext getPortletContext(PortletWindow portletWindow) throws PortletContainerException {
-        return portletContexts.get(portletWindow.getPortletDefinition().getApplication().getName());
+        return getPortletContext(portletWindow.getPortletDefinition().getApplication().getName());
     }
 
 
@@ -224,9 +280,12 @@
      * @see org.apache.pluto.container.driver.PortletRegistryService#getPortletApplication(java.lang.String)
      */
     public PortletApplicationDefinition getPortletApplication(String applicationName) throws PortletContainerException {
-        DriverPortletContext ipc = portletContexts.get(applicationName);
-        if (ipc != null) {
-            return ipc.getPortletApplicationDefinition();
+        final DriverPortletContextHolder driverPortletContextHolder = portletContexts.get(applicationName);
+        if (driverPortletContextHolder != null) {
+            DriverPortletContext ipc = driverPortletContextHolder.portletAppContext;
+            if (ipc != null) {
+                return ipc.getPortletApplicationDefinition();
+            }
         }
         String msg = "Unable to retrieve portlet application: '"+applicationName+"'";
         LOG.warn(msg);
@@ -255,6 +314,7 @@
     }
 
     private void fireRegistered(DriverPortletContext context) {
+        //TODO differentiate between portlet app and portlet events
         PortletRegistryEvent event = new PortletRegistryEvent();
         event.setPortletApplication(context.getPortletApplicationDefinition());
 
@@ -265,6 +325,7 @@
     }
 
     private void fireRemoved(DriverPortletContext context) {
+        //TODO differentiate between portlet app and portlet events
         PortletRegistryEvent event = new PortletRegistryEvent();
         event.setPortletApplication(context.getPortletApplicationDefinition());
 
diff --git a/pluto-portal-driver/src/main/java/org/apache/pluto/driver/core/PortletWindowImpl.java b/pluto-portal-driver/src/main/java/org/apache/pluto/driver/core/PortletWindowImpl.java
index a67056a..4f3354f 100644
--- a/pluto-portal-driver/src/main/java/org/apache/pluto/driver/core/PortletWindowImpl.java
+++ b/pluto-portal-driver/src/main/java/org/apache/pluto/driver/core/PortletWindowImpl.java
@@ -19,16 +19,16 @@
 import javax.portlet.PortletMode;
 import javax.portlet.WindowState;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.apache.pluto.container.PortletContainer;
 import org.apache.pluto.container.PortletContainerException;
 import org.apache.pluto.container.PortletWindow;
 import org.apache.pluto.container.PortletWindowID;
-import org.apache.pluto.container.driver.PlutoServices;
+import org.apache.pluto.container.driver.PortalDriverServices;
 import org.apache.pluto.container.om.portlet.PortletDefinition;
 import org.apache.pluto.driver.services.portal.PortletWindowConfig;
 import org.apache.pluto.driver.url.PortalURL;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Implementation of <code>PortletWindow</code> interface.
@@ -59,7 +59,8 @@
         try
         {
             String applicationName = config.getContextPath();
-            this.portlet = PlutoServices.getServices().getPortletRegistryService().getPortlet(applicationName, config.getPortletName());
+            //TODO need better way to get reference to PortletRegistryService
+            this.portlet = ((PortalDriverServices)container).getPortletRegistryService().getPortlet(applicationName, config.getPortletName());
         }
         catch (PortletContainerException ex)
         {
diff --git a/pluto-portal/src/main/webapp/WEB-INF/pluto-portal-driver-services-config.xml b/pluto-portal/src/main/webapp/WEB-INF/pluto-portal-driver-services-config.xml
index 6b45907..0530df4 100644
--- a/pluto-portal/src/main/webapp/WEB-INF/pluto-portal-driver-services-config.xml
+++ b/pluto-portal/src/main/webapp/WEB-INF/pluto-portal-driver-services-config.xml
@@ -55,13 +55,9 @@
       <ref bean="PortalDriverServices"/>
     </constructor-arg>
   </bean>
-
-  <bean id="PlutoServices"
-        class="org.apache.pluto.container.driver.PlutoServices"
-        singleton="true">
-    <constructor-arg>
-      <ref bean="PortalDriverServices"/>
-    </constructor-arg>
+  
+  <bean class="org.apache.pluto.driver.container.PlutoServicesRegistryBean">
+    <constructor-arg ref="PortalDriverServices" />
   </bean>
 
   <bean id="PortalDriverServices"
@@ -224,7 +220,7 @@
   <bean id="PortletContextService"
         class="org.apache.pluto.driver.container.PortletContextManager">
     <constructor-arg>
-      <ref bean="RequestDispatcherService"/>
+      <ref bean="PortalDriverServices"/>
     </constructor-arg>
     <constructor-arg>
       <bean class="org.apache.pluto.container.impl.PortletAppDescriptorServiceImpl"/>