/* (c) 2014 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.cluster.client; import java.io.Serializable; import java.util.Enumeration; import java.util.Properties; import java.util.concurrent.atomic.AtomicLong; import java.util.logging.Level; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.ObjectMessage; import javax.jms.Session; import org.geoserver.cluster.JMSApplicationListener; import org.geoserver.cluster.JMSEventHandler; import org.geoserver.cluster.JMSEventHandlerSPI; import org.geoserver.cluster.JMSManager; import org.geoserver.cluster.configuration.JMSConfiguration; import org.geoserver.cluster.events.ToggleType; import org.geotools.util.logging.Logging; import org.springframework.jms.listener.SessionAwareMessageListener; /** * JMS Client (Consumer) * * Class which leverages on commons classes to define a Topic consumer handling incoming messages using runtime loaded SPI to instantiate needed * handlers. * * @see {@link JMSManager} * * @author Carlo Cancellieri - carlo.cancellieri@geo-solutions.it * */ public class JMSQueueListener extends JMSApplicationListener implements SessionAwareMessageListener<Message> { private final static java.util.logging.Logger LOGGER = Logging .getLogger(JMSQueueListener.class); private final JMSManager jmsManager; public JMSQueueListener(final JMSManager jmsManager) { super(ToggleType.SLAVE); this.jmsManager = jmsManager; } private AtomicLong consumedEvents = new AtomicLong(); @Override public void onMessage(Message message, Session session) throws JMSException { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("Incoming message event for session: " + session.toString()); } // CHECKING LISTENER STATUS if (!isEnabled()) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("Incoming message is swallowed since this component is disabled"); } return; } // FILTERING INCOMING MESSAGE if (!message.propertyExists(JMSConfiguration.INSTANCE_NAME_KEY)) { throw new JMSException("Unable to handle incoming message, property \'" + JMSConfiguration.INSTANCE_NAME_KEY + "\' not set."); } // FILTERING INCOMING MESSAGE if (!message.propertyExists(JMSConfiguration.GROUP_KEY)) { throw new JMSException("Unable to handle incoming message, property \'" + JMSConfiguration.GROUP_KEY + "\' not set."); } // check if message comes from a master with the same name of this slave if (message.getStringProperty(JMSConfiguration.INSTANCE_NAME_KEY).equals( config.getConfiguration(JMSConfiguration.INSTANCE_NAME_KEY))) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("Incoming message discarded: source is equal to destination"); } // if so discard the message return; } // check if message comes from a different group final String group = message.getStringProperty(JMSConfiguration.GROUP_KEY); final String localGroup = config.getConfiguration(JMSConfiguration.GROUP_KEY); if (!group.equals(localGroup)) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("Incoming message discarded: incoming group-->"+group+" is different from the local one-->"+localGroup); } // if so discard the message return; } // check the property which define the SPI used (to serialize on the // server side). if (!message.propertyExists(JMSEventHandlerSPI.getKeyName())) throw new JMSException("Unable to handle incoming message, property \'" + JMSEventHandlerSPI.getKeyName() + "\' not set."); // END -> FILTERING INCOMING MESSAGE // get the name of the SPI used to serialize the message final String generatorClass = message.getStringProperty(JMSEventHandlerSPI.getKeyName()); if (generatorClass == null || generatorClass.isEmpty()) { throw new IllegalArgumentException( "Unable to handle a message without a generator class name"); } if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("Incoming message was serialized using an handler generated by: \'" + generatorClass + "\'"); } // USING INCOMING MESSAGE if (message instanceof ObjectMessage) { final ObjectMessage objMessage = (ObjectMessage) (message); final Serializable obj = objMessage.getObject(); try { // lookup the SPI handler, search is performed using the // name final JMSEventHandler<Serializable, Object> handler = jmsManager .getHandlerByClassName(generatorClass); if (handler == null) { throw new JMSException("Unable to find SPI named \'" + generatorClass + "\', be shure to load that SPI into your context."); } final Enumeration<String> keys = message.getPropertyNames(); final Properties options = new Properties(); while (keys.hasMoreElements()) { String key = keys.nextElement(); options.put(key, message.getObjectProperty(key)); } handler.setProperties(options); // try to synchronize object locally if (!handler.synchronize(handler.deserialize(obj))) { throw new JMSException("Unable to synchronize message locally.\n SPI: " + generatorClass); } } catch (Exception e) { final JMSException jmsE = new JMSException(e.getLocalizedMessage()); jmsE.initCause(e); throw jmsE; } finally { this.consumedEvents.incrementAndGet(); } } else throw new JMSException("Unrecognized message type for catalog incoming event"); } // /** // * @deprecated unused/untested // * @param message // * @throws JMSException // */ // private static void getStreamMessage(Message message) throws JMSException // { // if (message instanceof StreamMessage) { // StreamMessage streamMessage = StreamMessage.class.cast(message); // // File file; // // FILTERING incoming message // // if (!message.propertyExists(JMSEventType.FILENAME_KEY)) // // throw new JMSException( // // "Unable to handle incoming message, property \'" // // + JMSEventType.FILENAME_KEY + "\' not set."); // // FileOutputStream fos = null; // try { // file = new File(GeoserverDataDirectory // .getGeoserverDataDirectory().getCanonicalPath(), ""); // // TODO get file name // // message.getStringProperty(JMSEventType.FILENAME_KEY)); // fos = new FileOutputStream(file); // final int size = 1024; // final byte[] buf = new byte[size]; // int read = 0; // streamMessage.reset(); // while ((read = streamMessage.readBytes(buf)) != -1) { // fos.write(buf, 0, read); // fos.flush(); // } // } catch (IOException e) { // if (LOGGER.isErrorEnabled()) { // LOGGER.error(e.getLocalizedMessage(), e); // } // throw new JMSException(e.getLocalizedMessage()); // } catch (JMSException e) { // if (LOGGER.isErrorEnabled()) { // LOGGER.error(e.getLocalizedMessage(), e); // } // throw new JMSException(e.getLocalizedMessage()); // } finally { // IOUtils.closeQuietly(fos); // } // // } else // throw new JMSException( // "Unrecognized message type for catalog incoming event"); // } public long getConsumedEvents() { return consumedEvents.get(); } public void resetconsumedevents() { consumedEvents.set(0); } }