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

  $Archive: /njcl_v2/src/com/novell/service/session/nds/NDSInitialSessionState.java $
  $Revision: 33 $
  $Modtime: 10/02/01 11:44a $

  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.nds;

import com.novell.service.jncpv2.clx.*;
import com.novell.service.session.xplat.*;
import com.novell.service.session.*;
import com.novell.service.session.spi.*;
import com.novell.service.session.util.Debug;
import java.util.Enumeration;
import java.util.Vector;
import java.util.Hashtable;
import com.novell.service.jncp.NSIException;

/** @internal
 * NDSSession provider's state.
 */
public class NDSInitialSessionState extends NDSSessionState
{
   // 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 FAIL_DEBUG = false; // when eval fails
   final static private boolean I_DEBUG = false; // Ignore exception
   final static private boolean S_DEBUG = false; // May have side effects

   // NLM special case of connections being opened outside threadgroups
   static private boolean NLM_KLUDGE_THREADGROUP = true;
   final static private boolean  CACHE_DEBUG = false;

   public NDSInitialSessionState
   (
      SessionImpl attachedSession
   )
   throws SessionException
   {
      super(attachedSession);

      // We're ready to be used!
      this.attachedSession.setState(this);
   }

   /**
    * Inherited from Session
    *
    * @see Session#getSession
    */
   public Session getSession
   (
      String domainName,
      SessionEnv environment
   )
   throws SessionException
   {
      if (DEBUG)
      {
         Debug.println(
            domainName);
      }

      // Make sure existing code doesn't break...don't need padded
      // domain name any more
      String uDomainName = DomainName.stripUnderscores(domainName.toUpperCase());

      validateChildren();
      Session session = findSession(uDomainName);
      // Not existing session
      if (null == session)
      {
         if (FAIL_DEBUG)
            Debug.printlnOnly("Failed as existing session: '" + uDomainName + "'");

         Vector exceptions = new Vector();

         // At this point, this name doesn't exist in current sessions...
         // Try as new tree name...
         try
         {
            session = addNewSession(
               uDomainName,
               environment);
            if (DEBUG)
            {
               Debug.println(
                  "Added tree: " + session);
            }
         }
         catch (Exception e)
         {
            // Record failure as tree
            exceptions.addElement(e);

            if (I_DEBUG || FAIL_DEBUG)
            {
               Debug.printlnOnly("Failed as new tree: '" +
                  uDomainName + "'");
            }
            if (I_DEBUG)
            {
               Debug.ignoreException(e);
            }

            // Failed as tree, try passing to children...
            Session cs;

            SessionEnumerator childSessionEnumeration = getChildren();

            // Existing server?
            while (childSessionEnumeration.hasMoreElements())
            {
               cs = childSessionEnumeration.next();
               if (DEBUG)
               {
                  Debug.println("trying child: " + cs);
               }
               try
               {
                  session = cs.getSession(domainName, environment);
                  break;
               }
               catch (Exception e1)
               {
                  // Record failure as server in existing children
                  exceptions.addElement(e1);
                  if (I_DEBUG || FAIL_DEBUG)
                  {
                     Debug.printlnOnly("Failed under existing trees: '" +
                        domainName + "'");
                  }
                  if (I_DEBUG)
                  {
                     Debug.ignoreException(e1);
                  }
               }
            } // while (childSessionEnumeration.hasMoreElements()); existing server?
            // Try as new server
            if (null == session)
            {
               // Failed as tree name...assume we were given a server name
               // that is part of a tree that we're not currently connected
               // to.
               try
               {
                  // Connect to server and get tree name
                  Connection connection = xplatUtil.getConnectionByName(
                     environment,
                     uDomainName,
//                     DomainName.stripQualifiers(uDomainName),
                     Natives.OWNER_NDS);
                  String treeName =
                     xplatUtil.getInfoString(
                        connection,
                        Natives.CONN_INFO_TREE_NAME);
                  if (DEBUG)
                  {
                     Debug.println(
                        "Trying tree: " + treeName);
                  }
                  if (NLM_KLUDGE_THREADGROUP)
                  {
                     /*
                      * Connection was only needed for tree name and gets
                      * "in the way" on nlm requester because it was created
                      * outside of the tg/currentuser of it's soon to be
                      * created nds context...if the context needs a conn to
                      * the same place, it will create a separate one under
                      * it's own tg/currentuser.
                      */
                     if (Natives.isNetWareNLM())
                        xplatUtil.close(connection);
                  }
                  // Recursive...call self with tree name
                  if (!treeName.equals(""))
                  {
                     // Create new tree session
                     Session s = getSession(
                        treeName,
                        environment);
                     // Add server to it
                     session = s.getSession(
                        uDomainName,
                        environment);
                  }
                  else
                     xplatUtil.close(connection);
               }
               catch (Exception e1)
               {
                  // Record failure as server in new tree
                  exceptions.addElement(e1);
                  if (I_DEBUG || FAIL_DEBUG)
                  {
                     Debug.printlnOnly("Failed as new server under new tree: '" +
                        uDomainName + "'");
                  }
                  if (I_DEBUG)
                  {
                     Debug.ignoreException(e1);
                  }
               }
            } // if (null == session); try new server
            if (null == session)
            {
               InvalidDomainNameException t =
                  new InvalidDomainNameException(domainName);
               t.setRootCauses(exceptions);
               if (DEBUG)
               {
                  Debug.dumpException(t);
               }
               throw t;
            } // if (null == session); complete failure
         }
      } // if (null == session); existing session?
      if (DEBUG)
      {
         Debug.println("session = " + session);
      }
      return session;
   }

   /**
    * Inherited from Session
    *
    * @see Session#getAttributes()
    */
   public SessionAttrs getAttributes
   (
   )
   throws SessionException
   {
      try
      {
         // Get inherited attributes
         SessionAttrs attributes = super.getAttributes();

         // Override with this session's
         String attrIds[] =
         {
            Session.SESSION_TYPE_ATTR_ID,
            Session.IS_REAL_ATTR_ID
         };
         attributes.modify(getStateAttrs(attrIds));

         return attributes;
      }
      catch (NSIException e)
      {
         throw InvalidSessionState.makeInvalid(this.attachedSession, e);
      }
   }

   /**
    * Inherited from Session
    *
    * @see Session#getAttributes(String[])
    */
   public SessionAttrs getAttributes
   (
      String attrIds[]
   )
   throws SessionException
   {
      try
      {
         // Get this state's attributes
         SessionAttrs attributes = getStateAttrs(attrIds);

         if (attributes.count() != attrIds.length)
         {
            // Get inherited attributes
            attributes.merge(super.getAttributes(attrIds));
         }

         return attributes;
      }
      catch (NSIException e)
      {
         throw InvalidSessionState.makeInvalid(this.attachedSession, e);
      }
   }

   private SessionAttrs getStateAttrs
   (
      String attrIds[]
   )
   throws SessionException
   {
      // Get inherited attributes
      SessionAttrs attributes = new SessionAttrs();

      // Override with this session's
      for (int i = 0; i < attrIds.length; i++)
      {
         if (Session.SESSION_TYPE_ATTR_ID.equals(attrIds[i]))
         {
            attributes.modify(
               Session.SESSION_TYPE_ATTR_ID,
               Session.INITIAL_SESSION_TYPE);
         }
         // SessionState sets to true...need to override
         if (Session.IS_REAL_ATTR_ID.equals(attrIds[i]))
         {
            attributes.add(
               Session.IS_REAL_ATTR_ID,
               new Boolean(false));
         }
      }
      return attributes;
   }

   /*
    * Validate just children, not children's children.
    */
   protected void validateChildren
   (
   )
   throws SessionException
   {
      if (DEBUG)
      {
         Debug.println("->NISS.validateChildren()");
      }
      // Get all trees that requester currently sees
      Hashtable reqTrees = xplatUtil.getTrees();
      SessionEnumerator sessionEnum = getChildren();
      Enumeration reqTreeEnum;

      // Validate existing sessions: if tree exists in session, but not
      // in real world, remove session.
      while (sessionEnum.hasMoreElements())
      {
         Session session = null;
         try
         {
            session = sessionEnum.next();
            if (DEBUG)
            {
               Debug.println(
                  "--NISS.validateChildren():validating:" + session);
            }
            String sessionDomainName = session.getDomainName();
            boolean found = false;

            reqTreeEnum = reqTrees.elements();
            while (reqTreeEnum.hasMoreElements())
            {
               String reqTree =
                  (String)reqTreeEnum.nextElement();
               if (reqTree.equals(sessionDomainName))
               {
                  found = true;
                   // Don't need to check next time
                  reqTrees.remove(reqTree);
                  break;
               }
            }
             // Close com.novell.service.session if it doesn't exist in real world.
            if (!found)
            {
               if (DEBUG)
               {
                  Debug.println(
                     "--NISS.validateChildren(): !!!Session not found in requester: " + session);
               }
               InvalidSessionState.makeInvalid(
                  (SessionImpl)session,
                  new SessionException("Session not found in requester: " + session));
            }
         }
         catch (InvalidSessionException e)
         {
            if (I_DEBUG)
            {
               Debug.ignoreException(e);
            }
            attachedSession.removeChild((SessionImpl)session);
         }
      }

       // Add new sessions: any remaining trees must be new sessions.
      reqTreeEnum = reqTrees.elements();
      while (reqTreeEnum.hasMoreElements())
      {
         try
         {
            String newTree = (String)reqTreeEnum.nextElement();
            if (DEBUG)
            {
               Debug.println(
                  "--NTSS.validateChildren(): adding new tree: " + newTree);
            }
            this.addNewSession(newTree, new SessionEnv());
         }
         catch (NotConnectionOwnerException e)
         {
            // Ignore...this is separate from SessionException ignore
            // because it MUST be ignored...the others may or may not be
            // removed or specialized.  However, this one must be ignored.
            //
            // The tree list returned is generated from the connections know,
            // regardless of who the owner is.
            if (I_DEBUG)
            {
               Debug.ignoreException(e);
            }
         }
         catch (SessionException e)
         {
            // Ignore...just won't create session...other provider owns
            // the connection
            if (I_DEBUG)
            {
               Debug.ignoreException(e);
            }
         }
         catch (NSIException e)
         {
            // Ignore...just won't create session...other provider owns
            // the connection
            if (I_DEBUG)
            {
               Debug.ignoreException(e);
            }
         }
      }
      if (DEBUG)
      {
         Debug.println("<-NISS.validateChildren()");
      }
   }

   /**
    * Add Session to existing children, no questions asked.
    */
   private Session addNewSession
   (
      String domainName,
      SessionEnv  environment
   )
   throws SessionException
   {
      if (DEBUG || CACHE_DEBUG)
      {
         Debug.println("Adding new session: " + domainName);
      }

      SessionImpl newSession;

      // Synchronize off impl...doesn't change...unlike state
//      synchronized (this.attachedSession)
      {
         newSession =
            new SessionImpl(
               this.attachedSession,
               domainName,
               (SessionEnv)environment.clone());

         new NDSTreeSessionState(
            newSession);

         attachedSession.addChild(newSession);
      }

      newSession.validateLinks();

      if (DEBUG || CACHE_DEBUG)
      {
         Debug.println("Just created " + newSession + " in " + sessionManager);
         NDSContextSessionState state = (NDSContextSessionState)newSession.getState();
         Debug.println("ThreadGroup: " + state.context.nativeContext.threadGroup + " ContextHandle: " + state.context.nativeContext.contextHandle);
         Debug.printCaller();
      }
      return newSession;
   }
}
