/** * * Copyright (c) 2009-2016 Freedomotic team http://freedomotic.com * * This file is part of Freedomotic * * This Program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2, or (at your option) any later version. * * This Program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * Freedomotic; see the file COPYING. If not, see * <http://www.gnu.org/licenses/>. */ package com.freedomotic.bus; import com.freedomotic.app.Freedomotic; import com.freedomotic.app.Profiler; import com.freedomotic.util.UidGenerator; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageConsumer; import javax.jms.MessageListener; import javax.jms.ObjectMessage; import javax.jms.Queue; import javax.jms.Session; import javax.jms.TextMessage; import javax.jms.Topic; /** * {@link MessageListener} implementation (former AbstractBusConnector class) * <p> * Receives an {@link ObjectMessage} (it can be an event or a command) and sends * it to his {@link BusConsumer} * <p> * This is the bus hook for any {@link BusConsumer} that should register itself * in this listener. * * @author Freedomotic Team * * @see BusConsumer */ public class BusMessagesListener implements MessageListener { private static final Logger LOG = LoggerFactory.getLogger(BusMessagesListener.class.getName()); private BusService busService; private BusConsumer messageHandler; private Session session; // A listener can consume from multiple sources private List<MessageConsumer> consumers = new ArrayList<>(); /** * Constructor * * @param busConsumer * @param busService */ @Inject public BusMessagesListener(BusConsumer busConsumer, BusService busService) { if (busService == null) { throw new IllegalArgumentException("Bus service cannot be not null"); } this.messageHandler = busConsumer; this.busService = busService; if (busService == null) { throw new IllegalStateException("A message listener must have a working bus link"); } if (busConsumer == null) { throw new IllegalStateException("A message listener must have an attached consumer"); } try { this.session = busService.createSession(); } catch (Exception ex) { LOG.error(Freedomotic.getStackTraceInfo(ex)); } } /** * Passes a message to the listener * * @param message */ @Override public final void onMessage(Message message) { Profiler.incrementReceivedEvents(); if (message instanceof ObjectMessage) { final ObjectMessage objectMessage = (ObjectMessage) message; messageHandler.onMessage(objectMessage); } else { LOG.error("Message received by " + this.getClass().getSimpleName() + " is not an object message, is a " + message.getClass().getCanonicalName()); if (message instanceof TextMessage) { TextMessage text = (TextMessage) message; try { LOG.error(text.getText()); } catch (JMSException ex) { LOG.error("Error while receiving a text message", ex); } } } } /** * Registers on a command queue * * @param queueName Queue name */ public void consumeCommandFrom(String queueName) { try { Queue queue = busService.getReceiveSession().createQueue(queueName); MessageConsumer consumer = session.createConsumer(queue); consumers.add(consumer); consumer.setMessageListener(this); } catch (JMSException e) { LOG.error(Freedomotic.getStackTraceInfo(e)); } } /** * Registers on a event topic. It is a Virtual Topic in activemq lingo * * @param topicName */ public void consumeEventFrom(String topicName) { try { final String virtualTopicName = "Consumer." + UidGenerator.getNextStringUid() + ".VirtualTopic." + topicName; Queue queue = busService.getReceiveSession().createQueue(virtualTopicName); MessageConsumer consumer = session.createConsumer(queue); consumers.add(consumer); consumer.setMessageListener(this); } catch (JMSException e) { LOG.error(Freedomotic.getStackTraceInfo(e)); } } /** * Subscribes a messaging topic. The message will be received by ALL the * subscribers. It's not a virtual topic as in consumeEventFrom(). DO NOT * USE IT IF YOU ARE NOT AWARE OF THE CONSEQUENCES. USE consumeEventFrom() * instead. * * @param topicName */ public void subscribeCrossInstanceEvents(String topicName) { try { Topic topic = busService.getReceiveSession().createTopic("VirtualTopic." + topicName); //TODO: add a selector for provenance field which should be "not from current instance" MessageConsumer consumer = session.createConsumer(topic); consumers.add(consumer); consumer.setMessageListener(this); } catch (JMSException e) { LOG.error(Freedomotic.getStackTraceInfo(e)); } } /** * Unsubscribes from all messaging channels (topics and queues) * <br> * (invocations should be life cycle managed) */ public void destroy() { try { Iterator it = consumers.iterator(); while (it.hasNext()) { MessageConsumer consumer = (MessageConsumer) it.next(); LOG.info("Closing bus connection for {}", messageHandler.getClass().getSimpleName()); consumer.close(); it.remove(); } consumers.clear(); session.close(); } catch (JMSException ex) { LOG.error(ex.getMessage()); } } }