/**
   * Notify RM to create a new node.
   *
   * @param parentID The ID of the machine the node belongs to
   * @param name the name of the node
   * @param number the number of the node (rank)
   * @return the id of the new node
   */
  public String createNode(String parentID, String name, int number) {
    String id = generateID();
    ElementAttributeManager mgr = new ElementAttributeManager();
    AttributeManager attrMgr = new AttributeManager();
    attrMgr.addAttribute(
        NodeAttributes.getStateAttributeDefinition().create(NodeAttributes.State.UP));
    try {
      attrMgr.addAttribute(
          NodeAttributes.getNumberAttributeDefinition().create(new Integer(number)));
    } catch (IllegalValueException e) {
      /*
       * This exception is not possible, since number is always valid.
       */
      RMCorePlugin.log(e);
      assert false;
    }
    attrMgr.addAttribute(ElementAttributes.getNameAttributeDefinition().create(name));
    mgr.setAttributeManager(new RangeSet(id), attrMgr);
    fireRuntimeNewNodeEvent(eventFactory.newRuntimeNewNodeEvent(parentID, mgr));

    DebugUtil.trace(
        DebugUtil.RTS_TRACING,
        "RTS {0}: new node #{1}",
        getResourceManager().getConfiguration().getName(),
        id); //$NON-NLS-1$

    return id;
  }
  /**
   * Create a single process.
   *
   * @param jobId the parent job that the process belongs to
   * @param the index (job rank) of the new process
   * @since 2.0
   */
  public void createProcess(String jobId, int index) {
    ElementAttributeManager mgr = new ElementAttributeManager();
    AttributeManager attrMgr = new AttributeManager();
    attrMgr.addAttribute(
        ProcessAttributes.getStateAttributeDefinition().create(ProcessAttributes.State.STARTING));
    mgr.setAttributeManager(new RangeSet(index), attrMgr);
    fireRuntimeNewProcessEvent(eventFactory.newRuntimeNewProcessEvent(jobId, mgr));

    DebugUtil.trace(
        DebugUtil.RTS_TRACING,
        "RTS {0}: new process #{1}",
        getResourceManager().getConfiguration().getName(),
        Integer.toString(index)); // $NON-NLS-1$
  }
  /**
   * Notify RM to create a new queue.
   *
   * @param name the name of the queue
   * @return the id of the new queue
   */
  public String createQueue(String name) {
    String id = generateID();
    ElementAttributeManager mgr = new ElementAttributeManager();
    AttributeManager attrMgr = new AttributeManager();
    attrMgr.addAttribute(ElementAttributes.getNameAttributeDefinition().create(name));
    mgr.setAttributeManager(new RangeSet(id), attrMgr);
    IPResourceManager rm =
        (IPResourceManager) getResourceManager().getAdapter(IPResourceManager.class);
    fireRuntimeNewQueueEvent(eventFactory.newRuntimeNewQueueEvent(rm.getID(), mgr));

    DebugUtil.trace(
        DebugUtil.RTS_TRACING,
        "RTS {0}: new queue #{1}",
        getResourceManager().getConfiguration().getName(),
        id); //$NON-NLS-1$

    return id;
  }
  /**
   * Change attributes of a node
   *
   * @param nodeID
   * @param changedAttrMgr
   */
  public void changeNode(String nodeID, AttributeManager changedAttrMgr) {
    AttributeManager attrMgr = new AttributeManager();
    attrMgr.addAttributes(changedAttrMgr.getAttributes());
    ElementAttributeManager elementAttrs = new ElementAttributeManager();
    elementAttrs.setAttributeManager(new RangeSet(nodeID), attrMgr);
    IRuntimeNodeChangeEvent event = eventFactory.newRuntimeNodeChangeEvent(elementAttrs);
    fireRuntimeNodeChangeEvent(event);

    for (IAttribute<?, ?, ?> attr : changedAttrMgr.getAttributes()) {
      DebugUtil.trace(
          DebugUtil.RTS_TRACING,
          "RTS {0}, node #{1}: {2}={3}",
          getResourceManager().getConfiguration().getName(),
          nodeID,
          attr.getDefinition().getId(),
          attr.getValueAsString()); // $NON-NLS-1$
    }
  }
  /**
   * Change attributes of a process
   *
   * @param jobId id of processes' parent job
   * @param processJobRanks
   * @param changedAttrMgr
   * @since 2.0
   */
  public void changeProcesses(
      String jobId, BitSet processJobRanks, AttributeManager changedAttrMgr) {
    AttributeManager attrMgr = new AttributeManager();
    attrMgr.addAttributes(changedAttrMgr.getAttributes());
    ElementAttributeManager elementAttrs = new ElementAttributeManager();
    elementAttrs.setAttributeManager(new RangeSet(processJobRanks), attrMgr);
    IRuntimeProcessChangeEvent event =
        eventFactory.newRuntimeProcessChangeEvent(jobId, elementAttrs);
    fireRuntimeProcessChangeEvent(event);

    for (IAttribute<?, ?, ?> attr : changedAttrMgr.getAttributes()) {
      DebugUtil.trace(
          DebugUtil.RTS_TRACING,
          "RTS {0}, processes #{1}: {2}={3}",
          getResourceManager().getConfiguration().getName(),
          processJobRanks,
          attr.getDefinition().getId(),
          attr.getValueAsString()); // $NON-NLS-1$
    }
  }
  /*
   * (non-Javadoc)
   *
   * @see
   * org.eclipse.ptp.rtsystem.IRuntimeSystem#startup(org.eclipse.core.runtime
   * .IProgressMonitor)
   */
  public void startup(IProgressMonitor monitor) throws CoreException {
    SubMonitor subMon = SubMonitor.convert(monitor, 100);

    synchronized (this) {
      startupMonitor = subMon;
    }

    initialize();

    subMon.subTask(Messages.AbstractToolRuntimeSystem_1);

    DebugUtil.trace(
        DebugUtil.RTS_TRACING,
        "RTS {0}: startup",
        getResourceManager().getConfiguration().getName()); // $NON-NLS-1$

    try {
      remoteServices =
          PTPRemoteCorePlugin.getDefault()
              .getRemoteServices(
                  getResourceManager().getControlConfiguration().getRemoteServicesId(),
                  subMon.newChild(10));
      if (remoteServices == null) {
        throw new CoreException(
            new Status(
                IStatus.ERROR,
                RMCorePlugin.PLUGIN_ID,
                Messages.AbstractToolRuntimeSystem_Exception_NoRemoteServices));
      }

      IRemoteConnectionManager connectionManager = remoteServices.getConnectionManager();
      Assert.isNotNull(connectionManager);

      subMon.worked(10);
      subMon.subTask(Messages.AbstractToolRuntimeSystem_2);

      connection =
          connectionManager.getConnection(
              getResourceManager().getControlConfiguration().getConnectionName());
      if (connection == null) {
        throw new CoreException(
            new Status(
                IStatus.ERROR,
                RMCorePlugin.PLUGIN_ID,
                Messages.AbstractToolRuntimeSystem_Exception_NoConnection));
      }

      if (!connection.isOpen()) {
        try {
          connection.open(subMon.newChild(50));
        } catch (RemoteConnectionException e) {
          throw new CoreException(
              new Status(IStatus.ERROR, RMCorePlugin.PLUGIN_ID, e.getMessage()));
        }
      }

      if (subMon.isCanceled()) {
        connection.close();
        return;
      }

      try {
        doStartup(subMon.newChild(40));
      } catch (CoreException e) {
        connection.close();
        throw e;
      }

      if (subMon.isCanceled()) {
        connection.close();
        return;
      }

      /*
       * Wait for discover job to complete so we can check it's status and
       * throw an exception if it fails.
       */
      Job discoverJob = createDiscoverJob();
      if (discoverJob != null) {
        /*
         * Disable error messages being displayed on job's progress
         * monitor. These are handled by the progress service starting
         * the RM.
         */
        discoverJob.setProperty(
            IProgressConstants.NO_IMMEDIATE_ERROR_PROMPT_PROPERTY, Boolean.TRUE);
        discoverJob.schedule();
        try {
          discoverJob.join();
        } catch (InterruptedException e) {
          // Just check result
        }
        IStatus status = discoverJob.getResult();
        if (!status.isOK()) {
          throw new CoreException(status);
        }
      }

      if (jobQueueThread == null) {
        jobQueueThread =
            new Thread(
                new JobRunner(), Messages.AbstractToolRuntimeSystem_JobQueueManagerThreadTitle);
        jobQueueThread.start();
      }

      /*
       * Register for connection events
       */
      connection.addConnectionChangeListener(fConnectionChangeHandler);

      fireRuntimeRunningStateEvent(eventFactory.newRuntimeRunningStateEvent());
    } finally {
      synchronized (this) {
        startupMonitor = null;
      }
      if (monitor != null) {
        monitor.done();
      }
    }
  }
  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.ptp.rtsystem.IRuntimeSystem#shutdown()
   */
  public void shutdown() throws CoreException {
    DebugUtil.trace(
        DebugUtil.RTS_TRACING,
        "RTS {0}: shutdown",
        getResourceManager().getConfiguration().getName()); // $NON-NLS-1$

    /*
     * Remove listener to avoid re-entry
     */
    if (connection != null) {
      connection.removeConnectionChangeListener(fConnectionChangeHandler);
    }

    try {
      stopEvents();
    } catch (CoreException e) {
      // Ignore exception and shutdown anyway
      RMCorePlugin.log(e);
    }

    try {
      doShutdown();
    } finally {

      /*
       * Stop jobs that might be in the pending queue. Also stop the
       * thread that dispatches pending jobs.
       */
      if (jobQueueThread != null) {
        jobQueueThread.interrupt();
        for (Job job : pendingJobQueue) {
          job.cancel();
        }
      }

      /*
       * Stop jobs that are running or that already finished.
       */
      Iterator<Job> iterator = jobs.values().iterator();
      while (iterator.hasNext()) {
        Job job = iterator.next();
        job.cancel();
        iterator.remove();
      }

      synchronized (this) {
        if (startupMonitor != null) {
          startupMonitor.setCanceled(true);
        }
      }

      /*
       * Close the the connection.
       */
      if (connection != null) {
        connection.close();
      }

      jobQueueThread = null;
      fireRuntimeShutdownStateEvent(eventFactory.newRuntimeShutdownStateEvent());
    }
  }
  /**
   * Notify RM to create a new job.
   *
   * @param parentID parent element ID
   * @param jobID the ID of the job
   * @param attrMgr attributes from the job thread
   * @return job id of the newly created job
   */
  public String createJob(String parentID, AttributeManager attrMgr) throws CoreException {
    ElementAttributeManager mgr = new ElementAttributeManager();
    AttributeManager jobAttrMgr = new AttributeManager();

    /*
     * Add generated attributes.
     */
    String jobID = generateID().toString();
    jobAttrMgr.addAttribute(JobAttributes.getJobIdAttributeDefinition().create(jobID));
    jobAttrMgr.addAttribute(
        JobAttributes.getStatusAttributeDefinition()
            .create(MPIJobAttributes.Status.NORMAL.toString()));
    jobAttrMgr.addAttribute(
        JobAttributes.getUserIdAttributeDefinition().create(System.getenv("USER"))); // $NON-NLS-1$
    jobAttrMgr.addAttribute(
        ElementAttributes.getNameAttributeDefinition().create(generateJobName()));

    /*
     * Get mandatory launch attributes.
     */
    String subId = getAttributeValue(JobAttributes.getSubIdAttributeDefinition(), attrMgr);
    String execName =
        getAttributeValue(JobAttributes.getExecutableNameAttributeDefinition(), attrMgr);
    String execPath =
        getAttributeValue(JobAttributes.getExecutablePathAttributeDefinition(), attrMgr);
    String workDir =
        getAttributeValue(JobAttributes.getWorkingDirectoryAttributeDefinition(), attrMgr);
    Integer numProcs =
        getAttributeValue(JobAttributes.getNumberOfProcessesAttributeDefinition(), attrMgr);
    List<? extends String> progArgs =
        getAttributeValue(JobAttributes.getProgramArgumentsAttributeDefinition(), attrMgr);

    /*
     * Copy these relevant attributes to IPJob.
     */
    jobAttrMgr.addAttribute(JobAttributes.getSubIdAttributeDefinition().create(subId));
    jobAttrMgr.addAttribute(JobAttributes.getExecutableNameAttributeDefinition().create(execName));
    jobAttrMgr.addAttribute(JobAttributes.getExecutablePathAttributeDefinition().create(execPath));
    jobAttrMgr.addAttribute(JobAttributes.getWorkingDirectoryAttributeDefinition().create(workDir));
    try {
      jobAttrMgr.addAttribute(
          JobAttributes.getNumberOfProcessesAttributeDefinition().create(numProcs));
    } catch (IllegalValueException e) {
      RMCorePlugin.log(e);
    }
    jobAttrMgr.addAttribute(
        JobAttributes.getProgramArgumentsAttributeDefinition()
            .create(progArgs.toArray(new String[0])));

    /*
     * Copy optional attributes
     */
    BooleanAttribute debugAttr =
        attrMgr.getAttribute(JobAttributes.getDebugFlagAttributeDefinition());
    if (debugAttr != null) {
      jobAttrMgr.addAttribute(
          JobAttributes.getDebugFlagAttributeDefinition().create(debugAttr.getValue()));
    }

    /*
     * Notify RM.
     */
    mgr.setAttributeManager(new RangeSet(jobID), jobAttrMgr);
    fireRuntimeNewJobEvent(eventFactory.newRuntimeNewJobEvent(parentID, mgr));

    DebugUtil.trace(
        DebugUtil.RTS_TRACING,
        "RTS {0}: new job #{1}",
        getResourceManager().getConfiguration().getName(),
        jobID); //$NON-NLS-1$

    return jobID;
  }