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

  $Archive: /njcl_v2rmi/src/com/novell/service/nds/naming/net/NetEnvironment.java $
  $Revision: 23 $
  $Modtime: 1/23/01 4:03p $

  Copyright (c) 1998 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.nds.naming.net;

import java.rmi.RemoteException;
import java.io.Serializable;

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

import javax.naming.*;

import com.novell.service.jncp.NSIException;

import com.novell.service.nds.naming.NdsEnvironment;

import com.novell.service.session.Session;
import com.novell.service.session.SessionEnv;
import com.novell.service.session.SessionManager;
import com.novell.service.session.SessionManagerFactory;
import com.novell.service.session.SessionException;
import com.novell.service.session.nds.NDS;
import com.novell.service.jncpv2r.net.NetService;
import com.novell.service.jncpv2r.net.BasicNetService;
import com.novell.service.jncpv2r.net.NetBuffer;

import com.novell.service.session.*;

import com.novell.utility.naming.Environment;
import com.novell.utility.naming.URLParser;


/** @internal
 * This adds the ability to specify a batch size for native calls.
 * It also adds the getSession method
 */
public class NetEnvironment
   extends NdsEnvironment
   implements Cloneable, Serializable
{
   // NdsEnvironment.DEFAULT_ATTRS must always evaluate to the string in the
   //   first position in this array -- so that default selection works
   private static String[] attrsTable =
   {
      NdsEnvironment.OBJECT_ATTRS,
      NdsEnvironment.DSI_OBJECT_ATTRS
   };

   // NdsEnvironment.DEFAULT_DEFS must always evaluate to the string in the
   //   first position in this array -- so that default selection works
   private static String[] defsTable =
   {
      NdsEnvironment.CLASS_DEFS,
      NdsEnvironment.EXPANDED_CLASS_DEFS,
      NdsEnvironment.INFO_CLASS_DEFS,
      NdsEnvironment.FULL_CLASS_DEFS
   };

   // State variables.  Any default state needs to also be set in setState

   protected final static String defaultHost = null;
   protected String host = defaultHost;

   protected final static String defaultPrincipal = null;
   protected String principal = defaultPrincipal;

   private final static String defaultObjectAttributeLevel =
                              NdsEnvironment.DEFAULT_ATTRS;
   private String objectAttributeLevel = defaultObjectAttributeLevel;

   protected final static String defaultCredentials = "";
   protected String credentials = defaultCredentials;

   protected final static boolean defaultReferrals = false;
   protected boolean referrals = defaultReferrals;

   private final static int defaultClassAttributesLevel = 1;
   private int classAttributesLevel = defaultClassAttributesLevel;

   private final static String defaultClassAttributesLevelStr = NdsEnvironment.DEFAULT_DEFS;
   private String classAttributesLevelStr = defaultClassAttributesLevelStr;

   protected final static boolean defaultConnectionIsValid = false;
   protected boolean connectionIsValid;

   // State variables.  Any default state needs to also be set in setState

   private int batchSize = NetBuffer.DEFAULT_MESSAGE_LEN;

   private final static String defaultLastConnection = "";
   private String lastConnection = defaultLastConnection;

   private final static int defaultConfidence = 0;
   private int confidence = defaultConfidence;

   private final static int defaultNameCacheDepth = -1;
   private int nameCacheDepth = defaultNameCacheDepth;

   private final static boolean defaultTypelessNames = true;
   private boolean typelessNames = defaultTypelessNames;

   private transient boolean setLastConnection = false;

   //
   private transient Session session;
   private transient NetService service;
   private final static String defaultServiceKey = BasicNetService.KEY;
   protected String serviceKey = defaultServiceKey;
   private final static boolean defaultServiceIsValid = false;
   protected boolean serviceIsValid = defaultServiceIsValid;


   /**
    *
    */
   public NetEnvironment (
         Hashtable environment)
      throws NamingException
   {
      super (environment);
      setState (environment, false);
      this.getSession ();

   } /* NetEnvironment () */

   /**
    *
    */
   public NetEnvironment (
         Environment environment)
      throws NamingException
   {
      super (environment);

      setState (this.environment, false);
      this.getSession ();

   } /* NetEnvironment () */

   /**
    * Creates a deep copy of this environment. The hashtable is duplicated.
    * This is a relatively expensive operation.
    *
    * @return  a clone of this NetEnvironment.
    * @see     java.lang.Cloneable
    */
   public synchronized Object clone ()
   {
      NetEnvironment environment = (NetEnvironment) super.clone ();

      environment.host = this.host;
      environment.principal = this.principal;
      environment.credentials = this.credentials;
      environment.classAttributesLevel = this.classAttributesLevel;
      environment.classAttributesLevelStr = this.classAttributesLevelStr;

      environment.connectionIsValid = this.connectionIsValid;
      environment.session = this.session;

      environment.serviceIsValid = this.serviceIsValid;
      environment.service = this.service;

      environment.confidence = this.confidence;

      environment.nameCacheDepth = this.nameCacheDepth;

      environment.typelessNames = this.typelessNames;

      environment.batchSize = this.batchSize;

      return (environment);

   } /* clone () */

   /**
    *
    */
   public Hashtable addToEnvironment (
         Hashtable environment)
      throws NamingException
   {
      // Set the state of this object
      setState (environment, false);

      // Copy environment's properties to this environment's properties
      Enumeration elements = environment.keys ();

      while (elements.hasMoreElements ())
      {
         String key = (String) elements.nextElement ();

         this.environment.put (key, environment.get (key));
      }
      return ((Hashtable) this.environment.clone ());

   } /* addToEnvironment () */

   /**
    *
    */
   public Object addToEnvironment (
         String propName,
         Object propVal)
      throws NamingException
   {
      Object object = super.addToEnvironment (propName, propVal);

      Hashtable h = new Hashtable (1);
      h.put (propName, propVal);

      addToEnvironment (h);

      return (object);

   } /* addToEnvironment () */


   /**
    *
    *
   public Hashtable removeFromEnvironment (
         Hashtable deletions)
      throws NamingException
   {
      // Set the state of this object
      setState (deletions, true);

      // Remove entries
      Enumeration e = deletions.propertyNames ();
      while(e.hasMoreElements ())
      {
         environment.remove(e.nextElement ());
      }

      return (Properties) environment.clone ();

   } / removeFromEnvironment () */

   /**
    *
    */
   public Object removeFromEnvironment (
         String propName)
      throws NamingException
   {
      Object object = super.removeFromEnvironment (propName);

      // Set the state of this object
      Hashtable h = new Hashtable (1);
//      h.put (propName, new Object ());
      h.put (propName, new String ());
      setState (h, true);

      // Remove entry
      return (object);

   } /* removeFromEnvironment () */


   /**
    *
    */
   public Hashtable getEnvironment ()
      throws NamingException
   {
      return ((Hashtable) environment.clone ());
   }

   /**
    * Sets the state of this object based on the specified environment.
    * if default is set to true, the keys in the environment are used to set
    * those values to their default states.
    */
   protected void setState (
         Hashtable environment,
         boolean setDefault)
      throws NamingException
   {
      // Set host
      String url = (String) environment.get (Context.PROVIDER_URL);
      String host = new URLParser (url).getHost ();
      if (host != null)
      {
         if (setDefault)
         {
            host = defaultHost;
         }
         else if (!host.equals (this.host))
         {
            // New host name invalidates session
            this.host = host;
            connectionIsValid = false;
            serviceIsValid = false;
         }
      }

      // Set objectAttributeLevel
      String objectAttributeLevel =
                  (String) environment.get (OBJECT_ATTRIBUTE_LEVEL);
      if (objectAttributeLevel != null)
      {
         if (setDefault || objectAttributeLevel.length () == 0)
         {
            this.objectAttributeLevel = defaultObjectAttributeLevel;
         }
         else
         {
            this.objectAttributeLevel = objectAttributeLevel;
         }
      }

      // Set Disallow Referrals
      String referrals = (String) environment.get (DISALLOW_REFERRALS);
      if (referrals != null)
      {
         if (setDefault)
         {
            this.referrals = defaultReferrals;
            connectionIsValid = false;
            serviceIsValid = false;
         }
         else
         {
            boolean r = referrals.equals ("TRUE") ? true : false;

            if (r != this.referrals)
            {
               // New disallow referrals flag invalidates session
// NOTE: is this true for all transports?
               this.referrals = r;
               connectionIsValid = false;
               serviceIsValid = false;
            }
         }
      }

      // Set principal
      String principal = (String) environment.get (Context.SECURITY_PRINCIPAL);
      if (principal != null)
      {
         if (setDefault)
         {
            principal = defaultPrincipal;
         }
         else if (!principal.equals (this.principal))
         {
            // New user name invalidates session
            this.principal = principal;
            connectionIsValid = false;
            serviceIsValid = false;
         }
      }

      // Set credentials
      String credentials = (String) environment.get (Context.SECURITY_CREDENTIALS);
      if (credentials != null)
      {
         if (setDefault)
         {
            this.credentials = defaultCredentials;
         }
         else if (!credentials.equals (this.credentials))
         {
            // New password invalidates session
            this.credentials = credentials;
            connectionIsValid = false;
            serviceIsValid = false;
         }
      }

      // Set expandedAttributes
      String eaFlag = (String) environment.get (CLASS_ATTRIBUTE_LEVEL);
      if (eaFlag != null)
      {
         if (setDefault || eaFlag.length () == 0)
         {
            this.classAttributesLevel = defaultClassAttributesLevel;
            this.classAttributesLevelStr = defaultClassAttributesLevelStr;
         }
         else
         {
            // invalid value goes to: -1
            this.classAttributesLevel = -1;
            this.classAttributesLevelStr = eaFlag;

            for (int i = 0; i < defsTable.length; i++)
            {
               if (eaFlag.equals (defsTable[i]))
                  this.classAttributesLevel = i + 1;
            }
         }
      }

      // Set batchSize
      String batchSize = (String) environment.get (Context.BATCHSIZE);
      if (batchSize != null)
      {
         if (setDefault)
         {
            this.batchSize = NetBuffer.DEFAULT_MESSAGE_LEN;
         }
         else
         {
            try
            {
               this.batchSize = Integer.parseInt (batchSize);

               // Ensure limit
               if (this.batchSize > NetBuffer.MAX_MESSAGE_LEN)
               {
                  this.batchSize = NetBuffer.MAX_MESSAGE_LEN;
               }
               else if (this.batchSize < NetBuffer.MIN_MESSAGE_LEN)
               {
                  this.batchSize = NetBuffer.MIN_MESSAGE_LEN;
               }
            }
            catch (NumberFormatException e)
            {
               // Bad number, this.batchSize becomes default
               this.batchSize = NetBuffer.DEFAULT_MESSAGE_LEN;
            }
         }
      }

      // set lastConnection
      String lastConnection = (String) environment.get (LAST_CONNECTION);
      if (lastConnection != null)
      {
         if (setDefault)
         {
            this.lastConnection = defaultLastConnection;
         }
         else
         {
            this.lastConnection = lastConnection;
            setLastConnection = true;
         }
      }

      // set confidence
      Integer confidence = (Integer) environment.get (CONFIDENCE);
      if (confidence != null)
      {
         if (setDefault)
         {
            this.confidence = defaultConfidence;
         }
         else
         {
            this.confidence = confidence.intValue ();
         }
         serviceIsValid = false;
      }

      // set nameCacheDepth
      Integer nameCacheDepth = (Integer) environment.get (NAME_CACHE_DEPTH);
      if (nameCacheDepth != null)
      {
         if (setDefault)
         {
            this.nameCacheDepth = defaultNameCacheDepth;
         }
         else
         {
            this.nameCacheDepth = nameCacheDepth.intValue ();
         }
         serviceIsValid = false;
      }

      //set typelessNames
      Boolean typelessNames = (Boolean) environment.get (TYPELESS_NAMES);
      if (typelessNames != null)
      {
         if (setDefault)
         {
            this.typelessNames = defaultTypelessNames;
         }
         else
         {
            this.typelessNames = typelessNames.booleanValue ();
         }
         serviceIsValid = false;
      }

   } /* setState () */


// ******************** Service properties ********************

   /**
    *
    */
   public String getHost ()
   {
      return (host);
   }

   /**
    *
    */
   public String getServiceHost ()
   {
      return (host);
   }

   /**
    *
    */
   public boolean getDisallowReferrals ()
   {
      return (referrals);
   }

   /**
    *
    */
   int getBatchSize ()
   {
      return (batchSize);
   }


// ******************** Security properties ********************

   /**
    *
    */
   public String getPrincipal ()
   {
      return (principal);
   }

   /**
    *
    */
   public synchronized NetService getService ()
      throws NamingException
   {
      if (!serviceIsValid)
      {
         try
         {
            Session session = getSession ();

            service = (NetService) session.getService (serviceKey);
            service.setConfidence (confidence);
	    if (nameCacheDepth != defaultNameCacheDepth)
            {
               service.setNameCacheDepth (nameCacheDepth);
	    }
            service.setTypelessNames (typelessNames);
         }
         catch (SessionException e)
         {
            NamingException ne = new AuthenticationException ();

            ne.setRootCause (e);
            throw (ne);
         }
         catch (RemoteException e)
         {
            NamingException ne = new AuthenticationException ();

            ne.setRootCause (e);
            throw (ne);
         }
      }
      serviceIsValid = true;
      return (this.service);

   } /* getService () */

   /**
    *
    */
   public synchronized Session getSession ()
      throws NamingException
   {
      if (!connectionIsValid)
      {
         SessionManager manager;

         try
         {
            Session session, found;

            manager = getSessionManager ();

            // get NDSInitialSession
            found = manager.findSession (NDS.INITIAL_SESSION);

            if (null != principal)
            {
/*
               session = sessionManager.createSession (
                                             host,
                                             principal,
                                             credentials,
                                             new Hashtable ());
*/
//               session = manager.getSession (host);
               session = found.getSession (host);
            }
            else
            {
               session = found.getSession (host);
            }

            SessionAttrs type = session.getAttributes (new String[] { Session.SESSION_TYPE_ATTR_ID });

            if (((String) type.getValue (Session.SESSION_TYPE_ATTR_ID)).equals ("TREE"))
            {
               this.session = session;
            }
            else
            {
               throw new NamingException();
            }

            this.environment.put (SESSION_OBJECT, this.session);
         }
         catch (SessionException e)
         {
            NamingException ne = new AuthenticationException ();

            ne.setRootCause (e);
            throw (ne);
         }
         catch (RemoteException e)
         {
            NamingException ne = new AuthenticationException ();

            ne.setRootCause (e);
            throw (ne);
         }
      }

      // check disallow referrals flag, if true, clone, set flag
      if (!connectionIsValid && this.referrals && setLastConnection)
      {
         try
         {
            session = session.getSession (lastConnection);

            environment.put (SESSION_OBJECT, session);
         }
         catch (SessionException e)
         {
            NamingException ne = new NamingException ();

            ne.setRootCause (e);
            throw (ne);
         }
         catch (RemoteException e)
         {
            NamingException ne = new NamingException ();

            ne.setRootCause (e);
            throw (ne);
         }
      }
/*
      if (setLastConnection)
      {
         int handle;
         Hashtable contextInfo = new Hashtable ();

         ConnectionManager connectionManager;
         Connection connection;

         connectionManager = NCPClient.getConnectionManager ();
         connection = connectionManager.openConnection (
                                                lastConnection,
                                                new Hashtable ());

         handle = ((ConnectionImpl) connection).getHandle ();

         contextInfo.put (
                        new Integer (ClxJNI.DCK_LAST_CONNECTION),
                        new Integer (handle));

         this.session.setInfo (contextInfo);
         setLastConnection = false;
      }

      // check disallow referrals flag, if true, clone, set flag
      if (!connectionIsValid && this.referrals)
      {
         Session session;

         session = (Session) this.session.clone ();

         Hashtable contextInfo = session.getInfo ();

         Integer flags = (Integer) contextInfo.get (
                                       new Integer (ClxJNI.DCK_FLAGS));

         // turn DCV_DISALLOW_REFERRALS flag on
         flags = new Integer (
                        flags.intValue () | ClxJNI.DCV_DISALLOW_REFERRALS);
         contextInfo.clear ();
         contextInfo.put (new Integer (ClxJNI.DCK_FLAGS), flags);
         session.setInfo (contextInfo);
         this.session = session;
      }
*/
      connectionIsValid = true;
      return (this.session);

   } /* getSession () */

// ******************** Attribute properties ********************

   /**
    *
    */
   public String getObjectAttributeLevel ()
   {
      return (objectAttributeLevel);
   }

// ******************** Schema properties ********************

   /**
    *
    */
   public int getClassAttributesLevel ()
   {
      return (classAttributesLevel);
   }

   /**
    *
    */
   public String getClassAttributesLevelString ()
   {
      return (classAttributesLevelStr);
   }


   // ******************** ComponentContext Interface ********************

   /**
    *
    */
   protected synchronized void setServiceKey (
         String serviceKey)
   {
      this.serviceKey = serviceKey;
      serviceIsValid = false;
   }

}  /* NetEnvironment */


