You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@manifoldcf.apache.org by kw...@apache.org on 2010/02/19 17:21:29 UTC

svn commit: r911857 - in /incubator/lcf/trunk/modules/framework/pull-agent/org/apache/lcf/crawler/jobs: JobManager.java Jobs.java

Author: kwright
Date: Fri Feb 19 16:21:29 2010
New Revision: 911857

URL: http://svn.apache.org/viewvc?rev=911857&view=rev
Log:
Add job states that represent situations where the output connector for the job is not currently installed.  This should prevent the job from running or being deleted.  (This code was missing as a result of an oversight.  Now it has been implemented.)

Modified:
    incubator/lcf/trunk/modules/framework/pull-agent/org/apache/lcf/crawler/jobs/JobManager.java
    incubator/lcf/trunk/modules/framework/pull-agent/org/apache/lcf/crawler/jobs/Jobs.java

Modified: incubator/lcf/trunk/modules/framework/pull-agent/org/apache/lcf/crawler/jobs/JobManager.java
URL: http://svn.apache.org/viewvc/incubator/lcf/trunk/modules/framework/pull-agent/org/apache/lcf/crawler/jobs/JobManager.java?rev=911857&r1=911856&r2=911857&view=diff
==============================================================================
--- incubator/lcf/trunk/modules/framework/pull-agent/org/apache/lcf/crawler/jobs/JobManager.java (original)
+++ incubator/lcf/trunk/modules/framework/pull-agent/org/apache/lcf/crawler/jobs/JobManager.java Fri Feb 19 16:21:29 2010
@@ -355,7 +355,50 @@
   public void noteOutputConnectorDeregistration(String[] connectionNames)
     throws LCFException
   {
-    // MHL
+    // For each connection, find the corresponding list of jobs.  From these jobs, we want the job id and the status.
+    StringBuffer sb = new StringBuffer();
+    ArrayList list = new ArrayList();
+    int maxCount = database.getMaxInClause();
+    int currentCount = 0;
+    int i = 0;
+    while (i < connectionNames.length)
+    {
+      if (currentCount == maxCount)
+      {
+        noteOutputConnectionDeregistration(sb.toString(),list);
+        sb.setLength(0);
+        list.clear();
+        currentCount = 0;
+      }
+
+      if (currentCount > 0)
+        sb.append(",");
+      sb.append("?");
+      list.add(connectionNames[i++]);
+      currentCount++;
+    }
+    if (currentCount > 0)
+      noteOutputConnectionDeregistration(sb.toString(),list);
+  }
+
+  /** Note deregistration for a batch of output connection names.
+  */
+  protected void noteOutputConnectionDeregistration(String query, ArrayList list)
+    throws LCFException
+  {
+    //System.out.println("Query is "+query);
+    // Query for the matching jobs, and then for each job potentially adjust the state
+    IResultSet set = database.performQuery("SELECT "+jobs.idField+","+jobs.statusField+" FROM "+
+      jobs.getTableName()+" WHERE "+jobs.outputNameField+" IN ("+query+") FOR UPDATE",
+      list,null,null);
+    int i = 0;
+    while (i < set.getRowCount())
+    {
+      IResultRow row = set.getRow(i++);
+      Long jobID = (Long)row.getValue(jobs.idField);
+      int statusValue = jobs.stringToStatus((String)row.getValue(jobs.statusField));
+      jobs.noteOutputConnectorDeregistration(jobID,statusValue);
+    }
   }
 
   /** Note the registration of an output connector used by the specified connections.
@@ -366,7 +409,49 @@
   public void noteOutputConnectorRegistration(String[] connectionNames)
     throws LCFException
   {
-    // MHL
+    // For each connection, find the corresponding list of jobs.  From these jobs, we want the job id and the status.
+    StringBuffer sb = new StringBuffer();
+    ArrayList list = new ArrayList();
+    int maxCount = database.getMaxInClause();
+    int currentCount = 0;
+    int i = 0;
+    while (i < connectionNames.length)
+    {
+      if (currentCount == maxCount)
+      {
+        noteOutputConnectionRegistration(sb.toString(),list);
+        sb.setLength(0);
+        list.clear();
+        currentCount = 0;
+      }
+
+      if (currentCount > 0)
+        sb.append(",");
+      sb.append("?");
+      list.add(connectionNames[i++]);
+      currentCount++;
+    }
+    if (currentCount > 0)
+      noteOutputConnectionRegistration(sb.toString(),list);
+  }
+
+  /** Note registration for a batch of output connection names.
+  */
+  protected void noteOutputConnectionRegistration(String query, ArrayList list)
+    throws LCFException
+  {
+    // Query for the matching jobs, and then for each job potentially adjust the state
+    IResultSet set = database.performQuery("SELECT "+jobs.idField+","+jobs.statusField+" FROM "+
+      jobs.getTableName()+" WHERE "+jobs.outputNameField+" IN ("+query+") FOR UPDATE",
+      list,null,null);
+    int i = 0;
+    while (i < set.getRowCount())
+    {
+      IResultRow row = set.getRow(i++);
+      Long jobID = (Long)row.getValue(jobs.idField);
+      int statusValue = jobs.stringToStatus((String)row.getValue(jobs.statusField));
+      jobs.noteOutputConnectorRegistration(jobID,statusValue);
+    }
   }
 
   /** Load a sorted list of job descriptions.
@@ -408,18 +493,24 @@
       // If the job is running, throw an error
       ArrayList list = new ArrayList();
       list.add(id);
-      IResultSet set = database.performQuery("SELECT "+jobs.statusField+" FROM "+
+      IResultSet set = database.performQuery("SELECT "+jobs.statusField+","+jobs.outputNameField+" FROM "+
         jobs.getTableName()+" WHERE "+jobs.idField+"=? FOR UPDATE",list,null,null);
       if (set.getRowCount() == 0)
         throw new LCFException("Attempting to delete a job that doesn't exist: "+id);
       IResultRow row = set.getRow(0);
+      String outputName = (String)row.getValue(jobs.outputNameField);
       int status = jobs.stringToStatus(row.getValue(jobs.statusField).toString());
       if (status == jobs.STATUS_ACTIVE || status == jobs.STATUS_ACTIVESEEDING ||
-        status == jobs.STATUS_ACTIVE_UNINSTALLED || status == jobs.STATUS_ACTIVESEEDING_UNINSTALLED)
+        status == jobs.STATUS_ACTIVE_UNINSTALLED || status == jobs.STATUS_ACTIVESEEDING_UNINSTALLED ||
+        status == jobs.STATUS_ACTIVE_NOOUTPUT || status == jobs.STATUS_ACTIVESEEDING_NOOUTPUT ||
+        status == jobs.STATUS_ACTIVE_NEITHER || status == jobs.STATUS_ACTIVESEEDING_NEITHER)
       throw new LCFException("Job "+id+" is active; you must shut it down before deleting it");
       if (status != jobs.STATUS_INACTIVE)
         throw new LCFException("Job "+id+" is busy; you must wait and/or shut it down before deleting it");
-      jobs.writeStatus(id,jobs.STATUS_READYFORDELETE);
+      if (outputMgr.checkConnectorExists(outputName))
+        jobs.writeStatus(id,jobs.STATUS_READYFORDELETE);
+      else
+        jobs.writeStatus(id,jobs.STATUS_READYFORDELETE_NOOUTPUT);
       if (Logging.jobs.isDebugEnabled())
         Logging.jobs.debug("Job "+id+" marked for deletion");
     }
@@ -3751,6 +3842,7 @@
         jobs.lastTimeField+","+
         jobs.statusField+","+
         jobs.startMethodField+","+
+        jobs.outputNameField+","+
         jobs.connectionNameField+
         " FROM "+jobs.getTableName()+" WHERE "+
         jobs.statusField+" IN ("+
@@ -3782,6 +3874,7 @@
 
         Long jobID = (Long)row.getValue(jobs.idField);
         int startMethod = jobs.stringToStartMethod((String)row.getValue(jobs.startMethodField));
+        String outputName = (String)row.getValue(jobs.outputNameField);
         String connectionName = (String)row.getValue(jobs.connectionNameField);
         ScheduleRecord[] thisSchedule = srSet[i++];
 
@@ -3886,9 +3979,19 @@
         case Jobs.STATUS_ACTIVEWAIT:
           unwaitList.add(jobID);
           if (connectionMgr.checkConnectorExists(connectionName))
-            newJobState = jobs.STATUS_ACTIVE;
+          {
+            if (outputMgr.checkConnectorExists(outputName))
+              newJobState = jobs.STATUS_ACTIVE;
+            else
+              newJobState = jobs.STATUS_ACTIVE_NOOUTPUT;
+          }
           else
-            newJobState = jobs.STATUS_ACTIVE_UNINSTALLED;
+          {
+            if (outputMgr.checkConnectorExists(outputName))
+              newJobState = jobs.STATUS_ACTIVE_UNINSTALLED;
+            else
+              newJobState = jobs.STATUS_ACTIVE_NEITHER;
+          }
           jobs.unwaitJob(jobID,newJobState,windowEnd);
           jobQueue.clearFailTimes(jobID);
           if (Logging.jobs.isDebugEnabled())
@@ -3899,9 +4002,19 @@
         case Jobs.STATUS_ACTIVEWAITSEEDING:
           unwaitList.add(jobID);
           if (connectionMgr.checkConnectorExists(connectionName))
-            newJobState = jobs.STATUS_ACTIVESEEDING;
+          {
+            if (outputMgr.checkConnectorExists(outputName))
+              newJobState = jobs.STATUS_ACTIVESEEDING;
+            else
+              newJobState = jobs.STATUS_ACTIVESEEDING_NOOUTPUT;
+          }
           else
-            newJobState = jobs.STATUS_ACTIVESEEDING_UNINSTALLED;
+          {
+            if (outputMgr.checkConnectorExists(outputName))
+              newJobState = jobs.STATUS_ACTIVESEEDING_UNINSTALLED;
+            else
+              newJobState = jobs.STATUS_ACTIVESEEDING_NEITHER;
+          }
           jobs.unwaitJob(jobID,newJobState,windowEnd);
           jobQueue.clearFailTimes(jobID);
           if (Logging.jobs.isDebugEnabled())
@@ -3971,6 +4084,10 @@
         database.quoteSQLString(jobs.statusToString(jobs.STATUS_ACTIVESEEDING))+","+
         database.quoteSQLString(jobs.statusToString(jobs.STATUS_ACTIVE_UNINSTALLED))+","+
         database.quoteSQLString(jobs.statusToString(jobs.STATUS_ACTIVESEEDING_UNINSTALLED))+","+
+        database.quoteSQLString(jobs.statusToString(jobs.STATUS_ACTIVE_NOOUTPUT))+","+
+        database.quoteSQLString(jobs.statusToString(jobs.STATUS_ACTIVESEEDING_NOOUTPUT))+","+
+        database.quoteSQLString(jobs.statusToString(jobs.STATUS_ACTIVE_NEITHER))+","+
+        database.quoteSQLString(jobs.statusToString(jobs.STATUS_ACTIVESEEDING_NEITHER))+","+
         database.quoteSQLString(jobs.statusToString(jobs.STATUS_PAUSED))+","+
         database.quoteSQLString(jobs.statusToString(jobs.STATUS_PAUSEDSEEDING))+") AND "+
         jobs.windowEndField+"<"+(new Long(currentTime)).toString()+" FOR UPDATE",
@@ -3991,6 +4108,8 @@
         {
         case Jobs.STATUS_ACTIVE:
         case Jobs.STATUS_ACTIVE_UNINSTALLED:
+        case Jobs.STATUS_ACTIVE_NOOUTPUT:
+        case Jobs.STATUS_ACTIVE_NEITHER:
           jobs.waitJob(jobID,Jobs.STATUS_ACTIVEWAIT);
           if (Logging.jobs.isDebugEnabled())
           {
@@ -3999,6 +4118,8 @@
           break;
         case Jobs.STATUS_ACTIVESEEDING:
         case Jobs.STATUS_ACTIVESEEDING_UNINSTALLED:
+        case Jobs.STATUS_ACTIVESEEDING_NOOUTPUT:
+        case Jobs.STATUS_ACTIVESEEDING_NEITHER:
           jobs.waitJob(jobID,Jobs.STATUS_ACTIVEWAITSEEDING);
           if (Logging.jobs.isDebugEnabled())
           {
@@ -4761,6 +4882,20 @@
           // Set the state of the job back to "Active"
           jobs.writeStatus(jobID,jobs.STATUS_ACTIVE_UNINSTALLED);
           break;
+        case Jobs.STATUS_ACTIVESEEDING_NOOUTPUT:
+          if (Logging.jobs.isDebugEnabled())
+            Logging.jobs.debug("Setting job "+jobID+" back to 'Active_NoOutput' state");
+
+          // Set the state of the job back to "Active"
+          jobs.writeStatus(jobID,jobs.STATUS_ACTIVE_NOOUTPUT);
+          break;
+        case Jobs.STATUS_ACTIVESEEDING_NEITHER:
+          if (Logging.jobs.isDebugEnabled())
+            Logging.jobs.debug("Setting job "+jobID+" back to 'Active_Neither' state");
+
+          // Set the state of the job back to "Active"
+          jobs.writeStatus(jobID,jobs.STATUS_ACTIVE_NEITHER);
+          break;
         case Jobs.STATUS_ACTIVESEEDING:
           if (Logging.jobs.isDebugEnabled())
             Logging.jobs.debug("Setting job "+jobID+" back to 'Active' state");
@@ -4809,6 +4944,8 @@
         case Jobs.STATUS_ABORTINGFORRESTART:
         case Jobs.STATUS_ACTIVE:
         case Jobs.STATUS_ACTIVE_UNINSTALLED:
+        case Jobs.STATUS_ACTIVE_NOOUTPUT:
+        case Jobs.STATUS_ACTIVE_NEITHER:
         case Jobs.STATUS_PAUSED:
         case Jobs.STATUS_ACTIVEWAIT:
         case Jobs.STATUS_PAUSEDWAIT:
@@ -4876,8 +5013,9 @@
 
         // Do the first query, getting the candidate jobs to be considered
         IResultSet set = database.performQuery("SELECT "+jobs.idField+" FROM "+
-          jobs.getTableName()+" WHERE "+jobs.statusField+"="+
-          database.quoteSQLString(jobs.statusToString(jobs.STATUS_READYFORDELETE))+" FOR UPDATE",null,null,null);
+          jobs.getTableName()+" WHERE "+jobs.statusField+" IN("+
+          database.quoteSQLString(jobs.statusToString(jobs.STATUS_READYFORDELETE))+","+
+          database.quoteSQLString(jobs.statusToString(jobs.STATUS_READYFORDELETE_NOOUTPUT))+") FOR UPDATE",null,null,null);
 
         // Now, loop through this list.  For each one, verify that it's okay to delete it
         int i = 0;
@@ -5010,7 +5148,9 @@
           jobs.getTableName()+" WHERE "+jobs.statusField+" IN ("+
           database.quoteSQLString(jobs.statusToString(jobs.STATUS_ACTIVE))+","+
           database.quoteSQLString(jobs.statusToString(jobs.STATUS_ACTIVEWAIT))+","+
-          database.quoteSQLString(jobs.statusToString(jobs.STATUS_ACTIVE_UNINSTALLED))+") FOR UPDATE",null,null,null);
+          database.quoteSQLString(jobs.statusToString(jobs.STATUS_ACTIVE_UNINSTALLED))+","+
+          database.quoteSQLString(jobs.statusToString(jobs.STATUS_ACTIVE_NOOUTPUT))+","+
+          database.quoteSQLString(jobs.statusToString(jobs.STATUS_ACTIVE_NEITHER))+") FOR UPDATE",null,null,null);
 
         int i = 0;
         while (i < set.getRowCount())
@@ -5307,6 +5447,10 @@
       database.quoteSQLString(Jobs.statusToString(Jobs.STATUS_ACTIVESEEDING))+","+
       database.quoteSQLString(Jobs.statusToString(Jobs.STATUS_ACTIVE_UNINSTALLED))+","+
       database.quoteSQLString(Jobs.statusToString(Jobs.STATUS_ACTIVESEEDING_UNINSTALLED))+","+
+      database.quoteSQLString(Jobs.statusToString(Jobs.STATUS_ACTIVE_NOOUTPUT))+","+
+      database.quoteSQLString(Jobs.statusToString(Jobs.STATUS_ACTIVESEEDING_NOOUTPUT))+","+
+      database.quoteSQLString(Jobs.statusToString(Jobs.STATUS_ACTIVE_NEITHER))+","+
+      database.quoteSQLString(Jobs.statusToString(Jobs.STATUS_ACTIVESEEDING_NEITHER))+","+
       database.quoteSQLString(Jobs.statusToString(Jobs.STATUS_PAUSED))+","+
       database.quoteSQLString(Jobs.statusToString(Jobs.STATUS_PAUSEDSEEDING))+","+
       database.quoteSQLString(Jobs.statusToString(Jobs.STATUS_ACTIVEWAIT))+","+
@@ -5442,6 +5586,10 @@
         break;
       case Jobs.STATUS_ACTIVE_UNINSTALLED:
       case Jobs.STATUS_ACTIVESEEDING_UNINSTALLED:
+      case Jobs.STATUS_ACTIVE_NOOUTPUT:
+      case Jobs.STATUS_ACTIVESEEDING_NOOUTPUT:
+      case Jobs.STATUS_ACTIVE_NEITHER:
+      case Jobs.STATUS_ACTIVESEEDING_NEITHER:
         rstatus = JobStatus.JOBSTATUS_RUNNING_UNINSTALLED;
         break;
       case Jobs.STATUS_ACTIVE:
@@ -5478,6 +5626,7 @@
         rstatus = JobStatus.JOBSTATUS_STARTING;
         break;
       case Jobs.STATUS_READYFORDELETE:
+      case Jobs.STATUS_READYFORDELETE_NOOUTPUT:
         rstatus = JobStatus.JOBSTATUS_DESTRUCTING;
         break;
       default:

Modified: incubator/lcf/trunk/modules/framework/pull-agent/org/apache/lcf/crawler/jobs/Jobs.java
URL: http://svn.apache.org/viewvc/incubator/lcf/trunk/modules/framework/pull-agent/org/apache/lcf/crawler/jobs/Jobs.java?rev=911857&r1=911856&r2=911857&view=diff
==============================================================================
--- incubator/lcf/trunk/modules/framework/pull-agent/org/apache/lcf/crawler/jobs/Jobs.java (original)
+++ incubator/lcf/trunk/modules/framework/pull-agent/org/apache/lcf/crawler/jobs/Jobs.java Fri Feb 19 16:21:29 2010
@@ -60,8 +60,13 @@
   // But, since there is no indication in the jobs table of an uninstalled connector for such jobs, the code which starts
   // jobs up (or otherwise would enter any state that has a corresponding special state) must check to see if the underlying
   // connector exists before deciding what state to put the job into.
-  public static final int STATUS_ACTIVE_UNINSTALLED = 21; // Special because we don't want these jobs to actually queue anything
-  public static final int STATUS_ACTIVESEEDING_UNINSTALLED = 22; // Don't want job to queue documents
+  public static final int STATUS_ACTIVE_UNINSTALLED = 21;               // Active, but repository connector not installed
+  public static final int STATUS_ACTIVESEEDING_UNINSTALLED = 22;   // Active and seeding, but repository connector not installed
+  public static final int STATUS_ACTIVE_NOOUTPUT = 23;                  // Active, but output connector not installed
+  public static final int STATUS_ACTIVESEEDING_NOOUTPUT = 24;       // Active and seeding, but output connector not installed
+  public static final int STATUS_ACTIVE_NEITHER = 25;                     // Active, but neither repository connector nor output connector installed
+  public static final int STATUS_ACTIVESEEDING_NEITHER = 26;          // Active and seeding, but neither repository connector nor output connector installed
+  public static final int STATUS_READYFORDELETE_NOOUTPUT = 27;    // Job is being deleted but there's no output connector installed
 
   // Type field values
   public static final int TYPE_CONTINUOUS = IJobDescription.TYPE_CONTINUOUS;
@@ -150,7 +155,12 @@
     // These are the uninstalled states.  The values, I'm afraid, are pretty random.
     statusMap.put("R",new Integer(STATUS_ACTIVE_UNINSTALLED));
     statusMap.put("r",new Integer(STATUS_ACTIVESEEDING_UNINSTALLED));
-
+    statusMap.put("O",new Integer(STATUS_ACTIVE_NOOUTPUT));
+    statusMap.put("o",new Integer(STATUS_ACTIVESEEDING_NOOUTPUT));
+    statusMap.put("U",new Integer(STATUS_ACTIVE_NEITHER));
+    statusMap.put("u",new Integer(STATUS_ACTIVESEEDING_NEITHER));
+    statusMap.put("D",new Integer(STATUS_READYFORDELETE_NOOUTPUT));
+    
     typeMap = new HashMap();
     typeMap.put("C",new Integer(TYPE_CONTINUOUS));
     typeMap.put("S",new Integer(TYPE_SPECIFIED));
@@ -404,6 +414,7 @@
 
       IResultSet set = performQuery("SELECT "+idField+","+descriptionField+" FROM "+
         getTableName()+" WHERE "+statusField+"!="+quoteSQLString(statusToString(STATUS_READYFORDELETE))+
+        " AND "+statusField+"!="+quoteSQLString(statusToString(STATUS_READYFORDELETE_NOOUTPUT))+
         " ORDER BY "+descriptionField+" ASC",null,cacheKeys,null);
       // Convert to an array of id's, and then load them
       Long[] ids = new Long[set.getRowCount()];
@@ -733,6 +744,14 @@
       list.add(statusToString(STATUS_ACTIVESEEDING_UNINSTALLED));
       map.put(statusField,statusToString(STATUS_ACTIVE_UNINSTALLED));
       performUpdate(map,"WHERE "+statusField+"=?",list,invKey);
+      list.clear();
+      list.add(statusToString(STATUS_ACTIVESEEDING_NOOUTPUT));
+      map.put(statusField,statusToString(STATUS_ACTIVE_NOOUTPUT));
+      performUpdate(map,"WHERE "+statusField+"=?",list,invKey);
+      list.clear();
+      list.add(statusToString(STATUS_ACTIVESEEDING_NEITHER));
+      map.put(statusField,statusToString(STATUS_ACTIVE_NEITHER));
+      performUpdate(map,"WHERE "+statusField+"=?",list,invKey);
 
       // No need to do anything to the queue; it looks like it can take care of
       // itself.
@@ -753,6 +772,96 @@
     }
   }
 
+  /** Signal to a job that its underlying output connector has gone away.
+  *@param jobID is the identifier of the job.
+  *@param oldStatusValue is the current status value for the job.
+  */
+  public void noteOutputConnectorDeregistration(Long jobID, int oldStatusValue)
+    throws LCFException
+  {
+    int newStatusValue;
+    // The following states are special, in that when the underlying connector goes away, the jobs
+    // in such states are switched away to something else.  There are TWO reasons that a state may be in
+    // this category: EITHER we don't want the job in this state to be treated in the same way if its
+    // connector is uninstalled, OR we need feedback for the user interface.  If it's the latter situation,
+    // then all usages of the corresponding states will be identical - and that's in fact precisely where we
+    // start with in all the code.
+    switch (oldStatusValue)
+    {
+    case STATUS_ACTIVE:
+      newStatusValue = STATUS_ACTIVE_NOOUTPUT;
+      break;
+    case STATUS_ACTIVESEEDING:
+      newStatusValue = STATUS_ACTIVESEEDING_NOOUTPUT;
+      break;
+    case STATUS_ACTIVE_UNINSTALLED:
+      newStatusValue = STATUS_ACTIVE_NEITHER;
+      break;
+    case STATUS_ACTIVESEEDING_UNINSTALLED:
+      newStatusValue = STATUS_ACTIVESEEDING_NEITHER;
+      break;
+    case STATUS_READYFORDELETE:
+      newStatusValue = STATUS_READYFORDELETE_NOOUTPUT;
+      break;
+    default:
+      newStatusValue = oldStatusValue;
+      break;
+    }
+    if (newStatusValue == oldStatusValue)
+      return;
+
+    StringSet invKey = new StringSet(getJobStatusKey());
+
+    HashMap newValues = new HashMap();
+    newValues.put(statusField,statusToString(newStatusValue));
+    ArrayList list = new ArrayList();
+    list.add(jobID);
+    performUpdate(newValues,"WHERE "+idField+"=?",list,invKey);
+  }
+
+  /** Signal to a job that its underlying output connector has returned.
+  *@param jobID is the identifier of the job.
+  *@param oldStatusValue is the current status value for the job.
+  */
+  public void noteOutputConnectorRegistration(Long jobID, int oldStatusValue)
+    throws LCFException
+  {
+    int newStatusValue;
+    // The following states are special, in that when the underlying connector returns, the jobs
+    // in such states are switched back to their connector-installed value.
+    switch (oldStatusValue)
+    {
+    case STATUS_ACTIVE_NOOUTPUT:
+      newStatusValue = STATUS_ACTIVE;
+      break;
+    case STATUS_ACTIVESEEDING_NOOUTPUT:
+      newStatusValue = STATUS_ACTIVESEEDING;
+      break;
+    case STATUS_ACTIVE_NEITHER:
+      newStatusValue = STATUS_ACTIVE_UNINSTALLED;
+      break;
+    case STATUS_ACTIVESEEDING_NEITHER:
+      newStatusValue = STATUS_ACTIVESEEDING_UNINSTALLED;
+      break;
+    case STATUS_READYFORDELETE_NOOUTPUT:
+      newStatusValue = STATUS_READYFORDELETE;
+      break;
+    default:
+      newStatusValue = oldStatusValue;
+      break;
+    }
+    if (newStatusValue == oldStatusValue)
+      return;
+
+    StringSet invKey = new StringSet(getJobStatusKey());
+
+    HashMap newValues = new HashMap();
+    newValues.put(statusField,statusToString(newStatusValue));
+    ArrayList list = new ArrayList();
+    list.add(jobID);
+    performUpdate(newValues,"WHERE "+idField+"=?",list,invKey);
+  }
+
   /** Signal to a job that its underlying connector has gone away.
   *@param jobID is the identifier of the job.
   *@param oldStatusValue is the current status value for the job.
@@ -775,6 +884,12 @@
     case STATUS_ACTIVESEEDING:
       newStatusValue = STATUS_ACTIVESEEDING_UNINSTALLED;
       break;
+    case STATUS_ACTIVE_NOOUTPUT:
+      newStatusValue = STATUS_ACTIVE_NEITHER;
+      break;
+    case STATUS_ACTIVESEEDING_NOOUTPUT:
+      newStatusValue = STATUS_ACTIVESEEDING_NEITHER;
+      break;
     default:
       newStatusValue = oldStatusValue;
       break;
@@ -809,6 +924,12 @@
     case STATUS_ACTIVESEEDING_UNINSTALLED:
       newStatusValue = STATUS_ACTIVESEEDING;
       break;
+    case STATUS_ACTIVE_NEITHER:
+      newStatusValue = STATUS_ACTIVE_NOOUTPUT;
+      break;
+    case STATUS_ACTIVESEEDING_NEITHER:
+      newStatusValue = STATUS_ACTIVESEEDING_NOOUTPUT;
+      break;
     default:
       newStatusValue = oldStatusValue;
       break;
@@ -909,6 +1030,14 @@
       list.add(statusToString(STATUS_ACTIVESEEDING_UNINSTALLED));
       map.put(statusField,statusToString(STATUS_ACTIVE_UNINSTALLED));
       performUpdate(map,"WHERE "+statusField+"=?",list,invKey);
+      list.clear();
+      list.add(statusToString(STATUS_ACTIVESEEDING_NOOUTPUT));
+      map.put(statusField,statusToString(STATUS_ACTIVE_NOOUTPUT));
+      performUpdate(map,"WHERE "+statusField+"=?",list,invKey);
+      list.clear();
+      list.add(statusToString(STATUS_ACTIVESEEDING_NEITHER));
+      map.put(statusField,statusToString(STATUS_ACTIVE_NEITHER));
+      performUpdate(map,"WHERE "+statusField+"=?",list,invKey);
 
     }
     catch (LCFException e)
@@ -969,10 +1098,20 @@
       switch (status)
       {
       case STATUS_STARTINGUP:
-        if (connectionMgr.checkConnectorExists((String)row.getValue(connectionNameField)) && outputMgr.checkConnectorExists((String)row.getValue(outputNameField)))
-          newStatus = STATUS_ACTIVE;
+        if (connectionMgr.checkConnectorExists((String)row.getValue(connectionNameField)))
+        {
+          if (outputMgr.checkConnectorExists((String)row.getValue(outputNameField)))
+            newStatus = STATUS_ACTIVE;
+          else
+            newStatus = STATUS_ACTIVE_NOOUTPUT;
+        }
         else
-          newStatus = STATUS_ACTIVE_UNINSTALLED;
+        {
+          if (outputMgr.checkConnectorExists((String)row.getValue(outputNameField)))
+            newStatus = STATUS_ACTIVE_UNINSTALLED;
+          else
+            newStatus = STATUS_ACTIVE_NEITHER;
+        }
         break;
       case STATUS_ABORTINGSTARTINGUP:
         newStatus = STATUS_ABORTING;
@@ -987,7 +1126,7 @@
 
       HashMap map = new HashMap();
       map.put(statusField,statusToString(newStatus));
-      if (newStatus == STATUS_ACTIVE || newStatus == STATUS_ACTIVE_UNINSTALLED)
+      if (newStatus == STATUS_ACTIVE || newStatus == STATUS_ACTIVE_UNINSTALLED || newStatus == STATUS_ACTIVE_NOOUTPUT || newStatus == STATUS_ACTIVE_NEITHER)
       {
         map.put(startTimeField,new Long(startTime));
       }
@@ -1038,6 +1177,12 @@
       case STATUS_ACTIVESEEDING_UNINSTALLED:
         newStatus = STATUS_ACTIVE_UNINSTALLED;
         break;
+      case STATUS_ACTIVESEEDING_NOOUTPUT:
+        newStatus = STATUS_ACTIVE_NOOUTPUT;
+        break;
+      case STATUS_ACTIVESEEDING_NEITHER:
+        newStatus = STATUS_ACTIVE_NEITHER;
+        break;
       case STATUS_ACTIVEWAITSEEDING:
         newStatus = STATUS_ACTIVEWAIT;
         break;
@@ -1142,6 +1287,8 @@
       case STATUS_READYFORSTARTUP:
       case STATUS_ACTIVE:
       case STATUS_ACTIVE_UNINSTALLED:
+      case STATUS_ACTIVE_NOOUTPUT:
+      case STATUS_ACTIVE_NEITHER:
       case STATUS_ACTIVEWAIT:
       case STATUS_PAUSED:
       case STATUS_PAUSEDWAIT:
@@ -1150,6 +1297,8 @@
         break;
       case STATUS_ACTIVESEEDING:
       case STATUS_ACTIVESEEDING_UNINSTALLED:
+      case STATUS_ACTIVESEEDING_NOOUTPUT:
+      case STATUS_ACTIVESEEDING_NEITHER:
       case STATUS_ACTIVEWAITSEEDING:
       case STATUS_PAUSEDSEEDING:
       case STATUS_PAUSEDWAITSEEDING:
@@ -1211,6 +1360,8 @@
       case STATUS_READYFORSTARTUP:
       case STATUS_ACTIVE:
       case STATUS_ACTIVE_UNINSTALLED:
+      case STATUS_ACTIVE_NOOUTPUT:
+      case STATUS_ACTIVE_NEITHER:
       case STATUS_ACTIVEWAIT:
       case STATUS_PAUSED:
       case STATUS_PAUSEDWAIT:
@@ -1218,6 +1369,8 @@
         break;
       case STATUS_ACTIVESEEDING:
       case STATUS_ACTIVESEEDING_UNINSTALLED:
+      case STATUS_ACTIVESEEDING_NOOUTPUT:
+      case STATUS_ACTIVESEEDING_NEITHER:
       case STATUS_ACTIVEWAITSEEDING:
       case STATUS_PAUSEDSEEDING:
       case STATUS_PAUSEDWAITSEEDING:
@@ -1270,6 +1423,8 @@
       {
       case STATUS_ACTIVE:
       case STATUS_ACTIVE_UNINSTALLED:
+      case STATUS_ACTIVE_NOOUTPUT:
+      case STATUS_ACTIVE_NEITHER:
         newStatus = STATUS_PAUSED;
         break;
       case STATUS_ACTIVEWAIT:
@@ -1277,6 +1432,8 @@
         break;
       case STATUS_ACTIVESEEDING:
       case STATUS_ACTIVESEEDING_UNINSTALLED:
+      case STATUS_ACTIVESEEDING_NOOUTPUT:
+      case STATUS_ACTIVESEEDING_NEITHER:
         newStatus = STATUS_PAUSEDSEEDING;
         break;
       case STATUS_ACTIVEWAITSEEDING:
@@ -1330,19 +1487,39 @@
       switch (status)
       {
       case STATUS_PAUSED:
-        if (connectionMgr.checkConnectorExists(connectionName) && outputMgr.checkConnectorExists(outputName))
-          newStatus = STATUS_ACTIVE;
+        if (connectionMgr.checkConnectorExists(connectionName))
+        {
+          if (outputMgr.checkConnectorExists(outputName))
+            newStatus = STATUS_ACTIVE;
+          else
+            newStatus = STATUS_ACTIVE_NOOUTPUT;
+        }
         else
-          newStatus = STATUS_ACTIVE_UNINSTALLED;
+        {
+          if (outputMgr.checkConnectorExists(outputName))
+            newStatus = STATUS_ACTIVE_UNINSTALLED;
+          else
+            newStatus = STATUS_ACTIVE_NEITHER;
+        }
         break;
       case STATUS_PAUSEDWAIT:
         newStatus = STATUS_ACTIVEWAIT;
         break;
       case STATUS_PAUSEDSEEDING:
-        if (connectionMgr.checkConnectorExists(connectionName) && outputMgr.checkConnectorExists(outputName))
-          newStatus = STATUS_ACTIVESEEDING;
+        if (connectionMgr.checkConnectorExists(connectionName))
+        {
+          if (outputMgr.checkConnectorExists(outputName))
+            newStatus = STATUS_ACTIVESEEDING;
+          else
+            newStatus = STATUS_ACTIVESEEDING_NOOUTPUT;
+        }
         else
-          newStatus = STATUS_ACTIVESEEDING_UNINSTALLED;
+        {
+          if (outputMgr.checkConnectorExists(outputName))
+            newStatus = STATUS_ACTIVESEEDING_UNINSTALLED;
+          else
+            newStatus = STATUS_ACTIVESEEDING_NEITHER;
+        }
         break;
       case STATUS_PAUSEDWAITSEEDING:
         newStatus = STATUS_ACTIVEWAITSEEDING;
@@ -1632,6 +1809,16 @@
       return "R";
     case STATUS_ACTIVESEEDING_UNINSTALLED:
       return "r";
+    case STATUS_ACTIVE_NOOUTPUT:
+      return "O";
+    case STATUS_ACTIVESEEDING_NOOUTPUT:
+      return "o";
+    case STATUS_ACTIVE_NEITHER:
+      return "U";
+    case STATUS_ACTIVESEEDING_NEITHER:
+      return "u";
+    case STATUS_READYFORDELETE_NOOUTPUT:
+      return "D";
 
     default:
       throw new LCFException("Bad status value: "+Integer.toString(status));