/* **************************************************************************

  $Archive: /njcl_v2rmi/src/com/novell/service/session/SessionManagerFactory.java $
  $Revision: 37 $
  $Modtime: 8/17/01 1:07p $

  Copyright (c) 1997 Novell, Inc.  All Rights Reserved.

  THIS WORK IS  SUBJECT  TO  U.S.  AND  INTERNATIONAL  COPYRIGHT  LAWS  AND
  TREATIES.   NO  PART  OF  THIS  WORK MAY BE  USED,  PRACTICED,  PERFORMED
  COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED,  ABRIDGED, CONDENSED,
  EXPANDED,  COLLECTED,  COMPILED,  LINKED,  RECAST, TRANSFORMED OR ADAPTED
  WITHOUT THE PRIOR WRITTEN CONSENT OF NOVELL, INC. ANY USE OR EXPLOITATION
  OF THIS WORK WITHOUT AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO
  CRIMINAL AND CIVIL LIABILITY.

****************************************************************************/
package com.novell.service.session;

import java.rmi.*;
import java.util.Hashtable;
import java.util.Properties;
import java.io.*;
import com.novell.service.session.spi.*;
import com.novell.service.session.util.Debug;
import com.novell.service.session.util.DefaultProperties;
import com.novell.service.session.xplat.DomainName;
import com.novell.java.lang.HasRootCause;
import com.novell.java.security.*;

/**
 * Provides a static (singleton) class used to create a SessionManager.
 * A SessionManager is the top level object in a Session hierarchy.
 *
 *@see Session
 *@see SessionManager
 */
public class SessionManagerFactory
{
   // Always access debug via final static...hopefully a final static of
   // false will be optimized out by the compiler
   final static private boolean DEBUG = false;
   final static private boolean S_DEBUG = false; // May have side effects
   final static private boolean WHO_CALLED_DEBUG = false; // May have side effects
   final static private boolean METHOD_TRACE_DEBUG = false;

  /**
   * @internal
   */
   public static final int SCOPE                = 0x00000000;
  /**
   * @internal
   */
   public static final int PRIVATE_SCOPE              = 0x10000000;
  /**
   * @internal
   */
   public static final int NO_SCAN_SCOPE              = 0x20000000;

  /**
   * @internal
   */
   // SessionManager object is shared within the same JVM
   // and connections are scanned from the requester
   // DEFAULT for most non-nlm implementations
   public static final int SHARED_SCAN_SCOPE       =  SCOPE;

  /**
   * @internal
   */
   // SessionManager object is shared within the same JVM
   // and connections are only known about if WE open them
   // DEFAULT for nlm platform
   public static final int SHARED_NO_SCAN_SCOPE    =  SCOPE | NO_SCAN_SCOPE;

  /**
   * @internal
   */
   // SessionManager object is unique to each requested instance
   // and connections are scanned from the requester
   // PRIVATE for most non-nlm implementations
   public static final int PRIVATE_SCAN_SCOPE      =  SCOPE | PRIVATE_SCOPE;

  /**
   * @internal
   */
   // SessionManager object is unique to each requested instance
   // and connections are only known about if WE open them
   // PRIVATE for nlm platform
   public static final int PRIVATE_NO_SCAN_SCOPE   =  SCOPE | PRIVATE_SCOPE | NO_SCAN_SCOPE;

   private static SessionManager sessionManagerSingletonInstance;
   private static SessionManagerService sessionManagerService;

   private SessionManagerFactory()
   {
   }

   private static synchronized SessionManagerService getSessionManagerService
   (
      SessionEnv env
   )
   throws Exception
   {
      if (null != sessionManagerService)
         return sessionManagerService;
      String host = (String)env.get(SessionEnv.SESSION_MANAGER_URL);
      if(host != null)
         sessionManagerService = (SessionManagerService)Naming.lookup(
            "//" + host + "/" + SessionEnv.SESSION_MANAGER_URL);
      else
         sessionManagerService = SessionManagerServiceImpl.getInstance();
      return sessionManagerService;
   }

   private static synchronized SessionManager getSingletonSM
   (
      SessionEnv env,
      int scope
   )
   throws Exception
   {
      if (DEBUG || WHO_CALLED_DEBUG)
      {
         Debug.println("Getting PUBLIC (singleton) session manager");
      }
      // Make sure singleton is valid before returning
      if (null != sessionManagerSingletonInstance)
         if (!sessionManagerSingletonInstance.isValid())
            sessionManagerSingletonInstance = null;

      // Get new singleton instance
      if (null == sessionManagerSingletonInstance)
      {
         if (DEBUG || WHO_CALLED_DEBUG)
         {
            Debug.println("Getting NEW session manager singleton");
         }

         // Get session manager singleton
         sessionManagerSingletonInstance =
            getSessionManagerService(env).getSessionManager(env, scope);
      }
      return sessionManagerSingletonInstance;
   }

   private static synchronized SessionManager getPrivateSM(
      SessionEnv env,
      int scope)
   throws Exception
   {
      if (DEBUG || WHO_CALLED_DEBUG)
      {
         Debug.println("Getting PRIVATE session manager");
      }
      return getSessionManagerService(env).
         getSessionManager(env, scope);
   }

   private static synchronized SessionManager getSessionManager
   (
      SessionEnv environment,
      int scope
   )
   throws Exception
   {
      if (DEBUG || WHO_CALLED_DEBUG)
      {
         Debug.println("Getting SCOPED session manager, scope: 0x" +
            Integer.toHexString(scope));
      }
      if (0 == (PRIVATE_SCOPE & scope))
      {
         return getSingletonSM(environment, scope);
      }
      else
      {
         return getPrivateSM(environment, scope);
      }
   }

   /**
    * Returns a shared instance of SessionManager based on the
    * information in the environment parameter.
    *
    * @param environment The session environment of the SessionManager.
    *
    * @return The shared instance of the SessionManager.
    *
    * @exception SessionException Top-level exception extended by all Session
    *            exceptions and thrown by Session objects.
    */
   public static SessionManager getSessionManager
   (
      SessionEnv environment
   )
   throws Exception
   {
      int scope = SCOPE;
      Boolean privateScope = (Boolean)environment.get(SessionEnv.PRIVATE_SCOPE);
      if (null != privateScope)
         if (true == privateScope.booleanValue())
            scope |= PRIVATE_SCOPE;
      return getSessionManager(environment, scope);
   }

   /**
    * Returns the unique, private instance of SessionManager.
    * It returns a new session manager each time called.
    *
    * @param environment The session environment of the SessionManager.
    *
    * @return The private instance of the SessionManager.
    *
    * @exception SessionException Top-level exception extended by all Session
    *            exceptions and thrown by Session objects.
    */
   public static SessionManager getPrivate
   (
      SessionEnv environment
   )
   throws Exception
   {
      int scope = SCOPE | PRIVATE_SCOPE;
      return getSessionManager(environment, scope);
   }

   /** @internal
    *
    * Used for testing this class.
    */
   public static void main
   (
      String args[]
   )
   {
      if (DEBUG)
      {
         Debug.setDebug(false);
         Debug.readDebugProperties(
            "debug.properties");
         Debug.writeDebugProperties(
            "debug.properties");
      }


      String domainName = "";
      String userName = "";
      String password = "";
      String existingDomain = "";
      String initialSessionFactory = "";
      boolean useAuthenticator = false;
      boolean allowBackgroundValidation = true;
//RMI
//      String serviceLocation = "";

      DefaultProperties p = readProperties();
      writeProperties(p);

      // Get test values
      domainName  =
         p.getProperty("domain.name");
      userName    =
         p.getProperty("user.name");
      password    =
         p.getProperty("user.password");
      existingDomain =
         p.getProperty("domain.name.existing");
      initialSessionFactory =
         p.getProperty(SessionEnv.INITIAL_SESSION_FACTORY);
      allowBackgroundValidation = new Boolean(
         p.getProperty(SessionEnv.ALLOW_BACKGROUND_VALIDATION)).
            booleanValue();
      useAuthenticator = new Boolean(
         p.getProperty("authenticator.use")).
            booleanValue();


      // Command-line test
      if (args.length > 0)
      {
         if (args.length < 4)
         {
            System.out.println(
               "SMF: parameters: domain user password existing_domain");
            return;
         }
         domainName     = args[0];
         userName       = args[1];
         existingDomain = args[3];
      }

      try
      {
         // Initialize factories
         SessionEnv env = new SessionEnv();

         // Where do we connect to session manager service?

         // Define initial sessions and load order
         env.put(SessionEnv.INITIAL_SESSION_FACTORY,
                 initialSessionFactory);

         // Run background update thread?
         env.put(SessionEnv.ALLOW_BACKGROUND_VALIDATION,
                 new Boolean(allowBackgroundValidation));

         // Get the session manager
         SessionManager sm = SessionManagerFactory.getSessionManager(env);

         System.out.println("SMF: Find existing sessions...");

         // Look for existing sessions
         SessionAttrs as = new SessionAttrs();
         SessionEnumerator enum = sm.search(as);
         while (enum.hasMoreElements())
         {
            Session found = enum.next();
            printSession("Found", found);
         }

         // Add com.novell.service.session
         System.out.println("SMF: Adding existing" + existingDomain + "...");
         Session s2 = sm.getSession(existingDomain);
         if (s2.getDomainName().
               equals(
                  existingDomain))
            System.out.println("MATCHED");
         else
            System.out.println("NOT MATCHED");;

         // Add com.novell.service.session
         System.out.println("SMF: Adding " + domainName + "...");
         Session s = sm.getSession(domainName);
         printSession("Added", s);

//         sm.validateLinks();

         System.out.println("SMF: Find sessions after add...");

         // Look for sessions
         enum = sm.search(as);
         while (enum.hasMoreElements())
         {
            Session found = enum.next();
            printSession("Found", found);
         }

         // Try to authenticate
         if (userName.length() > 0)
         {
            System.out.println("SMF: Authenticating...");
            Authenticator.login(
               s.createIdentity(userName));
         }

//         sm.validateLinks();

         System.out.println("SMF: Find sessions after authenticate...");

         // Look for sessions
         enum = sm.search(as);
         while (enum.hasMoreElements())
         {
            Session found = enum.next();
            printSession("Found", found);
         }

         System.out.println("SMF: Closing " + domainName + "...");

         // Close com.novell.service.session
         try
         {
            if (false)
               s.getParent().
                  close();
            else
               s.close();
         }
         catch (InvalidStateException e)
         {
            System.out.println("SMF: Session is invalid");
         }

//         sm.validateLinks();

         System.out.println("SMF: Find sessions after close...");
         // Look for sessions and enum attributes
         enum = sm.search(as);
         while (enum.hasMoreElements())
         {
            Session found = enum.next();
            printSession("Found", found);
            if (false)
            {
               SessionAttrs attributes = found.getAttributes();
               SessionAttrEnumerator attributesEnum = attributes.getAttributes();
               while (attributesEnum.hasMoreElements())
               {
                  System.out.println(attributesEnum.next());
               }
            }
         }

//         sm.validateLinks();

         System.out.println("SMF: Running garbage collector...");
         System.gc();

         System.out.println("SMF: Fifth scan...");

         // Look for sessions
         enum = sm.search(as);
         while (enum.hasMoreElements())
         {
            Session found = enum.next();
            printSession("Found", found);
         }
         System.out.println("SMF: Done");
      }
      catch (Exception e)
      {
         dumpException(e);
      }
      System.out.println("SMF: TEST COMPLETE!");
   }

   static private void printSession(String str, Session s)
   {
      String attrIds[] =
      {
         Session.DOMAIN_NAME_ATTR_ID,
         Session.SESSION_STATE_ATTR_ID,
         Session.PROVIDER_NAME_ATTR_ID,
         Authenticatable.IS_AUTHENTICATED_ATTR_ID,
         Session.SESSION_TYPE_ATTR_ID
      };
      try
      {
         SessionAttrs sa = s.getAttributes(attrIds);
         String domainName = "";
         String state = "";
         String provider = "";
         String auth = "";
         String type = "";

         // Get values for printout
         try
         {
//            domainName = s.getDomainName();
            domainName = (String)sa.getValue(attrIds[0]);
         }
         catch (SessionAttrNotFoundException e)
         {
            domainName = "???";
         }

         try
         {
            state = (String)sa.getValue(attrIds[1]);
         }
         catch (SessionAttrNotFoundException e)
         {
            state = "???";
         }

         try
         {
            provider = (String)sa.getValue(attrIds[2]);
         }
         catch (SessionAttrNotFoundException e)
         {
            provider = "???";
         }

         try
         {
//            auth = (s).isAuthenticated()
//               booleanValue() ? "AUTH" : "NOT AUTH");
            auth = (((Boolean)sa.getValue(attrIds[3])).
               booleanValue() ? "AUTH" : "NOT AUTH");
         }
         catch (SessionAttrNotFoundException e)
         {
            auth = "???";
         }

         try
         {
            type = (String)sa.getValue(attrIds[4]);
         }
         catch (SessionAttrNotFoundException e)
         {
            type = "???";
         }

         System.out.println("SMF: " + str +
                            " : " + domainName +
//                           " : " + state +
                            " : " + type +
                           " : " + provider +
                           " : " +auth);
      }
      catch (Exception e)
      {
         dumpException(e);
      }
   }

   static private void dumpException(Throwable e)
   {
      System.out.println("\nSMF: Exception...");
      e.printStackTrace();
      while (null != e)
      {
         if (e instanceof HasRootCause)
            e = ((HasRootCause)e).getRootCause();
         else
            e = null;
         if (null != e)
         {
             System.out.println("\nSMF: Root cause...");
             e.printStackTrace();
         }
      }
   }

   static private void writeProperties(DefaultProperties p)
   {
      try
      {
         FileOutputStream out = new FileOutputStream(
            "test.properties");
         p.save(out, "Test properties");
         out.close();
      }
      catch (IOException e)
      {
         // Ignore
      }
   }

   static private DefaultProperties readProperties()
   {
      DefaultProperties defaults = new DefaultProperties();
      defaults.put("domain.name", "API-410");
      defaults.put("user.name", "admin.novell");
      defaults.put("domain.name.existing", "NOVELL_INC");
      defaults.put("authenticator.use", "false");
      defaults.put(
         SessionEnv.INITIAL_SESSION_FACTORY,
         "com.novell.service.session.nds.NDSInitialSessionFactory:" +
         "com.novell.service.session.bindery.BinderyInitialSessionFactory");
      defaults.put(
         SessionEnv.ALLOW_BACKGROUND_VALIDATION,
         "true");

      DefaultProperties p = new DefaultProperties(defaults);

      try
      {
         FileInputStream in = new FileInputStream(
            "test.properties");
         p.load(in);
         in.close();
      }
      catch (IOException e)
      {
         // Ignore
      }
      return p;
   }
}

