  public void run()
  {
    resetManager.registerMe();
    try
    {
      IThreadContext threadContext = ThreadContextFactory.make();
      IJobManager jobManager = JobManagerFactory.make(threadContext);
      IOutputConnectionManager connectionManager = OutputConnectionManagerFactory.make(threadContext);
      IRepositoryConnectionManager repositoryConnectionManager = RepositoryConnectionManagerFactory.make(threadContext);
      while (true)
      {
        try
        {
          resetManager.waitForReset(threadContext);
          JobStartRecord[] jobsNeedingNotification = jobManager.getJobsReadyForInactivity();
          try
          {
            HashMap connectionNames = new HashMap();
            int k = 0;
            while (k < jobsNeedingNotification.length)
            {
              JobStartRecord jsr = jobsNeedingNotification[k++];
              Long jobID = jsr.getJobID();
              IJobDescription job = jobManager.load(jobID,true);
              if (job != null)
              {
                String repositoryConnectionName = job.getConnectionName();
                String outputConnectionName = job.getOutputConnectionName();
                OutputAndRepositoryConnection c = new OutputAndRepositoryConnection(outputConnectionName, repositoryConnectionName);
                connectionNames.put(c,c);
              }
            }
            HashMap notifiedConnections = new HashMap();
            Iterator iter = connectionNames.keySet().iterator();
            while (iter.hasNext())
            {
              OutputAndRepositoryConnection connections = (OutputAndRepositoryConnection)iter.next();
              String outputConnectionName = connections.getOutputConnectionName();
              String repositoryConnectionName = connections.getRepositoryConnectionName();
              OutputNotifyActivity activity = new OutputNotifyActivity(repositoryConnectionName,repositoryConnectionManager,outputConnectionName);
              IOutputConnection connection = connectionManager.load(outputConnectionName);
              if (connection != null)
              {
                IOutputConnector connector = OutputConnectorFactory.grab(threadContext,connection.getClassName(),connection.getConfigParams(),connection.getMaxConnections());
                if (connector != null)
                {
                  try
                  {
                    try
                    {
                      connector.noteJobComplete(activity);
                      notifiedConnections.put(connections,connections);
                    }
                    catch (ServiceInterruption e)
                    {
                      Logging.threads.warn("Service interruption notifying connection - retrying: "+e.getMessage(),e);
                      continue;
                    }
                    catch (ManifoldCFException e)
                    {
                      if (e.getErrorCode() == ManifoldCFException.INTERRUPTED)
                        throw e;
                      if (e.getErrorCode() == ManifoldCFException.DATABASE_CONNECTION_ERROR)
                        throw e;
                      if (e.getErrorCode() == ManifoldCFException.SETUP_ERROR)
                        throw e;
                      Logging.threads.error(e.getMessage(),e);
                      continue;
                    }
                  }
                  finally
                  {
                    OutputConnectorFactory.release(connector);
                  }
                }
              }
            }
            k = 0;
            while (k < jobsNeedingNotification.length)
            {
              JobStartRecord jsr = jobsNeedingNotification[k++];
              Long jobID = jsr.getJobID();
              IJobDescription job = jobManager.load(jobID,true);
              if (job != null)
              {
                String outputConnectionName = job.getOutputConnectionName();
                String repositoryConnectionName = job.getConnectionName();
                OutputAndRepositoryConnection c = new OutputAndRepositoryConnection(outputConnectionName, repositoryConnectionName);
                if (notifiedConnections.get(c) != null)
                {
                  jobManager.inactivateJob(jobID);
                  jsr.noteStarted();
                }
              }
            }
          }
          finally
          {
            ManifoldCFException exception = null;
            int i = 0;
            while (i < jobsNeedingNotification.length)
            {
              JobStartRecord jsr = jobsNeedingNotification[i++];
              if (!jsr.wasStarted())
              {
                try
                {
                  jobManager.resetNotifyJob(jsr.getJobID());
                }
                catch (ManifoldCFException e)
                {
                  exception = e;
                }
              }
            }
            if (exception != null)
              throw exception;
          }
          ManifoldCF.sleep(10000L);
        }
        catch (ManifoldCFException e)
        {
          if (e.getErrorCode() == ManifoldCFException.INTERRUPTED)
            break;
          if (e.getErrorCode() == ManifoldCFException.DATABASE_CONNECTION_ERROR)
          {
            resetManager.noteEvent();
            Logging.threads.error("Job notification thread aborting and restarting due to database connection reset: "+e.getMessage(),e);
            try
            {
              ManifoldCF.sleep(10000L);
            }
            catch (InterruptedException se)
            {
              break;
            }
            continue;
          }
          Logging.threads.error("Exception tossed: "+e.getMessage(),e);
          if (e.getErrorCode() == ManifoldCFException.SETUP_ERROR)
          {
            System.exit(1);
          }
        }
        catch (InterruptedException e)
        {
          break;
        }
        catch (OutOfMemoryError e)
        {
          System.err.println("agents process ran out of memory - shutting down");
          e.printStackTrace(System.err);
          System.exit(-200);
        }
        catch (Throwable e)
        {
          Logging.threads.fatal("Error tossed: "+e.getMessage(),e);
        }
      }
    }
    catch (Throwable e)
    {
      System.err.println("agents process could not start - shutting down");
      Logging.threads.fatal("JobNotificationThread initialization error tossed: "+e.getMessage(),e);
      System.exit(-300);
    }
  }

  public void run()
  {
    resetManager.registerMe();
    try
    {
      IThreadContext threadContext = ThreadContextFactory.make();
      IJobManager jobManager = JobManagerFactory.make(threadContext);
      IOutputConnectionManager connectionManager = OutputConnectionManagerFactory.make(threadContext);
      IRepositoryConnectionManager repositoryConnectionManager = RepositoryConnectionManagerFactory.make(threadContext);
      while (true)
      {
        try
        {
          resetManager.waitForReset(threadContext);
          JobStartRecord[] jobsNeedingNotification = jobManager.getJobsReadyForInactivity();
          try
          {
            HashMap connectionNames = new HashMap();
            int k = 0;
            while (k < jobsNeedingNotification.length)
            {
              JobStartRecord jsr = jobsNeedingNotification[k++];
              Long jobID = jsr.getJobID();
              IJobDescription job = jobManager.load(jobID,true);
              if (job != null)
              {
                String repositoryConnectionName = job.getConnectionName();
                String outputConnectionName = job.getOutputConnectionName();
                OutputAndRepositoryConnection c = new OutputAndRepositoryConnection(outputConnectionName, repositoryConnectionName);
                connectionNames.put(c,c);
              }
            }
            HashMap notifiedConnections = new HashMap();
            Iterator iter = connectionNames.keySet().iterator();
            while (iter.hasNext())
            {
              OutputAndRepositoryConnection connections = (OutputAndRepositoryConnection)iter.next();
              String outputConnectionName = connections.getOutputConnectionName();
              String repositoryConnectionName = connections.getRepositoryConnectionName();
              OutputNotifyActivity activity = new OutputNotifyActivity(repositoryConnectionName,repositoryConnectionManager,outputConnectionName);
              IOutputConnection connection = connectionManager.load(outputConnectionName);
              if (connection != null)
              {
                IOutputConnector connector = OutputConnectorFactory.grab(threadContext,connection.getClassName(),connection.getConfigParams(),connection.getMaxConnections());
                if (connector != null)
                {
                  try
                  {
                    try
                    {
                      connector.noteJobComplete(activity);
                    }
                    catch (ServiceInterruption e)
                    {
                      Logging.threads.warn("Service interruption notifying connection - retrying: "+e.getMessage(),e);
                      continue;
                    }
                    catch (ManifoldCFException e)
                    {
                      if (e.getErrorCode() == ManifoldCFException.INTERRUPTED)
                        throw e;
                      if (e.getErrorCode() == ManifoldCFException.DATABASE_CONNECTION_ERROR)
                        throw e;
                      if (e.getErrorCode() == ManifoldCFException.SETUP_ERROR)
                        throw e;
                      Logging.threads.error(e.getMessage(),e);
                    }
                    notifiedConnections.put(connections,connections);
                  }
                  finally
                  {
                    OutputConnectorFactory.release(connector);
                  }
                }
              }
            }
            k = 0;
            while (k < jobsNeedingNotification.length)
            {
              JobStartRecord jsr = jobsNeedingNotification[k++];
              Long jobID = jsr.getJobID();
              IJobDescription job = jobManager.load(jobID,true);
              if (job != null)
              {
                String outputConnectionName = job.getOutputConnectionName();
                String repositoryConnectionName = job.getConnectionName();
                OutputAndRepositoryConnection c = new OutputAndRepositoryConnection(outputConnectionName, repositoryConnectionName);
                if (notifiedConnections.get(c) != null)
                {
                  jobManager.inactivateJob(jobID);
                  jsr.noteStarted();
                }
              }
            }
          }
          finally
          {
            ManifoldCFException exception = null;
            int i = 0;
            while (i < jobsNeedingNotification.length)
            {
              JobStartRecord jsr = jobsNeedingNotification[i++];
              if (!jsr.wasStarted())
              {
                try
                {
                  jobManager.resetNotifyJob(jsr.getJobID());
                }
                catch (ManifoldCFException e)
                {
                  exception = e;
                }
              }
            }
            if (exception != null)
              throw exception;
          }
          ManifoldCF.sleep(10000L);
        }
        catch (ManifoldCFException e)
        {
          if (e.getErrorCode() == ManifoldCFException.INTERRUPTED)
            break;
          if (e.getErrorCode() == ManifoldCFException.DATABASE_CONNECTION_ERROR)
          {
            resetManager.noteEvent();
            Logging.threads.error("Job notification thread aborting and restarting due to database connection reset: "+e.getMessage(),e);
            try
            {
              ManifoldCF.sleep(10000L);
            }
            catch (InterruptedException se)
            {
              break;
            }
            continue;
          }
          Logging.threads.error("Exception tossed: "+e.getMessage(),e);
          if (e.getErrorCode() == ManifoldCFException.SETUP_ERROR)
          {
            System.exit(1);
          }
        }
        catch (InterruptedException e)
        {
          break;
        }
        catch (OutOfMemoryError e)
        {
          System.err.println("agents process ran out of memory - shutting down");
          e.printStackTrace(System.err);
          System.exit(-200);
        }
        catch (Throwable e)
        {
          Logging.threads.fatal("Error tossed: "+e.getMessage(),e);
        }
      }
    }
    catch (Throwable e)
    {
      System.err.println("agents process could not start - shutting down");
      Logging.threads.fatal("JobNotificationThread initialization error tossed: "+e.getMessage(),e);
      System.exit(-300);
    }
  }
