/*
 * BIHttpSession.java
 *
 * Copyright © 1994-2004, Oracle. All rights reserved. Oracle grants
 * you ("Licensee") a non-exclusive, royalty-free, worldwide license to use,
 * modify and redistribute this software in source and binary code form, provided
 * that (i) this copyright notice and license appear on all copies of the
 * software; (ii) Licensee does not utilize the software in a manner disparaging
 * to Oracle; and (iii) Licensee does not utilize the software in a manner that
 * violates the terms of any Licensee agreement with Oracle.
 */
package samples.servlet;

import java.io.IOException;
import java.io.PrintWriter;

import java.util.Hashtable;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;

import oracle.cabo.share.error.ServletErrorLog;

import oracle.dss.bicontext.ObjectFactory;
import oracle.dss.persistence.persistencemanager.client.DefaultThinObjectFactory;
import oracle.dss.security.BIUser;

import oracle.dss.thin.servlet.ServletRequestHandler;
import oracle.dss.thin.state.BIAdvancedStateManager;

import oracle.dss.appmodule.client.BISession;
import oracle.dss.bicontext.ManagerFactory;
import oracle.dss.dataSource.client.QueryManager;
import oracle.dss.metadataManager.client.MetadataManager;
import oracle.dss.thin.beans.CaboErrorHandler;
import oracle.dss.util.persistence.PersistableConstants;

/** 
 * Provides a way to identify a user across more than one page request and
 * to store information about that user. This also serves as a base class
 * for all the other html client samples.
 * <P>
 * For a user, the BIHttpSession provides access to the following
 * <ol>
 * <li>The current page</li>
 * <li>The BISession object.</li>
 * <li>The MetadataManager object. The MetdataManager object encapsulates the connection to the OLAP service and the BI Catalog.</li>
 * <li>The <code>QueryManager</code> object. The <code>QueryManager</code> provides access to OLAPI operations.
 * </ol>
 * <P>
 * The BIHttpSession object has the following responsibility
 * <ol>
 * <li>Handling the state like the current page</li>
 * <li>Handling the event like connect and disconnect. The implementation also handles thin bean events.</li>
 * </ol>
 * <P>
 * The subclasses of the BIHttpSession also have access to the BISession
 * instance and the MetadataManager instance.
 */
public class BIHttpSession implements HttpSessionBindingListener {
  // an instance of the ServletRequestHandler. This object provides services
  // including setting state and handling events for the thin beans registered
  // with it.
  protected ServletRequestHandler m_handler = null;
  // the instance of the servlet
  protected BIServlet m_servlet = null;

  // HTTP session scope attributes
  protected Hashtable m_attributes = new Hashtable();

  // is the session connected
  protected boolean m_connected = false;
  // a BISession instance
  protected BISession m_biSession = null;
  // a MetadataManager instance
  protected MetadataManager m_metadataManager = null;
  // a QueryManager instance
  protected QueryManager m_queryManager = null;
  // a State Manager instance
  protected BIAdvancedStateManager m_stateManager = null;

  /**
   * Constructor for BIHttpSession. Initializes a ServletRequestHandler object.
   *
   * @param servlet The BIServlet (or subclass).
   */
  public BIHttpSession(BIServlet servlet) {
    m_servlet = servlet;
  }

  /**
   * Retrieve the ServletRequestHandler instance.
   *
   * @return the ServletRequestHandler instance
   */
  public ServletRequestHandler getServletRequestHandler() {
    // Key concept - The ServletRequestHandler object simple servlet controller.
    // It provides methods to handle state (setState and getState) and handle
    // events for the BI thin beans. ThinBeans that exist across requests
    // ( are stored in the HttpSession ) should also be registered with the
    // ServletRequestHandler.
    if (null == m_handler) {
      m_handler = new ServletRequestHandler();
    }
    return m_handler;
  }

  /** 
   * Notifies the object that it is being unbound from a session and identifies
   * the session.
   * If we are connected to the OLAP service and the BI Catalog, we disconnect and
   * release any resources allocated for the user.
   *
   * @param event the event that identifies the session
   */
  public void valueUnbound(HttpSessionBindingEvent event) {
    // disconnect from the OLAP Services or BI Beans Catalog
    try {
      disconnect();
    }
    catch(Throwable t) {
      m_servlet.log(t.toString());
    }
  }

  /** 
   * Notifies the object that it is being bound to a session and identifies
   * the session.
   *
   * @param event the event that identifies the session
   */
  public void valueBound(HttpSessionBindingEvent event) {
  }

  /**
   * Flush all session scope attributes.
   */
  public void flushAttributes() {
    m_attributes.clear();
  }

  /**
   * Retrieves a session scope attribute.
   *
   * @param the name of the attribute.
   *
   * @return a session scope attribute.
   */
  public Object getAttribute(String name) {
    return m_attributes.get(name);
  }

  /**
   * Remove a session scope attribute.
   *
   * @param the name of the attribute.
   */
  public void removeAttribute(String name) {
    m_attributes.remove(name);
  }

  /**
   * Set a session scope attribute.
   *
   * @param the name of the attribute.
   * @param the value of the attribute.
   */
  public void setAttribute(String name, Object value) {
    removeAttribute(name);
    m_attributes.put(name, value);
  }
  
  /**
   * Retrieves the BISession instance created for this user.
   *
   * @return the BISession instance created for this user.
   */
  public BISession getBISession() {
    return m_biSession;
  }

  /**
   * Retrieves the MetadataManager instance created for this user.
   *
   * @return the MetadataManager instance created for this user.
   */
  public MetadataManager getMetadataManager() {
    return m_metadataManager;
  }

  /**
   * Retrieves the QueryManager instance created for this user.
   *
   * @return the QueryManager instance created for this user.
   */
  public QueryManager getQueryManager() {
    return m_queryManager;    
  }
  
  /**
   * Indicates if this application is connected to the OLAP service
   * and BI Catalog service for this user.
   *
   * @return  <code>true</code> if connected,
   *          <code>false</code> if disconnected.
   */
  public boolean isConnected() {
    return m_connected;
  }

  /**
   * Connects to the OLAP service and BI Catalog service using connection
   * information from the BI Configuration file and from the connection page.
   *
   * @param userName The name of the user.
   * @param password The credential of the user.
   *
   * @throws Exception if an error is detected while the connection is taking place.
   */
  public void connect(String userName, String password) throws Exception {
    if(isConnected()) {
      return;
    }
    // creates a BISession for the current user session
    m_biSession = new BISession(m_servlet.getConfigFileName());
    m_biSession.setBIUser(new BIUser(userName));

    // retrieve the servlet context
    ServletContext servletContext = m_servlet.getServletConfig().getServletContext();
    // Update the ErrorHandler of the BISession. This MetadataManager and the 
    // QueryManager will use this ErrorHandler by default
    CaboErrorHandler errorHandler = new CaboErrorHandler( 
                                    new ServletErrorLog(servletContext));
    m_biSession.addErrorHandler(errorHandler);

    // retrieves the MetadataManager and QueryManager from the ManagerFactory
    // the MetadataManager and its associated Connection(s) are defined in the
    // BI Configuration file
    try {
      m_metadataManager = (MetadataManager)m_biSession.getManagerFactory().
                          lookupManager(ManagerFactory.METADATA_MANAGER, null, true);

      m_queryManager = (QueryManager)m_biSession.getManagerFactory().
                       lookupManager(ManagerFactory.QUERY_MANAGER, null, true);

      m_connected = true;
      
      doPostConnectionProcessing();
    }
    catch(Exception ex) {
      // disconnect existing BISession, MetadataManager and QueryManager objects
      disconnect();

      throw ex;
    }
  }

  /**
   * Disconnect the application from the MetadataManager and their associated
   * connections.
   * Disconnect the current user session.
   * Subclasses should override this method to add additional cleanup code.
   */
  public void disconnect() throws Exception {
    // clean up objects cached on the http session
    cleanup();

    try {
      // disconnect the current session
      if (m_biSession != null) {
        m_biSession.disconnect();
      }        
    }
    catch (Exception ex) {
      throw ex;
    }
    finally {
      m_connected = false;
      
      // clean up
      m_biSession = null;
      m_metadataManager = null;
      m_queryManager = null;
    }
  }

  /**
   * Clean up the objects cached on the http session
   */
  public void cleanup() {
    // delegate the task of cleaning up objects to the servlet
    m_servlet.cleanup(this);

    flushAttributes();

    m_handler = null;
  }

  /**
   * Do post connection processing. Register the thin bean class names for the
   * respective objects in the BI Catalog.
   */
  protected void doPostConnectionProcessing() {
    if(null == m_metadataManager)
      return;
      
    ObjectFactory thinObjectFactory = new DefaultThinObjectFactory(m_metadataManager.getErrorHandler());
    m_metadataManager.registerObjectFactory(thinObjectFactory);  
      
  }

  /**
   * Retrieves the StateManager instance created for this user.
   *
   * @return the StateManager instance created for this user.
   */
  public BIAdvancedStateManager getStateManager() throws Exception {
    if (m_stateManager == null) {
      if (getMetadataManager() != null)
        m_stateManager = new BIAdvancedStateManager(getMetadataManager().getMDRoot());
      else
        m_stateManager = new BIAdvancedStateManager(null);
    }
    return m_stateManager;
  }
  
}
