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

  $Archive: /njcl_v2/src/com/novell/service/nds/naming/net/PartitionFunctions.java $
  $Revision: 12 $
  $Modtime: 1/31/00 11:40a $

  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.io.*;

import java.util.*;

import javax.naming.*;
import javax.naming.directory.*;

import com.novell.service.session.xplat.NDSContextInfo;

import com.novell.service.jncp.*;
import com.novell.service.jncpv2.clx.*;

import com.novell.service.nds.NdsReplicaPointer;
import com.novell.service.nds.NdsSyntaxId;
import com.novell.service.nds.naming.*;
import com.novell.service.nds.naming.net.NetEnvironment;
import com.novell.service.nds.NdsAttributeValue;
import com.novell.service.nds.net.NetAttributeValueFactory;

import com.novell.service.session.SessionException;
import com.novell.service.jncpv2.net.*;


/** @internal
 *
 * Class for wrapping NWDS* and other partition related functions
 */
public class PartitionFunctions
{
//   private int contextHandle = 0;
   private NetService service;
   private NetEnvironment netEnv = null;
   private static NdsNamingExceptionFactory exceptionFactory =
       new NdsNamingExceptionFactory ();

   /**
   *
   */
   public PartitionFunctions(
            NetEnvironment env)
        throws NamingException
   {
      service = env.getService ();
      netEnv = env;
   }

   /**
    *
    */
   public String GetPartitionRoot (
         String name)
      throws NamingException
   {
      try
      {
         StringBuffer partitionRoot = new StringBuffer ();

         service.getPartitionRoot (name, partitionRoot);

         return (new String (partitionRoot));
      }
      catch (NSIException e)
      {
			NamingException ne = exceptionFactory.getNamingException (e);
         throw (ne);
      }
      catch (SessionException e)
      {
         NamingException ne = new NamingException ();

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

   } /* GetPartitionRoot () */

   /**
    *
    */
   public boolean IsAPartition(
            Name name)
   {
      return IsAPartition(name.toString());
   }

   /**
    *
    */
   public boolean IsAPartition (
         String name)
   {
      String result;

      // Compare the return values
      // If equal return true
      try
      {
         name = service.removeAllTypes (name);

         result = GetPartitionRoot (name);
      }
      catch (NamingException e)
      {
         return false;
      }
      catch (NSIException e)
      {
         return false;
      }
      catch (SessionException e)
      {
         return false;
      }
      return (result.equalsIgnoreCase (name));

   }

   /**
    * Indicate if name is a partition root by calling NWDSReadObjectInfo
    * and examining flags for DS_PARTITION_ROOT
    */
   public boolean IsPartitionRoot (
         String name)
   {
      StringBuffer distinguishedName = new StringBuffer ();
      NetObjectInfo objectInfo = new NetObjectInfo ();

      // Look for DS_PARTITION_ROOT flag
      // If found return true
      try
      {
         service.readObjectInfo (
                           name,
                           distinguishedName,
                           objectInfo);
      }
      catch (NSIException e)
      {
         return false;
      }
      catch (SessionException e)
      {
         return false;
      }
      return ((objectInfo.getObjectFlags () & NetJNI.DS_PARTITION_ROOT) != 0);
   }


   /**
   *
   */
   public void SplitPartition (
            String partnName,
            int id)
        throws NamingException
   {
      try
      {
         // Call the native NWDSSplitPartition(name)
         service.splitPartition (partnName, 0);

         // partnName = service.removeAllTypes (partnName);
      }
      catch (NSIException e)
      {
			NamingException ne = exceptionFactory.getNamingException (e);
         throw (ne);
      }
      catch (SessionException e)
      {
         NamingException ne = new NamingException ();

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

      PendingOpObject object = new PendingOpObject (
                                            partnName,
                                            service,
                                            NdsPartitionStrings.SPLIT,
                                            id);
      Hashtable h = NetPartitionRootContext.getPendingPartitionOperations();

      // Wait till the operation is over
      //while (partnName.equalsIgnoreCase (GetPartitionRoot(partnName)) != true)
      while(!IsPartitionRoot (partnName))
      {
        // wait for 2 seconds
        try
        {
           Thread.sleep(2000l);
        }
        catch(InterruptedException e)
        {
            // Ignore it
        }
        // Check for the partnName to be in the pending list
        if(h.containsKey(object) == true)
        {
            // Operation still pending, so continue in the loop
            continue;
        }
        else
        {
            throw new OperationAbortedException(NdsPartitionStrings.SPLIT);
        }
      }
   }

   public void JoinPartition(
            String partnName,
            int id)
        throws NamingException
   {
      try
      {
         // Call the native NWDSJointPartition(name)
         service.joinPartitions (partnName, 0);

         // partnName = service.removeAllTypes (partnName);
      }
      catch (NSIException e)
      {
			NamingException ne = exceptionFactory.getNamingException (e);
         throw (ne);
      }
      catch (SessionException e)
      {
         NamingException ne = new NamingException ();

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

      Hashtable h = NetPartitionRootContext.getPendingPartitionOperations();
      PendingOpObject object = new PendingOpObject(
                                            partnName,
                                            service,
                                            NdsPartitionStrings.JOIN,
                                            id);

      // Wait till the partition is merged
//      while(partnName.equalsIgnoreCase (GetPartitionRoot(partnName)) == true)
      while(IsPartitionRoot (partnName))
      {
        // wait for 2 seconds
        try
        {
           Thread.sleep(2000l);
        }
        catch(InterruptedException e)
        {
            // Ignore it
        }
        // Check for the partnName to be in the pending list
        if(h.containsKey(object) == true)
        {
            // Operation still pending, so continue in the loop
            continue;
        }
        else
        {
            throw new OperationAbortedException(NdsPartitionStrings.JOIN);
        }
      }
   }


   /**
    *
    */
   public boolean IsAReplica (
         String replicaName,
         String partnName)
      throws NamingException
   {
      NetReplicaNameClassEnumerator re = null;
      NameClassPair np;

      // make sure we have a typeless name
      replicaName =
         new NdsName (replicaName, new NetSyntax ()).getTypelessName ().getCanonicalString ();
      
      try
      {
         re = new NetReplicaNameClassEnumerator (netEnv, partnName);

         while (re.hasMore ())
         {
             np = (NameClassPair) re.next ();
             if (replicaName.equalsIgnoreCase (np.getName ()))
                 return true;
         }
      }
      catch  (NamingException e)
      {
      }
      return false;

   }

   /**
   *
   */
   public void AddReplica(
            String replicaName,
            String partnName,
            int replicaType)
        throws NamingException
   {
      try
      {
         // Call the native NWDSAddReplica()
         service.addReplica(
                                replicaName,
                                partnName,
                                replicaType);
      }
      catch (NSIException e)
      {
			NamingException ne = exceptionFactory.getNamingException (e);
         throw (ne);
      }
      catch (SessionException e)
      {
         NamingException ne = new NamingException ();

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

   }

   /**
   *
   */
   public void RemoveReplica(
            String replicaName,
            String partnName)
        throws NamingException
   {
      try
      {
         // Call the native NWDSRemoveReplica()
         service.removeReplica (replicaName, partnName);
      }
      catch (NSIException e)
      {
			NamingException ne = exceptionFactory.getNamingException (e);
         throw (ne);
      }
      catch (SessionException e)
      {
         NamingException ne = new NamingException ();

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

   }


   /**
   *
   */
   public void AbortOperations(
            String partnName)
        throws NdsPartitionOperationException
   {
      try
      {
         service.abortPartitionOperation (partnName);
      }
      catch (NSIException e)
      {
         NdsPartitionOperationException ne = new NdsPartitionOperationException ();

         ne.setRootCause (e);
         throw (ne);
      }
      catch (SessionException e)
      {
         NdsPartitionOperationException ne = new NdsPartitionOperationException ();

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

   }


   /**
    *
    */
   public DirContext lookupNDSObject (
         String name)
   {
//      NdsDirContext ctx;
      NetContextFactory factory = new NetContextFactory ();

      try
      {
//Note: Do NOT use System properties !!!
        return ((DirContext) factory.getObjectInstance (
                                          "",
                                          null,
                                          null,
                                          System.getProperties ()));
      }
      catch (Exception e)
      {
//Note: this method should throw NamingException
         //ignore it
      }
      return (null);
   }

   /**
    *
    */
   public NdsReplicaPointer getReplicaInfo (
         String partitionDN,
         String replicaDN)
      throws NamingException
   {
      try
      {
         NetAttributeValueFactory factory;
         NetAttributeValueEnumerator  replicaInfo;

         factory = new NetAttributeValueFactory();
         replicaInfo = new NetAttributeValueEnumerator (
                             service,
                             partitionDN,
                             "Replica");

         while (replicaInfo.hasMoreElements ())
         {
            NdsAttributeValue value;
//            NdsReplicaPointer replicaPointer;

            value = factory.createValue(
                                 NdsSyntaxId.REPLICA_POINTER_ID,
                                 (byte[]) replicaInfo.nextElement (),
                                 null,
                                 null,
                                 partitionDN,
                                 "Replica");

            if (replicaDN.equalsIgnoreCase (
                              ((NdsReplicaPointer) value).getServerName ()))
            {
               return ((NdsReplicaPointer) value);
            }
         }
      }
      catch (NSIException e)
      {
			NamingException ne = exceptionFactory.getNamingException (e);
         throw (ne);
      }
      return (null);

    } /* getReplicaInfo () */


   /**
    *
    */
   public NetReplicaSyncInfo getReplicaSyncInfo(
            String partitionDN,
            String replicaDN)
        throws NamingException
    {

        NdsReplicaPointer replicaInfo =
                this.getReplicaInfo(partitionDN, replicaDN);

        NetAttributeValueEnumerator  partitionStatus = null;
        NetReplicaSyncInfo partitionSyncInfo = null;
        NetReplicaSyncInfo replicaSyncInfo = null;

        try
        {
            partitionStatus = new NetAttributeValueEnumerator(
                 service, partitionDN, "Partition Status");

            while (partitionStatus.hasMoreElements()) {

                byte[] value = (byte[]) partitionStatus.nextElement ();

                NetReplicaSyncInfo syncInfo = new NetReplicaSyncInfo (value);

                if(syncInfo.getReplicaNumber() == 0)
                    partitionSyncInfo = syncInfo;
                else if (replicaInfo != null && syncInfo.getReplicaNumber() ==
                                                replicaInfo.getReplicaNumber())
                {
                    replicaSyncInfo = syncInfo;
                    break;
                }
            }
        }
        catch (NSIException e)
        {
           NamingException ne = exceptionFactory.getNamingException (e);
           throw ne;
        }
        catch (IOException e)
        {
            NamingException ne = new NamingException();
            ne.setRootCause(e);
            throw ne;
        }

        if (partitionSyncInfo == null)
            throw new NdsPartitionOperationException("Invalid Partition Status");

        if (replicaSyncInfo == null)
            replicaSyncInfo = partitionSyncInfo;

        if (partitionSyncInfo != replicaSyncInfo) {
            replicaSyncInfo.setLastSyncTime (partitionSyncInfo.getLastSyncTime());
            replicaSyncInfo.setReplicaNumber (replicaInfo.getReplicaNumber());
        }

        return replicaSyncInfo;
    }

   /**
    * Read the "Partition Status" attribute of the partition root object.
    * From the list of its attribute values, collect the information that the
    * users of JNDI + NDS service provider want. This includes 4 pieces of
    * information: 1) The name of the effected entry,
    * 2) The last successful synchronization time of all replica's of the
    * named partition, 3) The last attempted synchronization time for all
    * replica's of the named partition, and 4) The name of the server we read
	 * the partition sync info from. This function will create, populate
	 * and return a NetPartitionSyncInfo object to NetPartitionAttributes.
    *
    * @return NetPartitionSyncInfo object containing the specified info.
    */
   public NetPartitionSyncInfo getPartitionSyncInfo(
            String partitionDN)
        throws NamingException
    {
        NetAttributeValueEnumerator partitionStatus = null;
        NetPartitionSyncInfo partSyncInfo = new NetPartitionSyncInfo();

        try
        {
            partitionStatus = new NetAttributeValueEnumerator(
                 service, partitionDN, "Partition Status");

            while (partitionStatus.hasMoreElements()) {

                byte[] value = (byte[]) partitionStatus.nextElement ();

                /* We use a NetReplicaSyncInfo object to parse the partition
                 * status attribute value information. We do this because that
                 * class has already been coded.
                 */
                NetReplicaSyncInfo syncInfo = new NetReplicaSyncInfo (value);

                /* details explaining status flags are in NetReplicaSyncInfo.
                 */
                if(syncInfo.getStatusFlags() == 2) { // ok
                   partSyncInfo.setLastSuccessfulSyncTime(
                      syncInfo.getLastSyncTime());
						 partSyncInfo.setEntryName(syncInfo.getEntryName());
                }
                else if(syncInfo.getStatusFlags()   == 0 &&
                        syncInfo.getReplicaNumber() == 0) { // marker
                   partSyncInfo.setLastAttemptedSyncTime(
                      syncInfo.getLastSyncTime());
                }
            }
        }
        catch (NSIException e)
        {
           NamingException ne = exceptionFactory.getNamingException (e);
           throw ne;
        }
        catch (IOException e)
        {
            NamingException ne = new NamingException();
            ne.setRootCause(e);
            throw ne;
        }

        /*
         * if we didn't get a last attempted sync time, set it to last
         * successful sync time.
         */
        if(partSyncInfo.getLastAttemptedSyncTime() == 0) {
			  partSyncInfo.setLastAttemptedSyncTime(
				  partSyncInfo.getLastSuccessfulSyncTime());
        }

		  /*
		   * get the name of the server we read the partition status info from
			*/
		  try
		  {
			  partSyncInfo.setServerRead(service.getServerDN());
		  }
		  catch (SessionException e)
		  {
			  NamingException ne = new NamingException ();

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

        return partSyncInfo;

    } /* getPartitionSyncInfo () */

    /**
    *
    */
    public NetReplicaServerInfo getReplicaServerInfo(
            String partitionDN,
            String serverDN)
        throws NamingException
    {
        try {
            return new NetReplicaServerInfo(netEnv, partitionDN, serverDN);
        }
        catch (NSIException e) {
           NamingException ne = exceptionFactory.getNamingException (e);
           throw ne;
        }
    }


    /**
    *
    */
    public void changeReplicaType(
            String replicaName,
            String partnName,
            int type)
        throws NdsPartitionOperationException
    {
      try
      {
         service.changeReplicaType(
                            partnName,
                            replicaName,
                            type);
      }
      catch (NSIException e)
      {
         NdsPartitionOperationException ne =
                     new NdsPartitionOperationException();
         ne.setRootCause(e);
         throw ne;
      }
      catch (SessionException e)
      {
         NdsPartitionOperationException ne =
                     new NdsPartitionOperationException();
         ne.setRootCause(e);
         throw ne;
      }

    }

    public void syncReplica(String replicaName,
            String partnName,
            String destServer,
            int flags,
            int delay)
        throws NdsPartitionOperationException
    {
      try
      {
         service.syncReplicaToServer (
                        replicaName,
                        partnName,
                        destServer,
                        flags, delay);
      }
      catch (NSIException e)
      {
         NdsPartitionOperationException ne =
                     new NdsPartitionOperationException();
         ne.setRootCause(e);
         throw ne;
      }
      catch (SessionException e)
      {
         NdsPartitionOperationException ne =
                     new NdsPartitionOperationException();
         ne.setRootCause(e);
         throw ne;
      }

    }

    public void syncReplica(
            String replicaName,
            String partnName,
            int delay)
        throws NdsPartitionOperationException
    {
      try
      {
         service.syncPartition(
                            replicaName,
                            partnName,
                            delay);
      }
      catch (NSIException e)
      {
         NdsPartitionOperationException ne =
                     new NdsPartitionOperationException();
         ne.setRootCause(e);
         throw ne;
      }
      catch (SessionException e)
      {
         NdsPartitionOperationException ne =
                     new NdsPartitionOperationException();
         ne.setRootCause(e);
         throw ne;
      }

/*
      // First duplicate the context
      int newContextHandle = ClientJNI.NWDSDuplicateContext(contextHandle);

      // Set the context
      int flags  = ClientJNI.DCV_DEREF_ALIASES |
                   ClientJNI.DCV_XLATE_STRINGS |
                   ClientJNI.DCV_TYPELESS_NAMES |
                   ClientJNI.DCV_CANONICALIZE_NAMES;
      if((result = ClientJNI.NWDSSetContext(
                        newContextHandle,
                        ClientJNI.DCK_FLAGS,
                        new Integer(flags))) != 0)
      {

         PartitionOperationException e = new PartitionOperationException();
         e.setRootCause(NSIExceptionBuilder.build(result));
         throw e;
      }

      if((result = ClientJNI.NWDSSetContext(
                        newContextHandle,
                        ClientJNI.DCK_NAME_CONTEXT,
                        "[Root]")) != 0)
      {
         PartitionOperationException e = new PartitionOperationException();
         e.setRootCause(NSIExceptionBuilder.build(result));
         throw e;
      }

      result  = NetJNI.NWDSSyncPartition(
                            newContextHandle,
                            replicaName,
                            partnName,
                            delay);

      // Free the context
      ClientJNI.NWDSFreeContext(newContextHandle);

      if(result != 0)
      {
         // Throws Exception
         PartitionOperationException e = new PartitionOperationException();
         e.setRootCause(NSIExceptionBuilder.build(result));
         throw e;
      }
*/
    }

   /**
    *
    * /
   public String getLastServerRead ()
   {
      NDSContextInfo ctxInfo = new NDSContextInfo ();
      StringBuffer server = new StringBuffer ("");
      Integer conn;
      int ccode;

      ccode = ClxJNI.NWDSGetContext (
                          contextHandle,
                          ClxJNI.DCK_LAST_CONNECTION,
                          ctxInfo);

      if (ccode == 0)
      {
         conn= (Integer) ctxInfo.getValue ();
         ccode = ClxJNI.NWCGetConnInfoString (
                              conn.intValue(),
                              0,
                              ClxJNI.INFO_SERVER_NAME_NUM,
                              server);
      }
      return server.toString ();

   }   */

} /*  */


