// JMSCommunicationThread
  @Override
  protected void createProducersAndConsumers() throws Exception {
    final String configuration = model.getConfigurationName();
    // Write (if allowed) and also read the client topic
    if (model.isWriteAllowed())
      client_producer = createProducer(Preferences.getJMS_AlarmClientTopic(configuration));
    else client_producer = null;
    client_consumer = createConsumer(Preferences.getJMS_AlarmClientTopic(configuration));
    // Read messages from server
    server_consumer = createConsumer(Preferences.getJMS_AlarmServerTopic(configuration));

    // Handle MapMessages
    final MessageListener message_listener =
        new MessageListener() {
          @Override
          public void onMessage(final Message message) {
            if (message instanceof MapMessage) handleMapMessage((MapMessage) message);
            else
              Activator.getLogger()
                  .log(Level.WARNING, "Message type {0} not handled", message.getClass().getName());
          }
        };
    client_consumer.setMessageListener(message_listener);
    server_consumer.setMessageListener(message_listener);
  }
 /**
  * Initialize
  *
  * @param talker Talker that'll be used to annunciate
  * @param work_queue Work queue of the 'main' thread
  * @param root_name
  * @throws Exception on error
  */
 public AlarmServer(final WorkQueue work_queue, final String root_name) throws Exception {
   this.root_name = root_name;
   this.work_queue = work_queue;
   rdb =
       new AlarmRDB(
           this,
           Preferences.getRDB_Url(),
           Preferences.getRDB_User(),
           Preferences.getRDB_Password(),
           Preferences.getRDB_Schema(),
           root_name);
   messenger = new ServerCommunicator(this, work_queue, root_name);
   readConfiguration();
 }
 /** {@inheritDoc} */
 @Override
 public void init(ItemInfo item, AAData auto_action, IActionHandler handler) throws Exception {
   String details = auto_action.getDetails();
   if (details != null && details.startsWith("cmd:")) this.command = details.substring(4);
   else this.command = details;
   this.wait = Preferences.getCommandCheckTime();
 }
 /** {@inheritDoc} */
 @Override
 public void execute(List<PVSnapshot> pvs) throws Exception {
   final String dir;
   try {
     dir = Preferences.getCommandDirectory();
   } catch (Exception ex) {
     Activator.getLogger().log(Level.SEVERE, "Can not find command directory", ex);
     return;
   }
   if (command.contains("*")) { // List PVs and their alarm severity
     final StringBuilder buf = new StringBuilder();
     for (PVSnapshot pv : pvs) {
       if (buf.length() > 0) buf.append(" ");
       buf.append(pv.getPath());
       buf.append(" ");
       buf.append(pv.getSeverity().name());
     }
     final String expanded_command = command.replace("*", buf.toString());
     execCmd(dir, expanded_command, wait);
   } else execCmd(dir, command, wait);
 }
 /** Start PVs */
 private void startPVs() {
   final long delay = Preferences.getPVStartDelay();
   // Must not sync while calling PV, because Channel Access updates
   // might arrive while we're trying to start/stop channels,
   // and those updates will try to lock the Alarm Server,
   // which then results in a deadlock:
   // a) We lock AlarmServer, then try to call CA,
   //     which takes internal JNI locks
   // b) CA takes internal locks, calls PV update callback, which
   //    then tries to lock the AlarmServer when locating the PV by name
   final AlarmPV pvs[];
   synchronized (this) {
     pvs = pv_list.clone();
   }
   for (AlarmPV pv : pvs) {
     try {
       pv.start();
       if (delay > 0) Thread.sleep(delay);
     } catch (Exception ex) {
       Activator.getLogger().log(Level.SEVERE, "Error starting PV " + pv.getName(), ex);
     }
   }
 }
/**
 * Receives alarm updates, sends acknowledgments.
 *
 * <p>The communicator is started early on in "Queue" mode, where it queues received events until
 * the application has fully initialized.
 *
 * <p>Then it is switched to "Dispatch" mode, first sending the queued events, and from then on
 * directly dispatching received events.
 *
 * @author Kay Kasemir
 */
@SuppressWarnings("nls")
class AlarmClientCommunicator extends JMSCommunicationWorkQueueThread {
  /** Application name used when sending JMS messages */
  private static final String APPLICATION = "CSS";

  /** The model */
  private final AlarmClientModel model;

  /** Action to update a PV's state */
  private class UpdateAction implements Runnable {
    private final AlarmUpdateInfo info;

    public UpdateAction(final AlarmUpdateInfo info) {
      this.info = info;
    }

    @Override
    public void run() {
      model.updatePV(info);
    }

    @Override
    public String toString() {
      return info.toString();
    }
  }

  /** Action to enable/disable a PV */
  private class EnableAction implements Runnable {
    private final String name;
    private final boolean enable;

    public EnableAction(final String name, final boolean enable) {
      this.name = name;
      this.enable = enable;
    }

    @Override
    public void run() {
      model.updateEnablement(name, enable);
    }

    @Override
    public String toString() {
      return "Enable " + name + ": " + enable;
    }
  }

  /** Update mode SYNC on queue */
  private boolean use_queue = true;

  /** Event queue (filled in Queue mode) */
  private WorkQueue queue = new WorkQueue();

  /** Thread that checks for server timeouts. Using twice the expected idle message rate */
  private final TimeoutTimer timeout_timer =
      new TimeoutTimer(Preferences.getJMS_IdleTimeout() * 2000) {
        @Override
        protected void timeout() {
          model.fireServerTimeout();
        }
      };

  /** JMS producer for messages from client to server (<code>null</code> when read-only) */
  private MessageProducer client_producer;

  /** JMS read-back of client messages */
  private MessageConsumer client_consumer;

  /** JMS consumer for messages from server to client */
  private MessageConsumer server_consumer;

  /** Host for messages */
  private final String host = InetAddress.getLocalHost().getHostName();

  /** User for messages. Updated with authenticated user */
  private String user = System.getProperty("user.name");

  /**
   * Initialize communicator that writes to the 'client' topic and listens to 'server' topic
   *
   * @param model Model for which to communicate
   * @throws Exception on error
   */
  public AlarmClientCommunicator(final AlarmClientModel model) throws Exception {
    super(Preferences.getJMS_URL());
    this.model = model;
    timeout_timer.start();
  }

  // JMSCommunicationThread
  @Override
  protected void createProducersAndConsumers() throws Exception {
    final String configuration = model.getConfigurationName();
    // Write (if allowed) and also read the client topic
    if (model.isWriteAllowed())
      client_producer = createProducer(Preferences.getJMS_AlarmClientTopic(configuration));
    else client_producer = null;
    client_consumer = createConsumer(Preferences.getJMS_AlarmClientTopic(configuration));
    // Read messages from server
    server_consumer = createConsumer(Preferences.getJMS_AlarmServerTopic(configuration));

    // Handle MapMessages
    final MessageListener message_listener =
        new MessageListener() {
          @Override
          public void onMessage(final Message message) {
            if (message instanceof MapMessage) handleMapMessage((MapMessage) message);
            else
              Activator.getLogger()
                  .log(Level.WARNING, "Message type {0} not handled", message.getClass().getName());
          }
        };
    client_consumer.setMessageListener(message_listener);
    server_consumer.setMessageListener(message_listener);
  }

  // JMSCommunicationThread
  @Override
  protected void closeProducersAndConsumers() throws Exception {
    timeout_timer.cancel();
    server_consumer.close();
    server_consumer = null;
    client_consumer.close();
    client_consumer = null;
    if (client_producer != null) {
      client_producer.close();
      client_producer = null;
    }
  }

  /**
   * Dispatch queued events, or directly dispatch received events?
   *
   * <p>While reading model information, queue mode should be enabled. Then, after model information
   * from RDB has been obtained, disable queue mode which also dispatched all the queued up events.
   *
   * @param use_queue <code>true</code> to queue
   */
  public void setQueueMode(final boolean use_queue) {
    this.use_queue = use_queue;
    if (use_queue) return;
    // Queuing turned off -> Dispatched what has accumulated until now
    queue.performQueuedCommands();
  }

  /**
   * Create message initialized with basic alarm & application info
   *
   * @param text TEXT property
   * @return MapMessage
   * @throws Exception on error.
   */
  private MapMessage createMapMessage(final String text) throws Exception {
    final MapMessage map = createMapMessage();
    map.setString(JMSLogMessage.TYPE, JMSAlarmMessage.TYPE_ALARM);
    map.setString(JMSAlarmMessage.CONFIG, model.getConfigurationName());
    map.setString(JMSLogMessage.TEXT, text);
    map.setString(JMSLogMessage.APPLICATION_ID, APPLICATION);
    map.setString(JMSLogMessage.HOST, host);
    User loggedUser = SecurityFacade.getInstance().getCurrentUser();
    if (loggedUser == null) // if no user logged in...
    user = System.getProperty("user.name"); // $NON-NLS-1$
    else user = loggedUser.getUsername();
    map.setString(JMSLogMessage.USER, user);
    return map;
  }

  /**
   * Send request to enable/disable maintenance mode to alarm server
   *
   * @param maintenance <code>true</code> to enable
   */
  public void requestMaintenanceMode(final boolean maintenance) {
    if (client_producer == null) return;
    execute(
        new Runnable() {
          @Override
          public void run() {
            try {
              final MapMessage map = createMapMessage(JMSAlarmMessage.TEXT_MODE);
              map.setString(
                  JMSAlarmMessage.VALUE,
                  maintenance
                      ? JMSAlarmMessage.VALUE_MODE_MAINTENANCE
                      : JMSAlarmMessage.VALUE_MODE_NORMAL);
              client_producer.send(map);
            } catch (Exception ex) {
              Activator.getLogger().log(Level.SEVERE, "Cannot request maintenance mode", ex);
            }
          }
        });
  }

  /**
   * Ask alarm server to acknowledge alarm.
   *
   * @param pv PV to acknowledge
   * @param acknowledge Acknowledge, or un-acknowledge?
   */
  public void requestAcknowledgement(final AlarmTreePV pv, final boolean acknowledge) {
    if (client_producer == null) return;
    execute(
        new Runnable() {
          @Override
          public void run() {
            try {
              final MapMessage map =
                  createMapMessage(
                      acknowledge
                          ? JMSAlarmMessage.TEXT_ACKNOWLEDGE
                          : JMSAlarmMessage.TEXT_UNACKNOWLEDGE);
              map.setString(JMSLogMessage.NAME, pv.getName());
              client_producer.send(map);
            } catch (Exception ex) {
              Activator.getLogger().log(Level.SEVERE, "Cannot request acknowledgement", ex);
            }
          }
        });
  }

  /**
   * Notify alarm server and other clients about updated PV configuration in RDB.
   *
   * @param path Path name of the modified item or <code>null</code> if all changed
   */
  public void sendConfigUpdate(final String path) {
    if (client_producer == null) return;
    execute(
        new Runnable() {
          @Override
          public void run() {
            try {
              final MapMessage map = createMapMessage(JMSAlarmMessage.TEXT_CONFIG);
              if (path != null) map.setString(JMSLogMessage.NAME, path);
              client_producer.send(map);
            } catch (Exception ex) {
              Activator.getLogger().log(Level.SEVERE, "Cannot send config update", ex);
            }
          }
        });
  }

  /** Send 'debug' message to server */
  public void triggerDebugAction() {
    if (client_producer == null) return;
    execute(
        new Runnable() {
          @Override
          public void run() {
            try {
              final MapMessage map = createMapMessage(JMSAlarmMessage.TEXT_DEBUG);
              client_producer.send(map);
            } catch (Exception ex) {
              Activator.getLogger().log(Level.WARNING, "Debug trigger failed", ex);
            }
          }
        });
  }

  /** Invoked for received messages */
  private void handleMapMessage(final MapMessage message) {
    try {
      final String text = message.getString(JMSLogMessage.TEXT);
      Runnable action = null;

      // Create action, or handle the message right away

      // Alarm state change?
      if (JMSAlarmMessage.TEXT_STATE.equals(text)) {
        // Received a state update from server, reset timeout
        timeout_timer.reset();
        action = new UpdateAction(AlarmUpdateInfo.fromMapMessage(message));
        model.updateServerState(false);
      } else if (JMSAlarmMessage.TEXT_STATE_MAINTENANCE.equals(text)) {
        timeout_timer.reset();
        action = new UpdateAction(AlarmUpdateInfo.fromMapMessage(message));
        model.updateServerState(true);
      }
      // Idle messages in absence of 'real' traffic?
      else if (JMSAlarmMessage.TEXT_IDLE.equals(text)) {
        timeout_timer.reset();
        model.updateServerState(false);
      } else if (JMSAlarmMessage.TEXT_IDLE_MAINTENANCE.equals(text)) {
        timeout_timer.reset();
        model.updateServerState(true);
      }
      // Enable/disable?
      else if (JMSAlarmMessage.TEXT_ENABLE.equals(text)) {
        timeout_timer.reset();
        final String name = message.getString(JMSLogMessage.NAME);
        action = new EnableAction(name, true);
      } else if (JMSAlarmMessage.TEXT_DISABLE.equals(text)) {
        timeout_timer.reset();
        final String name = message.getString(JMSLogMessage.NAME);
        action = new EnableAction(name, false);
      }
      // Configuration change
      else if (JMSAlarmMessage.TEXT_CONFIG.equals(text)) {
        final String name = message.getString(JMSLogMessage.NAME);
        model.readConfig(name);
      }
      // Debug trigger
      else if (JMSAlarmMessage.TEXT_DEBUG.equals(text)) model.dump();

      if (action == null) return;
      // Queue or dispatch?
      synchronized (queue) {
        if (use_queue) {
          queue.execute(action);
          return;
        }
      }
      // else: Not using queue, and queue no longer locked
      action.run();
    } catch (Throwable ex) {
      Activator.getLogger().log(Level.SEVERE, "Message handler error", ex);
    }
  }
}
 /**
  * Initialize communicator that writes to the 'client' topic and listens to 'server' topic
  *
  * @param model Model for which to communicate
  * @throws Exception on error
  */
 public AlarmClientCommunicator(final AlarmClientModel model) throws Exception {
   super(Preferences.getJMS_URL());
   this.model = model;
   timeout_timer.start();
 }