manifoldcf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kwri...@apache.org
Subject svn commit: r1545624 [1/5] - in /manifoldcf/trunk: ./ framework/agents/src/main/java/org/apache/manifoldcf/agents/ framework/agents/src/main/java/org/apache/manifoldcf/agents/agentmanager/ framework/agents/src/main/java/org/apache/manifoldcf/agents/int...
Date Tue, 26 Nov 2013 11:45:55 GMT
Author: kwright
Date: Tue Nov 26 11:45:53 2013
New Revision: 1545624

URL: http://svn.apache.org/r1545624
Log:
More work on CONNECTORS-781.  Commit changes having to do with database handling of multiple agents processes.  WARNING: Schema change!!

Added:
    manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/interfaces/IServiceCleanup.java
      - copied unchanged from r1545621, manifoldcf/branches/CONNECTORS-781/framework/core/src/main/java/org/apache/manifoldcf/core/interfaces/IServiceCleanup.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/DeleteStartupResetManager.java
      - copied unchanged from r1545621, manifoldcf/branches/CONNECTORS-781/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/DeleteStartupResetManager.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/NotificationResetManager.java
      - copied unchanged from r1545621, manifoldcf/branches/CONNECTORS-781/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/NotificationResetManager.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/SeedingResetManager.java
      - copied unchanged from r1545621, manifoldcf/branches/CONNECTORS-781/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/SeedingResetManager.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/StartupResetManager.java
      - copied unchanged from r1545621, manifoldcf/branches/CONNECTORS-781/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/StartupResetManager.java
Modified:
    manifoldcf/trunk/   (props changed)
    manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/AgentRun.java
    manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/AgentStop.java
    manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/SynchronizeAll.java
    manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/agentmanager/AgentManager.java
    manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/interfaces/AgentFactory.java
    manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/interfaces/AgentManagerFactory.java
    manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/interfaces/IAgent.java
    manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/interfaces/OutputConnectorFactory.java
    manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/system/ManifoldCF.java
    manifoldcf/trunk/framework/combined-service/src/main/java/org/apache/manifoldcf/combinedservice/ServletListener.java
    manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/interfaces/ILockManager.java
    manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/lockmanager/BaseLockManager.java
    manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/lockmanager/FileLockManager.java
    manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/lockmanager/LockManager.java
    manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/lockmanager/ZooKeeperConnection.java
    manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/lockmanager/ZooKeeperLockManager.java
    manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/system/ManifoldCF.java
    manifoldcf/trunk/framework/jetty-runner/src/main/java/org/apache/manifoldcf/jettyrunner/ManifoldCFJettyRunner.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/IJobManager.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/Carrydown.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/EventManager.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/HopCount.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/IntrinsicLink.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/JobManager.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/JobQueue.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/Jobs.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/CrawlerAgent.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/DocCleanupResetManager.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/DocDeleteResetManager.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/DocumentCleanupStufferThread.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/DocumentCleanupThread.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/DocumentDeleteStufferThread.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/DocumentDeleteThread.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/ExpireStufferThread.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/ExpireThread.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/FinisherThread.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/HistoryCleanupThread.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/IdleCleanupThread.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/JobDeleteThread.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/JobNotificationThread.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/JobResetThread.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/JobStartThread.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/ManifoldCF.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/ResetManager.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/SeedingActivity.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/SeedingThread.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/SetPriorityThread.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/StartDeleteThread.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/StartupThread.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/StufferThread.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/WorkerResetManager.java
    manifoldcf/trunk/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/WorkerThread.java
    manifoldcf/trunk/framework/pull-agent/src/test/java/org/apache/manifoldcf/crawler/tests/ManifoldCFInstance.java

Propchange: manifoldcf/trunk/
------------------------------------------------------------------------------
  Merged /manifoldcf/branches/CONNECTORS-781:r1542879-1545621

Modified: manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/AgentRun.java
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/AgentRun.java?rev=1545624&r1=1545623&r2=1545624&view=diff
==============================================================================
--- manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/AgentRun.java (original)
+++ manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/AgentRun.java Tue Nov 26 11:45:53 2013
@@ -28,8 +28,7 @@ public class AgentRun extends BaseAgents
 {
   public static final String _rcsid = "@(#)$Id: AgentRun.java 988245 2010-08-23 18:39:35Z kwright $";
 
-  public static final String agentInUseSignal = "_AGENTINUSE_";
-  public static final String agentShutdownSignal = "_AGENTRUN_";
+  public static final String agentServiceType = "AGENT";
   
   public AgentRun()
   {
@@ -37,41 +36,31 @@ public class AgentRun extends BaseAgents
 
   protected void doExecute(IThreadContext tc) throws ManifoldCFException
   {
+    // Note well:
+    // As part of CONNECTORS-781, multiple agents processes are now permitted, provided
+    // that a truly global lock manager implementation is used.  This implementation thus does the
+    // following:
+    // (1) Register the agent, and begin its execution
+    // (2) Periodically check for any new registered IAgent implementations
+    // (3) Await a shutdown signal
+    // (4) If exit signal seen, exit active block
+    // (5) Trap JVM exit to be sure we exit active block no matter what
+    //   (This latter option requires the ability to exit active blocks from different ILockManager instances)
+    //
+    // Note well that the agents shutdown signal is NEVER modified by this code; it will be set/cleared by
+    // AgentStop only, and AgentStop will wait until all services become inactive before exiting.
+    String processID = ManifoldCF.getProcessID();
     ILockManager lockManager = LockManagerFactory.make(tc);
-    // Agent already in use?
-    if (lockManager.checkGlobalFlag(agentInUseSignal))
-    {
-      System.err.println("Agent already in use");
-      System.exit(1);
-    }
-    
-    ManifoldCF.addShutdownHook(new AgentRunShutdownRunner());
-    
-    // Set the agents in use signal.
-    lockManager.setGlobalFlag(agentInUseSignal);    
+    lockManager.registerServiceBeginServiceActivity(agentServiceType, processID, null);
     try
     {
-      // Clear the agents shutdown signal.
-      lockManager.clearGlobalFlag(agentShutdownSignal);
+      // Register a shutdown hook to make sure we signal that the main agents process is going inactive.
+      ManifoldCF.addShutdownHook(new AgentRunShutdownRunner(processID));
+      
       Logging.root.info("Running...");
-      while (true)
-      {
-        // Any shutdown signal yet?
-        if (lockManager.checkGlobalFlag(agentShutdownSignal))
-          break;
-
-        // Start whatever agents need to be started
-        ManifoldCF.startAgents(tc);
-
-        try
-        {
-          ManifoldCF.sleep(5000);
-        }
-        catch (InterruptedException e)
-        {
-          break;
-        }
-      }
+      // Register hook first so stopAgents() not required
+      ManifoldCF.registerAgentsShutdownHook(tc, processID);
+      ManifoldCF.runAgents(tc, processID);
       Logging.root.info("Shutting down...");
     }
     catch (ManifoldCFException e)
@@ -81,7 +70,9 @@ public class AgentRun extends BaseAgents
     }
     finally
     {
-      lockManager.clearGlobalFlag(agentInUseSignal);
+      // Exit service
+      // This is a courtesy; some lock managers (i.e. ZooKeeper) manage to do this anyway
+      lockManager.endServiceActivity(agentServiceType, processID);
     }
   }
 
@@ -111,8 +102,11 @@ public class AgentRun extends BaseAgents
   
   protected static class AgentRunShutdownRunner implements IShutdownHook
   {
-    public AgentRunShutdownRunner()
+    protected final String processID;
+    
+    public AgentRunShutdownRunner(String processID)
     {
+      this.processID = processID;
     }
     
     public void doCleanup()
@@ -120,7 +114,12 @@ public class AgentRun extends BaseAgents
     {
       IThreadContext tc = ThreadContextFactory.make();
       ILockManager lockManager = LockManagerFactory.make(tc);
-      lockManager.clearGlobalFlag(agentInUseSignal);
+      // We can blast the active flag off here; we may have already exited though and an exception will
+      // therefore be thrown.
+      if (lockManager.checkServiceActive(agentServiceType, processID))
+      {
+        lockManager.endServiceActivity(agentServiceType, processID);
+      }
     }
     
   }

Modified: manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/AgentStop.java
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/AgentStop.java?rev=1545624&r1=1545623&r2=1545624&view=diff
==============================================================================
--- manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/AgentStop.java (original)
+++ manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/AgentStop.java Tue Nov 26 11:45:53 2013
@@ -34,9 +34,35 @@ public class AgentStop extends BaseAgent
 
   protected void doExecute(IThreadContext tc) throws ManifoldCFException
   {
+    // As part of the work for CONNECTORS-781, this method is now synchronous.
+    // We assert the shutdown signal, and then wait until all active services have shut down.
     ILockManager lockManager = LockManagerFactory.make(tc);
-    lockManager.setGlobalFlag(AgentRun.agentShutdownSignal);
-    Logging.root.info("Shutdown signal sent");
+    ManifoldCF.assertAgentsShutdownSignal(tc);
+    try
+    {
+      Logging.root.info("Shutdown signal sent");
+      while (true)
+      {
+        // Check to see if services are down yet
+        int count = lockManager.countActiveServices(AgentRun.agentServiceType);
+        if (count == 0)
+          break;
+        try
+        {
+          ManifoldCF.sleep(1000L);
+        }
+        catch (InterruptedException e)
+        {
+          throw new ManifoldCFException(e.getMessage(),e,ManifoldCFException.INTERRUPTED);
+        }
+      }
+      Logging.root.info("All agents shut down");
+    }
+    finally
+    {
+      // Clear shutdown signal
+      ManifoldCF.clearAgentsShutdownSignal(tc);
+    }
   }
 
 

Modified: manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/SynchronizeAll.java
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/SynchronizeAll.java?rev=1545624&r1=1545623&r2=1545624&view=diff
==============================================================================
--- manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/SynchronizeAll.java (original)
+++ manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/SynchronizeAll.java Tue Nov 26 11:45:53 2013
@@ -43,7 +43,7 @@ public class SynchronizeAll extends Base
       String classname = classnames[i++];
       try
       {
-        AgentFactory.make(tc,classname);
+        AgentFactory.make(classname);
       }
       catch (ManifoldCFException e)
       {

Modified: manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/agentmanager/AgentManager.java
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/agentmanager/AgentManager.java?rev=1545624&r1=1545623&r2=1545624&view=diff
==============================================================================
--- manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/agentmanager/AgentManager.java (original)
+++ manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/agentmanager/AgentManager.java Tue Nov 26 11:45:53 2013
@@ -98,8 +98,8 @@ public class AgentManager extends org.ap
       {
         IResultRow row = set.getRow(i++);
         String className = row.getValue(classNameField).toString();
-        IAgent agent = AgentFactory.make(threadContext,className);
-        agent.deinstall();
+        IAgent agent = AgentFactory.make(className);
+        agent.deinstall(threadContext);
       }
       performDrop(null);
     }
@@ -142,8 +142,8 @@ public class AgentManager extends org.ap
         performInsert(map,null);
       }
       // In any case, call the install/upgrade method
-      IAgent agent = AgentFactory.make(threadContext,className);
-      agent.install();
+      IAgent agent = AgentFactory.make(className);
+      agent.install(threadContext);
     }
     catch (ManifoldCFException e)
     {
@@ -173,8 +173,8 @@ public class AgentManager extends org.ap
     try
     {
       // First, deregister agent
-      IAgent agent = AgentFactory.make(threadContext,className);
-      agent.deinstall();
+      IAgent agent = AgentFactory.make(className);
+      agent.deinstall(threadContext);
 
       // Remove from table
       removeAgent(className);

Modified: manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/interfaces/AgentFactory.java
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/interfaces/AgentFactory.java?rev=1545624&r1=1545623&r2=1545624&view=diff
==============================================================================
--- manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/interfaces/AgentFactory.java (original)
+++ manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/interfaces/AgentFactory.java Tue Nov 26 11:45:53 2013
@@ -27,8 +27,6 @@ public class AgentFactory
 {
   public static final String _rcsid = "@(#)$Id: AgentFactory.java 988245 2010-08-23 18:39:35Z kwright $";
 
-  protected static final String agentIdentifier = "_Agent_";
-
   private AgentFactory()
   {
   }
@@ -38,73 +36,65 @@ public class AgentFactory
   *@param className is the agent class name.
   *@return the agent.
   */
-  public static IAgent make(IThreadContext tc, String className)
+  public static IAgent make(String className)
     throws ManifoldCFException
   {
-    String agentName = agentIdentifier+className;
-    Object o = tc.get(agentName);
-    if (o == null || !(o instanceof IAgent))
-    {
-      try
-      {
-        Class theClass = Class.forName(className);
-        Class[] argumentClasses = new Class[1];
-        argumentClasses[0] = IThreadContext.class;
-        // Look for a constructor
-        Constructor c = theClass.getConstructor(argumentClasses);
-        Object[] arguments = new Object[1];
-        arguments[0] = tc;
-        o = c.newInstance(arguments);
-        if (!(o instanceof IAgent))
-          throw new ManifoldCFException("Class '"+className+"' does not implement IAgent.");
-        tc.save(agentName,o);
-      }
-      catch (InvocationTargetException e)
-      {
-        Throwable z = e.getTargetException();
-        if (z instanceof Error)
-          throw (Error)z;
-        else
-          throw (ManifoldCFException)z;
-      }
-      catch (ClassNotFoundException e)
-      {
-        throw new ManifoldCFException("No class implementing IAgent called '"+
-          className+"'.",
-          e);
-      }
-      catch (NoSuchMethodException e)
-      {
-        throw new ManifoldCFException("No appropriate constructor for IAgent implementation '"+
-          className+"'.  Need xxx(ConfigParams).",
-          e);
-      }
-      catch (SecurityException e)
-      {
-        throw new ManifoldCFException("Protected constructor for IAgent implementation '"+className+"'",
-          e);
-      }
-      catch (IllegalAccessException e)
-      {
-        throw new ManifoldCFException("Unavailable constructor for IAgent implementation '"+className+"'",
-          e);
-      }
-      catch (IllegalArgumentException e)
-      {
-        throw new ManifoldCFException("Shouldn't happen!!!",e);
-      }
-      catch (InstantiationException e)
-      {
-        throw new ManifoldCFException("InstantiationException for IAgent implementation '"+className+"'",
-          e);
-      }
-      catch (ExceptionInInitializerError e)
-      {
-        throw new ManifoldCFException("ExceptionInInitializerError for IAgent implementation '"+className+"'",
-          e);
-      }
+    try
+    {
+      Class theClass = Class.forName(className);
+      Class[] argumentClasses = new Class[0];
+      // Look for a constructor
+      Constructor c = theClass.getConstructor(argumentClasses);
+      Object[] arguments = new Object[0];
+      Object o = c.newInstance(arguments);
+      if (!(o instanceof IAgent))
+        throw new ManifoldCFException("Class '"+className+"' does not implement IAgent.");
+      return (IAgent)o;
+    }
+    catch (InvocationTargetException e)
+    {
+      Throwable z = e.getTargetException();
+      if (z instanceof Error)
+        throw (Error)z;
+      else
+        throw (ManifoldCFException)z;
+    }
+    catch (ClassNotFoundException e)
+    {
+      throw new ManifoldCFException("No class implementing IAgent called '"+
+        className+"'.",
+        e);
+    }
+    catch (NoSuchMethodException e)
+    {
+      throw new ManifoldCFException("No appropriate constructor for IAgent implementation '"+
+        className+"'.  Need xxx().",
+        e);
+    }
+    catch (SecurityException e)
+    {
+      throw new ManifoldCFException("Protected constructor for IAgent implementation '"+className+"'",
+        e);
+    }
+    catch (IllegalAccessException e)
+    {
+      throw new ManifoldCFException("Unavailable constructor for IAgent implementation '"+className+"'",
+        e);
+    }
+    catch (IllegalArgumentException e)
+    {
+      throw new ManifoldCFException("Shouldn't happen!!!",e);
+    }
+    catch (InstantiationException e)
+    {
+      throw new ManifoldCFException("InstantiationException for IAgent implementation '"+className+"'",
+        e);
+    }
+    catch (ExceptionInInitializerError e)
+    {
+      throw new ManifoldCFException("ExceptionInInitializerError for IAgent implementation '"+className+"'",
+        e);
     }
-    return (IAgent)o;
   }
 
 }

Modified: manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/interfaces/AgentManagerFactory.java
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/interfaces/AgentManagerFactory.java?rev=1545624&r1=1545623&r2=1545624&view=diff
==============================================================================
--- manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/interfaces/AgentManagerFactory.java (original)
+++ manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/interfaces/AgentManagerFactory.java Tue Nov 26 11:45:53 2013
@@ -67,7 +67,7 @@ public class AgentManagerFactory
     int i = 0;
     while (i < theAgents.length)
     {
-      if (theAgents[i++].isOutputConnectionInUse(connName))
+      if (theAgents[i++].isOutputConnectionInUse(threadContext, connName))
         return true;
     }
     return false;
@@ -86,7 +86,7 @@ public class AgentManagerFactory
     int i = 0;
     while (i < theAgents.length)
     {
-      theAgents[i++].noteOutputConnectorDeregistration(connectionNames);
+      theAgents[i++].noteOutputConnectorDeregistration(threadContext, connectionNames);
     }
   }
 
@@ -104,7 +104,7 @@ public class AgentManagerFactory
     int i = 0;
     while (i < theAgents.length)
     {
-      theAgents[i++].noteOutputConnectorRegistration(connectionNames);
+      theAgents[i++].noteOutputConnectorRegistration(threadContext, connectionNames);
     }
   }
 
@@ -121,7 +121,7 @@ public class AgentManagerFactory
     int i = 0;
     while (i < theAgents.length)
     {
-      theAgents[i++].noteOutputConnectionChange(connectionName);
+      theAgents[i++].noteOutputConnectionChange(threadContext, connectionName);
     }
   }
 
@@ -138,7 +138,7 @@ public class AgentManagerFactory
     int i = 0;
     while (i < rval.length)
     {
-      rval[i] = AgentFactory.make(threadContext,allAgents[i]);
+      rval[i] = AgentFactory.make(allAgents[i]);
       i++;
     }
     return rval;

Modified: manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/interfaces/IAgent.java
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/interfaces/IAgent.java?rev=1545624&r1=1545623&r2=1545624&view=diff
==============================================================================
--- manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/interfaces/IAgent.java (original)
+++ manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/interfaces/IAgent.java Tue Nov 26 11:45:53 2013
@@ -24,57 +24,101 @@ import org.apache.manifoldcf.core.interf
 * start-up time; they run independently until the JVM is shut down.
 * All agent classes are expected to support the following constructor:
 *
-* xxx(IThreadContext tc) throws ManifoldCFException
+* xxx() throws ManifoldCFException
 *
+* Agent classes are furthermore expected to be cross-thread, but not necessarily thread-safe
+* in that a given IAgent instance is meant to be used by only one thread at a time.  It is
+* furthermore safe to keep stateful data in the IAgent instance object pertaining to the
+* running state of the system.  That is, an instance of IAgent used to start the agent will be
+* the same one stopAgent() is called with.
 */
 public interface IAgent
 {
   public static final String _rcsid = "@(#)$Id: IAgent.java 988245 2010-08-23 18:39:35Z kwright $";
 
+  /** Initialize agent environment.
+  * This is called before any of the other operations are called, and is meant to insure that
+  * the environment is properly initialized.
+  */
+  public void initialize(IThreadContext threadContext)
+    throws ManifoldCFException;
+  
+  /** Tear down agent environment.
+  * This is called after all the other operations are completed, and is meant to allow
+  * environment resources to be freed.
+  */
+  public void cleanUp(IThreadContext threadContext)
+    throws ManifoldCFException;
+  
   /** Install agent.  This usually installs the agent's database tables etc.
   */
-  public void install()
+  public void install(IThreadContext threadContext)
     throws ManifoldCFException;
 
   /** Uninstall agent.  This must clean up everything the agent is responsible for.
   */
-  public void deinstall()
+  public void deinstall(IThreadContext threadContext)
+    throws ManifoldCFException;
+
+  /** Called ONLY when no other active services of this kind are running.  Meant to be
+  * used after the cluster has been down for an indeterminate period of time.
+  */
+  public void clusterInit(IThreadContext threadContext)
+    throws ManifoldCFException;
+    
+  /** Cleanup after ALL agents processes.
+  * Call this method to clean up dangling persistent state when a cluster is just starting
+  * to come up.  This method CANNOT be called when there are any active agents
+  * processes at all.
+  */
+  public void cleanUpAgentData(IThreadContext threadContext)
+    throws ManifoldCFException;
+  
+  /** Cleanup after agents process.
+  * Call this method to clean up dangling persistent state after agent has been stopped.
+  * This method CANNOT be called when the agent is active, but it can
+  * be called at any time and by any process in order to guarantee that a terminated
+  * agent does not block other agents from completing their tasks.
+  *@param processID is the process ID of the agent to clean up after.
+  */
+  public void cleanUpAgentData(IThreadContext threadContext, String processID)
     throws ManifoldCFException;
 
   /** Start the agent.  This method should spin up the agent threads, and
   * then return.
+  *@param processID is the process ID to start up an agent for.
   */
-  public void startAgent()
+  public void startAgent(IThreadContext threadContext, String processID)
     throws ManifoldCFException;
 
   /** Stop the agent.  This should shut down the agent threads.
   */
-  public void stopAgent()
+  public void stopAgent(IThreadContext threadContext)
     throws ManifoldCFException;
 
   /** Request permission from agent to delete an output connection.
   *@param connName is the name of the output connection.
   *@return true if the connection is in use, false otherwise.
   */
-  public boolean isOutputConnectionInUse(String connName)
+  public boolean isOutputConnectionInUse(IThreadContext threadContext, String connName)
     throws ManifoldCFException;
 
   /** Note the deregistration of a set of output connections.
   *@param connectionNames are the names of the connections being deregistered.
   */
-  public void noteOutputConnectorDeregistration(String[] connectionNames)
+  public void noteOutputConnectorDeregistration(IThreadContext threadContext, String[] connectionNames)
     throws ManifoldCFException;
 
   /** Note the registration of a set of output connections.
   *@param connectionNames are the names of the connections being registered.
   */
-  public void noteOutputConnectorRegistration(String[] connectionNames)
+  public void noteOutputConnectorRegistration(IThreadContext threadContext, String[] connectionNames)
     throws ManifoldCFException;
 
   /** Note a change in configuration for an output connection.
   *@param connectionName is the name of the connection being changed.
   */
-  public void noteOutputConnectionChange(String connectionName)
+  public void noteOutputConnectionChange(IThreadContext threadContext, String connectionName)
     throws ManifoldCFException;
   
 }

Modified: manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/interfaces/OutputConnectorFactory.java
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/interfaces/OutputConnectorFactory.java?rev=1545624&r1=1545623&r2=1545624&view=diff
==============================================================================
--- manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/interfaces/OutputConnectorFactory.java (original)
+++ manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/interfaces/OutputConnectorFactory.java Tue Nov 26 11:45:53 2013
@@ -162,7 +162,7 @@ public class OutputConnectorFactory
     catch (NoSuchMethodException e)
     {
       throw new ManifoldCFException("No appropriate constructor for IOutputConnector implementation '"+
-        className+"'.  Need xxx(ConfigParams).",
+        className+"'.  Need xxx().",
         e);
     }
     catch (SecurityException e)

Modified: manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/system/ManifoldCF.java
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/system/ManifoldCF.java?rev=1545624&r1=1545623&r2=1545624&view=diff
==============================================================================
--- manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/system/ManifoldCF.java (original)
+++ manifoldcf/trunk/framework/agents/src/main/java/org/apache/manifoldcf/agents/system/ManifoldCF.java Tue Nov 26 11:45:53 2013
@@ -27,11 +27,13 @@ public class ManifoldCF extends org.apac
 {
   public static final String _rcsid = "@(#)$Id: ManifoldCF.java 988245 2010-08-23 18:39:35Z kwright $";
 
+  public static final String agentShutdownSignal = "_AGENTRUN_";
+
   // Agents initialized flag
   protected static boolean agentsInitialized = false;
   
   /** This is the place we keep track of the agents we've started. */
-  protected static HashMap runningHash = new HashMap();
+  protected static Map<String,IAgent> runningHash = new HashMap<String,IAgent>();
   /** This flag prevents startAgents() from starting anything once stopAgents() has been called. */
   protected static boolean stopAgentsRun = false;
   
@@ -68,9 +70,6 @@ public class ManifoldCF extends org.apac
       if (agentsInitialized)
         return;
 
-      // Create the shutdown hook for agents.  All activity will be keyed off of runningHash, so it is safe to do this under all conditions.
-      org.apache.manifoldcf.core.system.ManifoldCF.addShutdownHook(new AgentsShutdownHook());
-      
       // Initialize the local loggers
       Logging.initializeLoggers();
       Logging.setLogLevels(threadContext);
@@ -129,12 +128,88 @@ public class ManifoldCF extends org.apac
     mgr.deinstall();
   }
 
+  // There are a number of different ways of running the agents framework.
+  // (1) Repeatedly call checkAgents(), and when all done make sure to call stopAgents().
+  // (2) Call registerAgentsShutdownHook(), then repeatedly run checkAgents(),  Agent shutdown happens on JVM exit.
+  // (3) Call runAgents(), which will wait for someone else to call assertAgentsShutdownSignal().  Before exit, stopAgents() must be called.
+  // (4) Call registerAgentsShutdownHook(), then call runAgents(), which will wait for someone else to call assertAgentsShutdownSignal().  Shutdown happens on JVM exit.
+  
+  /** Assert shutdown signal.
+  */
+  public static void assertAgentsShutdownSignal(IThreadContext threadContext)
+    throws ManifoldCFException
+  {
+    ILockManager lockManager = LockManagerFactory.make(threadContext);
+    lockManager.setGlobalFlag(agentShutdownSignal);
+  }
+  
+  /** Clear shutdown signal.
+  */
+  public static void clearAgentsShutdownSignal(IThreadContext threadContext)
+    throws ManifoldCFException
+  {
+    ILockManager lockManager = LockManagerFactory.make(threadContext);
+    lockManager.clearGlobalFlag(agentShutdownSignal);
+  }
+
+
+  /** Register agents shutdown hook.
+  * Call this ONCE before calling startAgents or checkAgents the first time, if you want automatic cleanup of agents on JVM stop.
+  */
+  public static void registerAgentsShutdownHook(IThreadContext threadContext, String processID)
+    throws ManifoldCFException
+  {
+    // Create the shutdown hook for agents.  All activity will be keyed off of runningHash, so it is safe to do this under all conditions.
+    org.apache.manifoldcf.core.system.ManifoldCF.addShutdownHook(new AgentsShutdownHook(processID));
+  }
+  
+  /** Agent service name prefix (followed by agent class name) */
+  public static final String agentServicePrefix = "AGENT_";
+  
+  /** Run agents process.
+  * This method will not return until a shutdown signal is sent.
+  */
+  public static void runAgents(IThreadContext threadContext, String processID)
+    throws ManifoldCFException
+  {
+    ILockManager lockManager = LockManagerFactory.make(threadContext);
+
+    // Don't come up at all if shutdown signal in force
+    if (lockManager.checkGlobalFlag(agentShutdownSignal))
+      return;
+
+    while (true)
+    {
+      // Any shutdown signal yet?
+      if (lockManager.checkGlobalFlag(agentShutdownSignal))
+        break;
+          
+      // Start whatever agents need to be started
+      checkAgents(threadContext, processID);
+
+      try
+      {
+        ManifoldCF.sleep(5000);
+      }
+      catch (InterruptedException e)
+      {
+        break;
+      }
+    }
+  }
+
+  protected static String getAgentsClassServiceType(String agentClassName)
+  {
+    return agentServicePrefix + agentClassName;
+  }
+  
   /** Start all not-running agents.
   *@param threadContext is the thread context.
   */
-  public static void startAgents(IThreadContext threadContext)
+  public static void checkAgents(IThreadContext threadContext, String processID)
     throws ManifoldCFException
   {
+    ILockManager lockManager = LockManagerFactory.make(threadContext);
     // Get agent manager
     IAgentManager manager = AgentManagerFactory.make(threadContext);
     ManifoldCFException problem = null;
@@ -145,6 +220,8 @@ public class ManifoldCF extends org.apac
       if (stopAgentsRun)
         return;
       String[] classes = manager.getAllAgents();
+      Set<String> currentAgentClasses = new HashSet<String>();
+
       int i = 0;
       while (i < classes.length)
       {
@@ -152,50 +229,143 @@ public class ManifoldCF extends org.apac
         if (runningHash.get(className) == null)
         {
           // Start this agent
-          IAgent agent = AgentFactory.make(threadContext,className);
+          IAgent agent = AgentFactory.make(className);
+          agent.initialize(threadContext);
           try
           {
+            // Throw a lock, so that cleanup processes and startup processes don't collide.
+            String serviceType = getAgentsClassServiceType(className);
+            lockManager.registerServiceBeginServiceActivity(serviceType, processID, new CleanupAgent(threadContext, agent));
             // There is a potential race condition where the agent has been started but hasn't yet appeared in runningHash.
             // But having runningHash be the synchronizer for this activity will prevent any problems.
-            // There is ANOTHER potential race condition, however, that can occur if the process is shut down just before startAgents() is called.
-            // We avoid that problem by means of a flag, which prevents startAgents() from doing anything once stopAgents() has been called.
-            agent.startAgent();
+            agent.startAgent(threadContext, processID);
             // Successful!
             runningHash.put(className,agent);
           }
           catch (ManifoldCFException e)
           {
             problem = e;
+            agent.cleanUp(threadContext);
+          }
+        }
+        currentAgentClasses.add(className);
+      }
+
+      // Go through running hash and look for agents processes that have left
+      Iterator<String> runningAgentsIterator = runningHash.keySet().iterator();
+      while (runningAgentsIterator.hasNext())
+      {
+        String runningAgentClass = runningAgentsIterator.next();
+        if (!currentAgentClasses.contains(runningAgentClass))
+        {
+          // Shut down this one agent.
+          IAgent agent = runningHash.get(runningAgentClass);
+          try
+          {
+            // Stop it
+            agent.stopAgent(threadContext);
+            lockManager.endServiceActivity(getAgentsClassServiceType(runningAgentClass), processID);
+            runningAgentsIterator.remove();
+            agent.cleanUp(threadContext);
+          }
+          catch (ManifoldCFException e)
+          {
+            problem = e;
           }
         }
       }
     }
+
     if (problem != null)
       throw problem;
-    // Done.
+    
+    synchronized (runningHash)
+    {
+      // For every class we're supposed to be running, find registered but no-longer-active instances and clean
+      // up after them.
+      for (String agentsClass : runningHash.keySet())
+      {
+        IAgent agent = runningHash.get(agentsClass);
+        IServiceCleanup cleanup = new CleanupAgent(threadContext, agent);
+        String agentsClassServiceType = getAgentsClassServiceType(agentsClass);
+        while (!lockManager.cleanupInactiveService(agentsClassServiceType, cleanup))
+        {
+          // Loop until no more inactive services
+        }
+      }
+    }
+    
   }
 
   /** Stop all started agents.
   */
-  public static void stopAgents(IThreadContext threadContext)
+  public static void stopAgents(IThreadContext threadContext, String processID)
     throws ManifoldCFException
   {
+    ILockManager lockManager = LockManagerFactory.make(threadContext);
     synchronized (runningHash)
     {
-      HashMap iterHash = (HashMap)runningHash.clone();
-      Iterator iter = iterHash.keySet().iterator();
+      // This is supposedly safe; iterator remove is used
+      Iterator<String> iter = runningHash.keySet().iterator();
       while (iter.hasNext())
       {
-        String className = (String)iter.next();
-        IAgent agent = (IAgent)runningHash.get(className);
+        String className = iter.next();
+        IAgent agent = runningHash.get(className);
         // Stop it
-        agent.stopAgent();
-        runningHash.remove(className);
+        agent.stopAgent(threadContext);
+        lockManager.endServiceActivity(getAgentsClassServiceType(className), processID);
+        iter.remove();
+        agent.cleanUp(threadContext);
       }
     }
     // Done.
   }
   
+  protected static class CleanupAgent implements IServiceCleanup
+  {
+    protected final IAgent agent;
+    protected final IThreadContext threadContext;
+    
+    public CleanupAgent(IThreadContext threadContext, IAgent agent)
+    {
+      this.agent = agent;
+      this.threadContext = threadContext;
+    }
+    
+    /** Clean up after the specified service.  This method will block any startup of the specified
+    * service for as long as it runs.
+    *@param serviceName is the name of the service.
+    */
+    @Override
+    public void cleanUpService(String serviceName)
+      throws ManifoldCFException
+    {
+      agent.cleanUpAgentData(threadContext, serviceName);
+    }
+
+    /** Clean up after ALL services of the type on the cluster.
+    */
+    @Override
+    public void cleanUpAllServices()
+      throws ManifoldCFException
+    {
+      agent.cleanUpAgentData(threadContext);
+    }
+    
+    /** Perform cluster initialization - that is, whatever is needed presuming that the
+    * cluster has been down for an indeterminate period of time, but is otherwise in a clean
+    * state.
+    */
+    @Override
+    public void clusterInit()
+      throws ManifoldCFException
+    {
+      // MHL - we really want a separate clusterInit in agents
+      agent.cleanUpAgentData(threadContext);
+    }
+
+  }
+  
   /** Signal output connection needs redoing.
   * This is called when something external changed on an output connection, and
   * therefore all associated documents must be reindexed.
@@ -216,9 +386,11 @@ public class ManifoldCF extends org.apac
   /** Agents shutdown hook class */
   protected static class AgentsShutdownHook implements IShutdownHook
   {
-    
-    public AgentsShutdownHook()
+    protected final String processID;
+
+    public AgentsShutdownHook(String processID)
     {
+      this.processID = processID;
     }
     
     public void doCleanup()
@@ -230,7 +402,7 @@ public class ManifoldCF extends org.apac
         stopAgentsRun = true;
       }
       IThreadContext tc = ThreadContextFactory.make();
-      stopAgents(tc);
+      stopAgents(tc,processID);
     }
     
   }

Modified: manifoldcf/trunk/framework/combined-service/src/main/java/org/apache/manifoldcf/combinedservice/ServletListener.java
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/framework/combined-service/src/main/java/org/apache/manifoldcf/combinedservice/ServletListener.java?rev=1545624&r1=1545623&r2=1545624&view=diff
==============================================================================
--- manifoldcf/trunk/framework/combined-service/src/main/java/org/apache/manifoldcf/combinedservice/ServletListener.java (original)
+++ manifoldcf/trunk/framework/combined-service/src/main/java/org/apache/manifoldcf/combinedservice/ServletListener.java Tue Nov 26 11:45:53 2013
@@ -29,8 +29,7 @@ public class ServletListener implements 
 {
   public static final String _rcsid = "@(#)$Id$";
 
-  public static final String agentShutdownSignal = org.apache.manifoldcf.agents.AgentRun.agentShutdownSignal;
-  private Thread jobsThread = null;
+  protected static AgentsThread agentsThread = null;
 
   public void contextInitialized(ServletContextEvent sce)
   {
@@ -44,7 +43,8 @@ public class ServletListener implements 
       ManifoldCF.registerThisAgent(tc);
       ManifoldCF.reregisterAllConnectors(tc);
 
-      ManifoldCF.startAgents(tc);
+      agentsThread = new AgentsThread(ManifoldCF.getProcessID());
+      agentsThread.start();
     }
     catch (ManifoldCFException e)
     {
@@ -57,13 +57,74 @@ public class ServletListener implements 
     IThreadContext tc = ThreadContextFactory.make();
     try
     {
-      ManifoldCF.stopAgents(tc);
+      if (agentsThread != null)
+      {
+        ManifoldCF.assertAgentsShutdownSignal(tc);
+        agentsThread.finishUp();
+        agentsThread = null;
+        ManifoldCF.clearAgentsShutdownSignal(tc);
+      }
+    }
+    catch (InterruptedException e)
+    {
     }
     catch (ManifoldCFException e)
     {
-      throw new RuntimeException("Cannot shutdown servlet cleanly; "+e.getMessage(),e);
+      if (e.getErrorCode() != ManifoldCFException.INTERRUPTED)
+        throw new RuntimeException("Cannot shutdown servlet cleanly; "+e.getMessage(),e);
     }
     ManifoldCF.cleanUpEnvironment(tc);
   }
 
+  protected static class AgentsThread extends Thread
+  {
+    
+    protected final String processID;
+    
+    protected Throwable exception = null;
+
+    public AgentsThread(String processID)
+    {
+      setName("Agents");
+      this.processID = processID;
+    }
+    
+    public void run()
+    {
+      IThreadContext tc = ThreadContextFactory.make();
+      try
+      {
+        ManifoldCF.clearAgentsShutdownSignal(tc);
+        try
+        {
+          ManifoldCF.runAgents(tc, processID);
+        }
+        finally
+        {
+          ManifoldCF.stopAgents(tc, processID);
+        }
+      }
+      catch (Throwable e)
+      {
+        exception = e;
+      }
+    }
+    
+    public void finishUp()
+      throws ManifoldCFException, InterruptedException
+    {
+      join();
+      if (exception != null)
+      {
+        if (exception instanceof RuntimeException)
+          throw (RuntimeException)exception;
+        if (exception instanceof Error)
+          throw (Error)exception;
+        if (exception instanceof ManifoldCFException)
+          throw (ManifoldCFException)exception;
+        throw new RuntimeException("Unknown exception type thrown: "+exception.getClass().getName()+": "+exception.getMessage(),exception);
+      }
+    }
+  }
+  
 }

Modified: manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/interfaces/ILockManager.java
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/interfaces/ILockManager.java?rev=1545624&r1=1545623&r2=1545624&view=diff
==============================================================================
--- manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/interfaces/ILockManager.java (original)
+++ manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/interfaces/ILockManager.java Tue Nov 26 11:45:53 2013
@@ -26,6 +26,71 @@ public interface ILockManager
 {
   public static final String _rcsid = "@(#)$Id: ILockManager.java 988245 2010-08-23 18:39:35Z kwright $";
 
+  // Node synchronization
+  
+  // The node synchronization model involves keeping track of active agents entities, so that other entities
+  // can perform any necessary cleanup if one of the agents processes goes away unexpectedly.  There is a
+  // registration primitive (which can fail if the same guid is used as is already registered and active), a
+  // shutdown primitive (which makes a process id go inactive), and various inspection primitives.
+  
+  /** Register a service and begin service activity.
+  * This atomic operation creates a permanent registration entry for a service.
+  * If the permanent registration entry already exists, this method will not create it or
+  * treat it as an error.  This operation also enters the "active" zone for the service.  The "active" zone will remain in force until it is
+  * canceled, or until the process is interrupted.  Ideally, the corresponding endServiceActivity method will be
+  * called when the service shuts down.  Some ILockManager implementations require that this take place for
+  * proper management.
+  * If the transient registration already exists, it is treated as an error and an exception will be thrown.
+  * If registration will succeed, then this method may call an appropriate IServiceCleanup method to clean up either the
+  * current service, or all services on the cluster.
+  *@param serviceType is the type of service.
+  *@param serviceName is the name of the service to register.
+  *@param cleanup is called to clean up either the current service, or all services of this type, if no other active service exists
+  */
+  public void registerServiceBeginServiceActivity(String serviceType, String serviceName, IServiceCleanup cleanup)
+    throws ManifoldCFException;
+  
+  /** Count all active services of a given type.
+  *@param serviceType is the service type.
+  *@return the count.
+  */
+  public int countActiveServices(String serviceType)
+    throws ManifoldCFException;
+  
+  /** Clean up any inactive services found.
+  * Calling this method will invoke cleanup of one inactive service at a time.
+  * If there are no inactive services around, then false will be returned.
+  * Note that this method will block whatever service it finds from starting up
+  * for the time the cleanup is proceeding.  At the end of the cleanup, if
+  * successful, the service will be atomically unregistered.
+  *@param serviceType is the service type.
+  *@param cleanup is the object to call to clean up an inactive service.
+  *@return true if there were no cleanup operations necessary.
+  */
+  public boolean cleanupInactiveService(String serviceType, IServiceCleanup cleanup)
+    throws ManifoldCFException;
+
+  /** End service activity.
+  * This operation exits the "active" zone for the service.  This must take place using the same ILockManager
+  * object that was used to registerServiceBeginServiceActivity() - which implies that it is the same thread.
+  *@param serviceType is the type of service.
+  *@param serviceName is the name of the service to exit.
+  */
+  public void endServiceActivity(String serviceType, String serviceName)
+    throws ManifoldCFException;
+    
+  /** Check whether a service is active or not.
+  * This operation returns true if the specified service is considered active at the moment.  Once a service
+  * is not active anymore, it can only return to activity by calling beginServiceActivity() once more.
+  *@param serviceType is the type of service.
+  *@param serviceName is the name of the service to check on.
+  *@return true if the service is considered active.
+  */
+  public boolean checkServiceActive(String serviceType, String serviceName)
+    throws ManifoldCFException;
+
+  // Configuration
+  
   /** Get the current shared configuration.  This configuration is available in common among all nodes,
   * and thus must not be accessed through here for the purpose of finding configuration data that is specific to any one
   * specific node.
@@ -34,6 +99,8 @@ public interface ILockManager
   public ManifoldCFConfiguration getSharedConfiguration()
     throws ManifoldCFException;
 
+  // Flags
+  
   /** Raise a flag.  Use this method to assert a condition, or send a global signal.  The flag will be reset when the
   * entire system is restarted.
   *@param flagName is the name of the flag to set.
@@ -54,6 +121,8 @@ public interface ILockManager
   public boolean checkGlobalFlag(String flagName)
     throws ManifoldCFException;
 
+  // Shared data
+  
   /** Read data from a shared data resource.  Use this method to read any existing data, or get a null back if there is no such resource.
   * Note well that this is not necessarily an atomic operation, and it must thus be protected by a lock.
   *@param resourceName is the global name of the resource.
@@ -70,6 +139,8 @@ public interface ILockManager
   public void writeData(String resourceName, byte[] data)
     throws ManifoldCFException;
 
+  // Locks
+  
   /** Wait for a time before retrying a lock.  Use this method to wait
   * after a LockException has been thrown.  )If this is not done, the application
   * will wind up busy waiting.)
@@ -198,6 +269,8 @@ public interface ILockManager
   public void clearLocks()
     throws ManifoldCFException;
 
+  // Critical sections
+  
   /** Enter a named, read critical section (NOT a lock).  Critical sections never cross JVM boundaries.
   * Critical section names do not collide with lock names; they have a distinct namespace.
   *@param sectionKey is the name of the section to enter.  Only one thread can be in any given named

Modified: manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/lockmanager/BaseLockManager.java
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/lockmanager/BaseLockManager.java?rev=1545624&r1=1545623&r2=1545624&view=diff
==============================================================================
--- manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/lockmanager/BaseLockManager.java (original)
+++ manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/lockmanager/BaseLockManager.java Tue Nov 26 11:45:53 2013
@@ -56,12 +56,344 @@ public class BaseLockManager implements 
 
   /** Global resource data.  Used only when ManifoldCF is run entirely out of one process. */
   protected final static Map<String,byte[]> globalData = new HashMap<String,byte[]>();
-    
+  
   public BaseLockManager()
     throws ManifoldCFException
   {
   }
 
+  // Node synchronization
+  
+  // The node synchronization model involves keeping track of active agents entities, so that other entities
+  // can perform any necessary cleanup if one of the agents processes goes away unexpectedly.  There is a
+  // registration primitive (which can fail if the same guid is used as is already registered and active), a
+  // shutdown primitive (which makes a process id go inactive), and various inspection primitives.
+  
+  // This implementation of the node infrastructure uses other primitives implemented by the lock
+  // manager for the implementation.  Specifically, instead of synchronizers, we use a write lock
+  // to prevent conflicts, and we use flags to determine whether a service is active or not.  The
+  // tricky thing, though, is the global registry - which must be able to list its contents.  To acheive
+  // that, we use data with a counter scheme; if the data is not found, it's presumed we are at the
+  // end of the list.
+  //
+  // By building on other primitives in this way, the same implementation will suffice for many derived
+  // lockmanager implementations - although ZooKeeper will want a native form.
+
+  /** The global write lock to control sync */
+  protected final static String serviceLock = "_SERVICELOCK_";
+  /** A data name prefix, followed by the service type, and then followed by "_" and the instance number */
+  protected final static String serviceListPrefix = "_SERVICELIST_";
+  /** A flag prefix, followed by the service type, and then followed by "_" and the service name */
+  protected final static String servicePrefix = "_SERVICE_";
+  /** A flag prefix, followed by the service type, and then followed by "_" and the service name */
+  protected final static String activePrefix = "_ACTIVE_";
+
+  /** Register a service and begin service activity.
+  * This atomic operation creates a permanent registration entry for a service.
+  * If the permanent registration entry already exists, this method will not create it or
+  * treat it as an error.  This operation also enters the "active" zone for the service.  The "active" zone will remain in force until it is
+  * canceled, or until the process is interrupted.  Ideally, the corresponding endServiceActivity method will be
+  * called when the service shuts down.  Some ILockManager implementations require that this take place for
+  * proper management.
+  * If the transient registration already exists, it is treated as an error and an exception will be thrown.
+  * If registration will succeed, then this method may call an appropriate IServiceCleanup method to clean up either the
+  * current service, or all services on the cluster.
+  *@param serviceType is the type of service.
+  *@param serviceName is the name of the service to register.
+  *@param cleanup is called to clean up either the current service, or all services of this type, if no other active service exists
+  */
+  @Override
+  public void registerServiceBeginServiceActivity(String serviceType, String serviceName, IServiceCleanup cleanup)
+    throws ManifoldCFException
+  {
+    enterWriteLock(serviceLock);
+    try
+    {
+      // First, do an active check
+      String serviceActiveFlag = makeActiveServiceFlagName(serviceType, serviceName);
+      if (checkGlobalFlag(serviceActiveFlag))
+        throw new ManifoldCFException("Service '"+serviceName+"' of type '"+serviceType+"' is already active");
+      
+      // First, see where we stand.
+      // We need to find out whether (a) our service is already registered; (b) how many registered services there are;
+      // (c) whether there are other active services.  But no changes will be made at this time.
+      boolean foundService = false;
+      boolean foundActiveService = false;
+      String resourceName;
+      int i = 0;
+      while (true)
+      {
+        resourceName = buildServiceListEntry(serviceType, i);
+        String x = readServiceName(resourceName);
+        if (x == null)
+          break;
+        if (x.equals(serviceName))
+          foundService = true;
+        else if (checkGlobalFlag(makeActiveServiceFlagName(serviceType, x)))
+          foundActiveService = true;
+        i++;
+      }
+
+      // Call the appropriate cleanup.  This will depend on what's actually registered, and what's active.
+      // If there were no services registered at all when we started, then no cleanup is needed, just cluster init.
+      // If this fails, we must revert to having our service not be registered and not be active.
+      boolean unregisterAll = false;
+      if (cleanup != null)
+      {
+        if (i == 0)
+        {
+          // If we could count on locks never being cleaned up, clusterInit()
+          // would be sufficient here.  But then there's no way to recover from
+          // a lock clean.
+          cleanup.cleanUpAllServices();
+          cleanup.clusterInit();
+        }
+        else if (foundService && foundActiveService)
+          cleanup.cleanUpService(serviceName);
+        else if (!foundActiveService)
+        {
+          cleanup.cleanUpAllServices();
+          cleanup.clusterInit();
+          unregisterAll = true;
+        }
+      }
+      
+      if (unregisterAll)
+      {
+        // Unregister all (since we did a global cleanup)
+        int k = i;
+        while (k > 0)
+        {
+          k--;
+          resourceName = buildServiceListEntry(serviceType, k);
+          String x = readServiceName(resourceName);
+          clearGlobalFlag(makeRegisteredServiceFlagName(serviceType, x));
+          writeServiceName(resourceName, null);
+        }
+        foundService = false;
+      }
+
+      // Now, register (if needed)
+      if (!foundService)
+      {
+        writeServiceName(resourceName, serviceName);
+        try
+        {
+          setGlobalFlag(makeRegisteredServiceFlagName(serviceType, serviceName));
+        }
+        catch (Throwable e)
+        {
+          writeServiceName(resourceName, null);
+          if (e instanceof Error)
+            throw (Error)e;
+          if (e instanceof RuntimeException)
+            throw (RuntimeException)e;
+          if (e instanceof ManifoldCFException)
+            throw (ManifoldCFException)e;
+          else
+            throw new RuntimeException("Unknown exception of type: "+e.getClass().getName()+": "+e.getMessage(),e);
+        }
+      }
+
+      // Last, set the appropriate active flag
+      setGlobalFlag(serviceActiveFlag);
+    }
+    finally
+    {
+      leaveWriteLock(serviceLock);
+    }
+  }
+  
+  /** Count all active services of a given type.
+  *@param serviceType is the service type.
+  *@return the count.
+  */
+  @Override
+  public int countActiveServices(String serviceType)
+    throws ManifoldCFException
+  {
+    enterWriteLock(serviceLock);
+    try
+    {
+      int count = 0;
+      int i = 0;
+      while (true)
+      {
+        String resourceName = buildServiceListEntry(serviceType, i);
+        String x = readServiceName(resourceName);
+        if (x == null)
+          break;
+        if (checkGlobalFlag(makeActiveServiceFlagName(serviceType, x)))
+          count++;
+        i++;
+      }
+      return count;
+    }
+    finally
+    {
+      leaveWriteLock(serviceLock);
+    }
+  }
+
+  /** Clean up any inactive services found.
+  * Calling this method will invoke cleanup of one inactive service at a time.
+  * If there are no inactive services around, then false will be returned.
+  * Note that this method will block whatever service it finds from starting up
+  * for the time the cleanup is proceeding.  At the end of the cleanup, if
+  * successful, the service will be atomically unregistered.
+  *@param serviceType is the service type.
+  *@param cleanup is the object to call to clean up an inactive service.
+  *@return true if there were no cleanup operations necessary.
+  */
+  @Override
+  public boolean cleanupInactiveService(String serviceType, IServiceCleanup cleanup)
+    throws ManifoldCFException
+  {
+    enterWriteLock(serviceLock);
+    try
+    {
+      // We find ONE service that is registered but inactive, and clean up after that one.
+      // Presumably the caller will lather, rinse, and repeat.
+      String serviceName;
+      String resourceName;
+      int i = 0;
+      while (true)
+      {
+        resourceName = buildServiceListEntry(serviceType, i);
+        serviceName = readServiceName(resourceName);
+        if (serviceName == null)
+          return true;
+        if (!checkGlobalFlag(makeActiveServiceFlagName(serviceType, serviceName)))
+          break;
+        i++;
+      }
+      
+      // Found one, in serviceName, at position i
+      // Ideally, we should signal at this point that we're cleaning up after it, and then leave
+      // the exclusive lock, so that other activity can take place.  MHL
+      cleanup.cleanUpService(serviceName);
+      
+      // Clean up the registration
+      String serviceRegisteredFlag = makeRegisteredServiceFlagName(serviceType, serviceName);
+      
+      // Find the end of the list
+      int k = i + 1;
+      String lastResourceName = null;
+      String lastServiceName = null;
+      while (true)
+      {
+        String rName = buildServiceListEntry(serviceType, k);
+        String x = readServiceName(rName);
+        if (x == null)
+          break;
+        lastResourceName = rName;
+        lastServiceName = x;
+        k++;
+      }
+
+      // Rearrange the registration
+      clearGlobalFlag(serviceRegisteredFlag);
+      if (lastServiceName != null)
+        writeServiceName(resourceName, lastServiceName);
+      writeServiceName(lastResourceName, null);
+      return false;
+    }
+    finally
+    {
+      leaveWriteLock(serviceLock);
+    }
+  }
+
+  /** End service activity.
+  * This operation exits the "active" zone for the service.  This must take place using the same ILockManager
+  * object that was used to registerServiceBeginServiceActivity() - which implies that it is the same thread.
+  *@param serviceType is the type of service.
+  *@param serviceName is the name of the service to exit.
+  */
+  @Override
+  public void endServiceActivity(String serviceType, String serviceName)
+    throws ManifoldCFException
+  {
+    enterWriteLock(serviceLock);
+    try
+    {
+      String serviceActiveFlag = makeActiveServiceFlagName(serviceType, serviceName);
+      if (!checkGlobalFlag(serviceActiveFlag))
+        throw new ManifoldCFException("Service '"+serviceName+"' of type '"+serviceType+" is not active");
+      clearGlobalFlag(serviceActiveFlag);
+    }
+    finally
+    {
+      leaveWriteLock(serviceLock);
+    }
+  }
+    
+  /** Check whether a service is active or not.
+  * This operation returns true if the specified service is considered active at the moment.  Once a service
+  * is not active anymore, it can only return to activity by calling beginServiceActivity() once more.
+  *@param serviceType is the type of service.
+  *@param serviceName is the name of the service to check on.
+  *@return true if the service is considered active.
+  */
+  @Override
+  public boolean checkServiceActive(String serviceType, String serviceName)
+    throws ManifoldCFException
+  {
+    enterWriteLock(serviceLock);
+    try
+    {
+      return checkGlobalFlag(makeActiveServiceFlagName(serviceType, serviceName));
+    }
+    finally
+    {
+      leaveWriteLock(serviceLock);
+    }
+  }
+
+  protected static String makeActiveServiceFlagName(String serviceType, String serviceName)
+  {
+    return activePrefix + serviceType + "_" + serviceName;
+  }
+  
+  protected static String makeRegisteredServiceFlagName(String serviceType, String serviceName)
+  {
+    return servicePrefix + serviceType + "_" + serviceName;
+  }
+
+  protected String readServiceName(String resourceName)
+    throws ManifoldCFException
+  {
+    byte[] bytes = readData(resourceName);
+    if (bytes == null)
+      return null;
+    try
+    {
+      return new String(bytes, "utf-8");
+    }
+    catch (UnsupportedEncodingException e)
+    {
+      throw new RuntimeException("Unsupported encoding: "+e.getMessage(),e);
+    }
+  }
+  
+  protected void writeServiceName(String resourceName, String serviceName)
+    throws ManifoldCFException
+  {
+    try
+    {
+      writeData(resourceName, (serviceName==null)?null:serviceName.getBytes("utf-8"));
+    }
+    catch (UnsupportedEncodingException e)
+    {
+      throw new RuntimeException("Unsupported encoding: "+e.getMessage(),e);
+    }
+  }
+  
+  protected static String buildServiceListEntry(String serviceType, int i)
+  {
+    return serviceListPrefix + serviceType + "_" + i;
+  }
+
   /** Get the current shared configuration.  This configuration is available in common among all nodes,
   * and thus must not be accessed through here for the purpose of finding configuration data that is specific to any one
   * specific node.

Modified: manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/lockmanager/FileLockManager.java
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/lockmanager/FileLockManager.java?rev=1545624&r1=1545623&r2=1545624&view=diff
==============================================================================
--- manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/lockmanager/FileLockManager.java (original)
+++ manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/lockmanager/FileLockManager.java Tue Nov 26 11:45:53 2013
@@ -78,7 +78,7 @@ public class FileLockManager extends Bas
   {
     return "flag-"+flagName;
   }
-  
+    
   /** Raise a flag.  Use this method to assert a condition, or send a global signal.  The flag will be reset when the
   * entire system is restarted.
   *@param flagName is the name of the flag to set.

Modified: manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/lockmanager/LockManager.java
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/lockmanager/LockManager.java?rev=1545624&r1=1545623&r2=1545624&view=diff
==============================================================================
--- manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/lockmanager/LockManager.java (original)
+++ manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/lockmanager/LockManager.java Tue Nov 26 11:45:53 2013
@@ -44,6 +44,82 @@ public class LockManager implements ILoc
       lockManager = new BaseLockManager();
   }
 
+  /** Register a service and begin service activity.
+  * This atomic operation creates a permanent registration entry for a service.
+  * If the permanent registration entry already exists, this method will not create it or
+  * treat it as an error.  This operation also enters the "active" zone for the service.  The "active" zone will remain in force until it is
+  * canceled, or until the process is interrupted.  Ideally, the corresponding endServiceActivity method will be
+  * called when the service shuts down.  Some ILockManager implementations require that this take place for
+  * proper management.
+  * If the transient registration already exists, it is treated as an error and an exception will be thrown.
+  * If registration will succeed, then this method may call an appropriate IServiceCleanup method to clean up either the
+  * current service, or all services on the cluster.
+  *@param serviceType is the type of service.
+  *@param serviceName is the name of the service to register.
+  *@param cleanup is called to clean up either the current service, or all services of this type, if no other active service exists
+  */
+  @Override
+  public void registerServiceBeginServiceActivity(String serviceType, String serviceName, IServiceCleanup cleanup)
+    throws ManifoldCFException
+  {
+    lockManager.registerServiceBeginServiceActivity(serviceType, serviceName, cleanup);
+  }
+  
+  /** Clean up any inactive services found.
+  * Calling this method will invoke cleanup of one inactive service at a time.
+  * If there are no inactive services around, then false will be returned.
+  * Note that this method will block whatever service it finds from starting up
+  * for the time the cleanup is proceeding.  At the end of the cleanup, if
+  * successful, the service will be atomically unregistered.
+  *@param serviceType is the service type.
+  *@param cleanup is the object to call to clean up an inactive service.
+  *@return true if there were no cleanup operations necessary.
+  */
+  @Override
+  public boolean cleanupInactiveService(String serviceType, IServiceCleanup cleanup)
+    throws ManifoldCFException
+  {
+    return lockManager.cleanupInactiveService(serviceType, cleanup);
+  }
+  
+  /** Count all active services of a given type.
+  *@param serviceType is the service type.
+  *@return the count.
+  */
+  @Override
+  public int countActiveServices(String serviceType)
+    throws ManifoldCFException
+  {
+    return lockManager.countActiveServices(serviceType);
+  }
+
+  /** End service activity.
+  * This operation exits the "active" zone for the service.  This must take place using the same ILockManager
+  * object that was used to registerServiceBeginServiceActivity() - which implies that it is the same thread.
+  *@param serviceType is the type of service.
+  *@param serviceName is the name of the service to exit.
+  */
+  @Override
+  public void endServiceActivity(String serviceType, String serviceName)
+    throws ManifoldCFException
+  {
+    lockManager.endServiceActivity(serviceType, serviceName);
+  }
+    
+  /** Check whether a service is active or not.
+  * This operation returns true if the specified service is considered active at the moment.  Once a service
+  * is not active anymore, it can only return to activity by calling beginServiceActivity() once more.
+  *@param serviceType is the type of service.
+  *@param serviceName is the name of the service to check on.
+  *@return true if the service is considered active.
+  */
+  @Override
+  public boolean checkServiceActive(String serviceType, String serviceName)
+    throws ManifoldCFException
+  {
+    return lockManager.checkServiceActive(serviceType, serviceName);
+  }
+
   /** Get the current shared configuration.  This configuration is available in common among all nodes,
   * and thus must not be accessed through here for the purpose of finding configuration data that is specific to any one
   * specific node.

Modified: manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/lockmanager/ZooKeeperConnection.java
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/lockmanager/ZooKeeperConnection.java?rev=1545624&r1=1545623&r2=1545624&view=diff
==============================================================================
--- manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/lockmanager/ZooKeeperConnection.java (original)
+++ manifoldcf/trunk/framework/core/src/main/java/org/apache/manifoldcf/core/lockmanager/ZooKeeperConnection.java Tue Nov 26 11:45:53 2013
@@ -42,6 +42,8 @@ public class ZooKeeperConnection
   private static final String NONEXWRITE_PREFIX = "nonexwrite-";
   private static final String WRITE_PREFIX = "write-";
 
+  private static final String CHILD_PREFIX = "child-";
+  
   // Our zookeeper client
   protected ZooKeeper zookeeper = null;
   protected ZooKeeperWatcher zookeeperWatcher = null;
@@ -68,6 +70,133 @@ public class ZooKeeperConnection
     }
   }
 
+  /** Create a transient node.
+  */
+  public void createNode(String nodePath)
+    throws ManifoldCFException, InterruptedException
+  {
+    try
+    {
+      zookeeper.create(nodePath, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
+    }
+    catch (KeeperException e)
+    {
+      throw new ManifoldCFException(e.getMessage(),e);
+    }
+  }
+  
+  /** Check whether a node exists.
+  *@param nodePath is the path of the node.
+  *@return true if exists.
+  */
+  public boolean checkNodeExists(String nodePath)
+    throws ManifoldCFException, InterruptedException
+  {
+    try
+    {
+      return zookeeper.exists(nodePath,false) != null;
+    }
+    catch (KeeperException e)
+    {
+      throw new ManifoldCFException(e.getMessage(),e);
+    }
+  }
+  
+  /** Delete a node.
+  */
+  public void deleteNode(String nodePath)
+    throws ManifoldCFException, InterruptedException
+  {
+    try
+    {
+      zookeeper.delete(nodePath,-1);
+    }
+    catch (KeeperException e)
+    {
+      throw new ManifoldCFException(e.getMessage(),e);
+    }
+  }
+  
+  /** Get the relative paths of all node's children.  If the node does not exist,
+  * return an empty list.
+  */
+  public List<String> getChildren(String nodePath)
+    throws ManifoldCFException, InterruptedException
+  {
+    try
+    {
+      //System.out.println("Children of '"+nodePath+"':");
+      List<String> children = zookeeper.getChildren(nodePath,false);
+      List<String> rval = new ArrayList<String>();
+      for (String child : children)
+      {
+        //System.out.println(" '"+child+"'");
+        if (child.startsWith(CHILD_PREFIX))
+          rval.add(child.substring(CHILD_PREFIX.length()));
+      }
+      return rval;
+    }
+    catch (KeeperException.NoNodeException e)
+    {
+      return new ArrayList<String>();
+    }
+    catch (KeeperException e)
+    {
+      throw new ManifoldCFException(e.getMessage(),e);
+    }
+  }
+  
+  /** Create a persistent child of a node.
+  */
+  public void createChild(String nodePath, String childName)
+    throws ManifoldCFException, InterruptedException
+  {
+    try
+    {
+      //System.out.println("Creating child '"+childName+"' of nodepath '"+nodePath+"'");
+      while (true)
+      {
+        try
+        {
+          zookeeper.create(nodePath + "/" + CHILD_PREFIX + childName, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+          break;
+        }
+        catch (KeeperException.NoNodeException e)
+        {
+          try
+          {
+            zookeeper.create(nodePath, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+          }
+          catch (KeeperException.NodeExistsException e2)
+          {
+          }
+        }
+      }
+      System.out.println("...done");
+    }
+    catch (KeeperException e)
+    {
+      throw new ManifoldCFException(e.getMessage(),e);
+    }
+  }
+  
+  /** Delete the child of a node.
+  */
+  public void deleteChild(String nodePath, String childName)
+    throws ManifoldCFException, InterruptedException
+  {
+    try
+    {
+      //System.out.println("Deleting child '"+childName+"' of nodePath '"+nodePath+"'");
+      zookeeper.delete(nodePath + "/" + CHILD_PREFIX + childName, -1);
+      //System.out.println("...done");
+    }
+    catch (KeeperException e)
+    {
+      throw new ManifoldCFException(e.getMessage(),e);
+    }
+  }
+  
   /** Obtain a write lock, with no wait.
   *@param lockPath is the lock node path.
   *@return true if the lock was obtained, false otherwise.



Mime
View raw message