/*
 * BIServlet.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 java.util.Locale;

import javax.servlet.ServletException;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import oracle.cabo.share.nls.LocaleContext;
import oracle.cabo.ui.ServletRenderingContext;

import oracle.dss.dataSource.client.QueryClient;
import oracle.dss.dataSource.client.QueryManager;
import oracle.dss.dataSource.common.Query;
import oracle.dss.security.BIUser;
import oracle.dss.thin.beans.QueryParameterProvider;
import oracle.dss.thin.servlet.ServletQueryParameterProvider;
import oracle.dss.thin.servlet.ServletRequestHandler;
import oracle.dss.thin.beans.ThinBeanUI;
import oracle.dss.thin.beans.dataView.ThinDataviewCommon;
import oracle.dss.thin.beans.dataView.Presentation;
import oracle.dss.thin.beans.dataView.PresentationBean;
import oracle.dss.thin.beans.graph.ThinGraph;
import oracle.dss.util.DataSource;
import oracle.dss.util.ErrorHandler;

import javax.servlet.http.HttpUtils;

/** 
 * The main class for the BIServlet sample. This also serves as the base class
 * for all the other html client samples.
 * <P>
 * The BIServlet is a HttpServlet derived class that provides a simple connection 
 * page which prompts a user for connection information used to connect to the
 * OLAP service and the BI Beans Catalog.
 * <P>
 * The BIServlet object processes the request.    
 * The following items are intended to be further customized by subclasses:
 * <ol>
 * <li>The BIServlet class</li>
 * </ol>
 */
public class BIServlet extends HttpServlet {
  // the name of the parameter used to specify the name of the configuration 
  // file in the servlet configuration file
  public static final String CONFIG_FILE = "biConfigFile";

  // the URL query parameter for the username
  public static final String LOGIN_USER_NAME_PARAM = "username";
  // the URL query parameter for the password
  public static final String LOGIN_PASSWORD_PARAM = "password";

  // the URL query parameter for connecting
  public static final String CONNECT_PARAM = "connect";
  // the URL query parameter for disconnecting
  public static final String DISCONNECT_PARAM = "disconnect";

  // the key for storing the BIHttpSession object on the HTTP session
  private static final String BI_HTTP_SESSION_KEY = "BIHttpSessionKey";

  // the name of the default configuration file
  protected String m_configFile = "/catalogOLAPConfig1.xml";

  // the name of the presentation
  protected static final String PRESENTATION_NAME = "presentation";

  // set the default locale to be english, US
  protected Locale m_locale = Locale.US;

  /**
   * Called by the server to allow a servlet to handle a GET or a POST request.
   * <P>
   * The method handles connection-related events, such as connect and disconnect.
   * <P>
   * @param request An HttpServletRequest object that contains the request the
   * client has made of the servlet.
   * @param response An HttpServletResponse object that contains the response
   * the servlet sends to the client
   *
   * @throws ServletException If the request for the GET could not be handled
   * @throws IOException If an input or output error is detected when the
   * servlet handles the GET request
   */
  public void service(HttpServletRequest request, 
                      HttpServletResponse response) 
                      throws IOException, ServletException {
    BIHttpSession biHttpSession = null;                      
    try {
      // Key Concept - Retrieve the BIHttpSession object. Initialize if one
      // does not exist.
      biHttpSession = getBIHttpSession(request);

      // Process connection related events 
      processConnectionRequest(request, response);

      // If the connection has been successful, allow further processing
      if (biHttpSession.isConnected()) {
        processRequest(request, response);
      }
    }
    catch(Throwable t) {
      // attempt to retrieve the ErrorHandler from the BISession
      ErrorHandler errorHandler = null;
      if (biHttpSession != null && biHttpSession.getBISession() != null) {
        errorHandler = biHttpSession.getBISession().getErrorHandler();
      }
      // if an error handler is specified, use it to output to the servlet log
      // or use the servlet api
      if (errorHandler != null) {
        errorHandler.error(t, getClass().getName(), "service");
      }
      else {
        log(t.toString());
      }        
        
      // package the exception information into the response
      PrintWriter output = response.getWriter();
      output.println("An internal application error has occured. Please redo your last operation.");
      output.println(t.toString());
    }
  }

  /**
   * Cleans up objects that were allocated by the servlet. These may include
   * thin beans and custom objects that are cached on the session.
   *
   * @param biHttpSession Provides a way to identify a user across more than one
   * page request or visit to a Web site and to store information about that user.
   */
  public void cleanup(BIHttpSession biHttpSession) {
    ServletRequestHandler handler = biHttpSession.getServletRequestHandler();
    
    Presentation pres = (Presentation)handler.getThinBean(PRESENTATION_NAME);
    ThinDataviewCommon dataView = null;
    if (pres != null)
      dataView = pres.getView();

    if (dataView != null) {
      // clean up the view and any embedded data source
      DataSource viewDataSource = dataView.getModel().getDataSource ();
      if(viewDataSource != null) {
        // release the data source
        if(viewDataSource instanceof QueryClient) {
          ((QueryClient)viewDataSource).close ();
        }
        // break the link between the view and the data source  
        dataView.getModel().setDataSource(null);
      }

      // release graph resources    
      if (dataView instanceof ThinGraph)
        ((ThinGraph)dataView).cleanUp();
    }
  }

  /**
   * Retrieves the name of the BI Configuration file from the servlet configuration
   * parameters. If one is not specified, this method uses the default sample
   * configuration file.
   *
   * @return The name of the BI Configuration file.
   */
  public String getConfigFileName() {
    String configFile = getInitParameter(CONFIG_FILE);

    if (configFile != null) {
      m_configFile = configFile;
    }
          
    return m_configFile;
  }

  /**
   * Allows processing of connection related events such as connection and disconnection.
   * 
   * @param request An HttpServletRequest object that contains the request that the
   * client has made of the servlet.
   * @param response An HttpServletResponse object that contains the response
   * that the servlet sends to the client
   *
   * @return <code>true</code> If the request was handled, <code>false</code>
   * otherwise.
   *
   * @throws Exception If the request for the GET could not be handled
   */
  protected boolean processConnectionRequest(HttpServletRequest request,
                                             HttpServletResponse response) throws Exception {
    BIHttpSession biHttpSession = getBIHttpSession(request);

    boolean isConnected = biHttpSession.isConnected();
    if (!isConnected) {
      // retrieve the parameters from the GET request
      String connect = request.getParameter(CONNECT_PARAM);
      String userName = request.getParameter(LOGIN_USER_NAME_PARAM);
      String password = request.getParameter(LOGIN_PASSWORD_PARAM);

      if (null == connect) {
        // render the connection page to get the connection information from
        // the end user
        renderConnectionPage(request, response);
        return true;
      }
      else if (connect.equals(Boolean.TRUE.toString())) {
        // if a connect event, retrieve the username and password and start
        // the connection process.
        biHttpSession.connect(userName, password);

        if ( !biHttpSession.isConnected()) {
          // if not connected, render the page for the connection status.
          renderConnectionStatusPage(request, response);
        }
        
        return true;
      }
    }
    else {
      String disconnect = request.getParameter(DISCONNECT_PARAM);
      if (disconnect != null &&
          disconnect.equals(Boolean.TRUE.toString())) {
        // process the disconnect event
        biHttpSession.disconnect();

        // display the connection page
        renderConnectionPage(request, response);
        return true;
      }
    }

    return false;
  }

  /**
   * Processes the request while connected to the OLAP Server and BI Beans Catalog.
   * 
   * @param request An HttpServletRequest object that contains the request that the
   * client has made of the servlet.
   * @param response An HttpServletResponse object that contains the response
   * that the servlet sends to the client
   *
   * @return <code>true</code> If the request was handled, <code>false</code>
   * otherwise.
   *
   * @throws Exception If the request for the GET could not be handled
   */
  protected boolean processRequest(HttpServletRequest request,
                                   HttpServletResponse response)throws Exception {
    // render the page for the connection status.
    renderConnectionStatusPage(request, response);
    return true;
  }

  /**
   * Retrieves a BIHttpSession object (or a subclass) from the http session.
   * If the object does not exist on the HTTP session, then a new BIHttpSession
   * object (or a subclass) is instantiated and attached to the HTTP session.
   * <P>
   * This method provides a way to identify a user across more than one page
   * request and to store information about that user.
   *
   * @param request An HttpServletRequest object that contains the request that
   * the client has made of the servlet.
   *
   * @return The BIHttpSession object (or a subclass).
   */
  protected BIHttpSession getBIHttpSession(HttpServletRequest request) {
    BIHttpSession biHttpSession = null;

    HttpSession httpSession = request.getSession(true);
    Object value = httpSession.getAttribute(getHttpSessionKey());
    if(value instanceof BIHttpSession)
      biHttpSession = (BIHttpSession)value;
    else {
      biHttpSession = createBIHttpSession();
      httpSession.setAttribute(getHttpSessionKey(), biHttpSession);
    }
    return biHttpSession;
  }

  /**
   * This method returns the key for storing the BIHttpSession object on the HTTP session
   *
   * @return The key for storing the BIHttpSession object on the HTTP session
   */
  protected String getHttpSessionKey() {
    return BI_HTTP_SESSION_KEY;
  }

  /**
   * Creates the BIHttpSession object (or a subclass). Override this method to
   * create a sample specific BIHttpSession subclass.
   * <P>
   * Provides a way to identify a user across more than one page request
   * and to store information about that user.
   *
   * @return The BIHttpSession object.
   */
  protected BIHttpSession createBIHttpSession() {
    return new BIHttpSession(this);
  }

  /**
   * Renders the header of the page.
   * This section contains a description about the sample.
   *
   * @param out A PrintWriter object that can send character text to the client.
   * @param request An HttpServletRequest object that contains the request that the
   * client has made of the servlet.
   * @param response An HttpServletResponse object that contains the response
   * that the servlet sends to the client
   *
   * @throws IOException If an input or output error is detected when the
   * page is being rendered.
   */
  protected void renderPageHeader(PrintWriter out, 
                                  HttpServletRequest request,
                                  HttpServletResponse response) throws IOException {
    String sampleTitle = getSampleTitle();
    out.println("<H1>" + sampleTitle + "</H1>");

    String sampleDescription = getSampleDescription();
    out.println("<B>" + sampleDescription + "<B>");

    out.println("<HR>");
  }

  /**
   * Retrieves the title of the sample. Derived classes should override this method
   * to provide custom titles.
   *
   * @return The title of the sample.
   */
  protected String getSampleTitle() {
    return "BI Beans Sample - Connecting and Initializing a Thin-Client Application";
  }

  /**
   * Retrieves the description of the sample. Derived classes should override
   * this method to provide custom descriptions.
   *
   * @return The description of the sample.
   */
  protected String getSampleDescription() {
    return "This sample connects to OLAP data and to the BI Beans Catalog.";
  }
  
  /**
   * Renders the page footer. The default implementation provides a link to
   * disconnect from the OLAPServer and the BI Catalog. The connection page
   * is displayed after the disconnect operation has completed.
   *
   * @param out A PrintWriter object that can send character text to the client.
   * @param request An HttpServletRequest object that contains the request that the
   * client has made of the servlet.
   * @param response An HttpServletResponse object that contains the response
   * that the servlet sends to the client
   *
   * @throws IOException If an input or output error is detected when the
   * page is being rendered.
   */
  protected void renderPageFooter(PrintWriter out, 
                                  HttpServletRequest request,
                                  HttpServletResponse response) throws IOException {
    out.println("<HR>");

    // Render the "Disconnect" link if already connected
    BIHttpSession biHttpSession = getBIHttpSession(request); 
    if ( biHttpSession.isConnected()) {
      String disconnectURI = request.getRequestURI();
      // note that you should encode this URI for special characters
      disconnectURI += "?" + DISCONNECT_PARAM + "=" + Boolean.TRUE.toString();

      out.println("<a href=\"" + disconnectURI + "\">Disconnect</a>");
    }
  }

  /**
   * Renders the connection page. The default implementation asks for a username
   * and password that are used to connect to the OLAPServer and the BI Beans Catalog.
   *
   * @param request An HttpServletRequest object that contains the request that the
   * client has made of the servlet.
   * @param response An HttpServletResponse object that contains the response
   * that the servlet sends to the client
   *
   * @throws IOException If an input or output error is detected when the
   * page is being rendered.
   */
  protected void renderConnectionPage(HttpServletRequest request,
                                      HttpServletResponse response) throws IOException {
    response.setContentType("text/html");
    PrintWriter out = response.getWriter();

    out.println("<HTML>");
    out.println("<HEAD>");
    
    String sampleTitle = getSampleTitle();
    out.println("<TITLE>" + sampleTitle + "</TITLE>");

    out.println("</HEAD>");
    out.println("<BODY>");

    // render the page header
    renderPageHeader(out, request, response);

    out.println(request.getRequestURL() );

    // render the page contents
    out.println ("<FORM method=\"POST\"" +
                 " action=\"" + request.getRequestURI ( ) + "\">");
    out.println ("<TABLE>");
    out.println ("<TR>");
    out.println ("<TD>Username:<TD>");
    out.println ("<TD><INPUT type=\"text\" name=" + LOGIN_USER_NAME_PARAM + "><TD>");
    out.println ("</TR>");
    out.println ("<TR>");
    out.println ("<TD>Password:<TD>");
    out.println ("<TD><INPUT type=\"password\" name=" + LOGIN_PASSWORD_PARAM + "><TD>");
    out.println ("</TR>");
    out.println ("<TR>");
    out.println ("<TD><INPUT type=\"submit\" value=\"Connect\"><TD>");
    out.println ("</TR>");
    out.println ("</TABLE>");
    out.println ("<INPUT type=\"hidden\" name=\"" + CONNECT_PARAM +
                 "\" value=\"" + Boolean.TRUE.toString ( ) + "\">");
    out.println ("</FORM>");

    // render the page footer
    renderPageFooter(out, request, response);

    out.println("</BODY>");
    out.println("</HTML>");
  }

  /**
   * Renders the connection status page. The default implementation displays
   * information about whether the connections to the OLAP service and the BI
   * Beans Catalog were successful.
   *
   * @param request An HttpServletRequest object that contains the request that
   * the client has made of the servlet.
   * @param response An HttpServletResponse object that contains the response
   * that the servlet sends to the client
   *
   * @throws IOException If an input or output error is detected when the
   * page is being rendered.
   */
  protected void renderConnectionStatusPage ( HttpServletRequest request,
                                              HttpServletResponse response ) throws IOException {
    String username = null;

    response.setContentType("text/html");
    PrintWriter out = response.getWriter();

    // Retrieve the user name from the BISession
    BIHttpSession biHttpSession = getBIHttpSession ( request );
    BIUser biUser = biHttpSession.getBISession().getBIUser ();
    if (biUser != null) {
      username = biUser.getName();
    }
    if (null == username) {
      username = "";
    }
      
    out.println("<HTML>");
    out.println("<HEAD>");
    
    String sampleTitle = getSampleTitle();
    out.println("<TITLE>" + sampleTitle + "</TITLE>");
    
    out.println("</HEAD>");
    out.println("<BODY>");

    // render the page header
    renderPageHeader(out, request, response);

    // render the connection status 
    if (biHttpSession.isConnected()) {
      out.println("<B>The connection for user " + username + " was successful.</B>");
    }
    else {
      out.println("<B>The connection for user " + username + " was not successful.</B>");
    }

    // render the page footer    
    renderPageFooter(out, request, response);

    out.println("</BODY>");
    out.println("</HTML>");
  }

  /**
   * Retrieves the name of a static data source. This data source is used
   * to provide a default data source if the BISession does not contain a
   * QueryManager object. This situation arises when the biconfig.xml
   * file does not specify OLAPI-related connection information.
   *
   * @return The name of a static data source.
   */
  public String getStaticDataSourceClassName() {
    return "oracle.dss.samples.dataSource.DrillablePagingDataSource";
  }

  /**
   * Retrieves a default data source. If the BISession object contains a
   * QueryManager object, a default datasource created by the QueryManager
   * is returned.
   * The static data source specified is instantiated and returned.
   *
   * @param request An HttpServletRequest object that contains the request that the
   * client has made of the servlet.
   *
   * @return A reference to an object implementing the DataSource interface.
   * 
   * @throws Exception If an error is detected when the default data source is being created.
   */
  public DataSource getDefaultDataSource(HttpServletRequest request) throws Exception {
    BIHttpSession biHttpSession = getBIHttpSession(request);
    QueryManager queryManager = biHttpSession.getQueryManager();
    if(queryManager != null) {
      // retrieve the default query created by the QueryManager
      String [ ] strMeasures = { "MDM!M_CS_OLAP.SHAWSALES.AMOUNT_SOLD" };
      Query query = queryManager.createQuery ( );
      query.initCubeQuery(strMeasures, null);
      return query;
    }

    String staticDataSource = getStaticDataSourceClassName();
    if(null == staticDataSource) {
      return null;
    }      
    // attempt to create the static data source specified by the derived classes
    try {
      Class staticDataSourceClass = Class.forName(staticDataSource);
      Object staticDataSourceObject = staticDataSourceClass.newInstance();
      if(null == staticDataSourceObject ||
         !(staticDataSourceObject instanceof DataSource)) {
        return null;
      }
      return (DataSource)staticDataSourceObject;
    }
    catch(Exception ex) {
      return null;
    }
  }

  protected void setLocale(Locale locale) {
    m_locale = locale;
  }

  protected Locale getLocale() {
    return m_locale;
  }

  protected ServletRenderingContext createRenderingContext(HttpServletRequest request, HttpServletResponse response) {
    ServletRenderingContext context = new ServletRenderingContext(request, response, getServletConfig());
    return context;
  }        
}

