// Copyright (c) 2000 Oracle Corporation
package oracle.jbo.http;

import java.io.Serializable;
import java.io.IOException;

import java.util.Hashtable;
import java.util.Properties;
import java.util.Enumeration;

import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionBindingEvent;

/**
 * A container for BC4J HTTP context resources.  This class is intended for
 * use by BC4J classes that must cache application resources in one of the
 * servlet/jsp contexts.  This may include the HttpSession,
 * the HttpServletRequest, the HttpServletResponse, and the PageContext.
 * <p>
 * The container provides a listener interface to notify cached BC4J resources
 * when they are bound and unbound from the context container.
 * </p>
 */
public class HttpContainer
   implements HttpSessionBindingListener, Serializable
{
   // External container property names
   public static final String PAGE_CONTEXT_CONTAINER_NAME    = "jbo.PageContext";
   public static final String SESSION_CONTEXT_CONTAINER_NAME = "jbo.SessionContext";
   public static final String APPLICATION_COOKIE_PREFIX      = "jbo.ApplicationCookie.";

   // Internal container property names
   public static final String APPLICATION_BINDING_LISTENER_PREFIX = "ApplicationBindingListener_";
   public static final String APPLICATION_PREFIX             = "Application_";

   private transient Hashtable   mResources = new Hashtable(10);

   // Retain a backwards pointer to the containing context
   private transient Object      mContextRef                      = null;

   /**
    * Constructor
    */
   public HttpContainer(Object contextRef)
   {
      super();
      mContextRef = contextRef;
   }

   public HttpContainer()
   {
      super();
   }

   public Object getValue(Object name)
   {
      return mResources.get(name);
   }

   public Object removeValue(Object name, Properties userProperties)
   {
      Object rtn = mResources.remove(name);

      if ((rtn != null) && (rtn instanceof BindingListener))
      {
         BindingEvent bindingEvent = new BindingEvent(mContextRef);

         bindingEvent.setUserProperties(userProperties);

         ((BindingListener)rtn).valueUnbound(bindingEvent);
      }

      return rtn;
   }

   public Object removeValueInternal(Object name)
   {
      Object rtn = mResources.remove(name);

      return rtn;
   }

   public void clear(Properties userProperties)
   {
      Hashtable resources = (Hashtable)mResources.clone();

      mResources.clear();

      Enumeration listenerEnum = resources.elements();

      BindingEvent bindingEvent = new BindingEvent(mContextRef);

      bindingEvent.setUserProperties(userProperties);

      while (listenerEnum.hasMoreElements())
      {
         Object listener = listenerEnum.nextElement();

         if (listener instanceof BindingListener)
         {
            ((BindingListener)listener).valueUnbound(bindingEvent);
         }
      }
   }

   public void timeout(Properties userProperties)
   {
      System.err.println("BC4J HTTP Container was timed out");

      Hashtable resources = (Hashtable)mResources.clone();

      mResources.clear();

      Enumeration listenerEnum = resources.elements();

      BindingEvent bindingEvent = new BindingEvent(mContextRef);

      bindingEvent.setUserProperties(userProperties);

      while (listenerEnum.hasMoreElements())
      {
         Object listener = listenerEnum.nextElement();

         if (listener instanceof BindingListener)
         {
            ((BindingListener)listener).timeout(bindingEvent);
         }
      }
   }

   public Object putValue(Object name, Object value, Properties userProperties)
   {
      Object rtn = mResources.put(name, value);

      if ((rtn != null) && (rtn instanceof BindingListener))
      {
         BindingEvent bindingEvent = new BindingEvent(mContextRef);

         bindingEvent.setUserProperties(userProperties);

         ((BindingListener)rtn).valueBound(bindingEvent);
      }

      return rtn;
   }

   public void valueBound(HttpSessionBindingEvent e)
   {
      // If the context reference is null then bind it to the session
      // reference.  It is possible that this container has been serialized
      // and moved to another web server VM.
      if (mContextRef == null)
      {
         mContextRef = e.getSession();
      }
   }

   public void valueUnbound(HttpSessionBindingEvent e)
   {
      timeout(null);
   }

   private void writeObject(java.io.ObjectOutputStream out)
      throws IOException
   {
      // Timeout the container before continuing.  This is necessary to prevent
      // orphaned container references (since the container's internal cache
      // is declared transient.  Chose timeout for now as the default mode.
      // Perhaps the listener interface should have a serialize method to
      // notify listeners of serialization events.
      timeout(null);

      out.writeObject(this);
   }
}

 