// 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(); }