/* (c) 2014 - 2016 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.server;
import java.util.List;
import java.util.Properties;
import javax.jms.JMSException;
import org.geoserver.catalog.impl.ModificationProxy;
import org.geoserver.cluster.JMSApplicationListener;
import org.geoserver.cluster.JMSPublisher;
import org.geoserver.cluster.impl.events.configuration.JMSEventType;
import org.geoserver.cluster.impl.events.configuration.JMSGlobalModifyEvent;
import org.geoserver.cluster.impl.events.configuration.JMSServiceModifyEvent;
import org.geoserver.cluster.impl.events.configuration.JMSSettingsModifyEvent;
import org.geoserver.cluster.impl.utils.BeanUtils;
import org.geoserver.config.ConfigurationListener;
import org.geoserver.config.GeoServer;
import org.geoserver.config.GeoServerInfo;
import org.geoserver.config.LoggingInfo;
import org.geoserver.config.ServiceInfo;
import org.geoserver.config.SettingsInfo;
import org.geotools.util.logging.Logging;
/**
* JMS MASTER (Producer) Listener used to send GeoServer JMSGeoServerConfigurationExt events over the JMS channel.
*
* @see {@link JMSApplicationListener}
*
* @author Carlo Cancellieri - carlo.cancellieri@geo-solutions.it
*
*/
public class JMSConfigurationListener extends JMSAbstractGeoServerProducer implements
ConfigurationListener {
private final GeoServer geoserver;
private final static java.util.logging.Logger LOGGER = Logging.getLogger(JMSConfigurationListener.class);
private final JMSPublisher jmsPublisher;
/**
*
* @param topicTemplate the JmsTemplate object used to send message to the topic queue
* @param geoserver
* @param props properties to attach to all the message. May contains at least the producer name which should be unique.
*/
public JMSConfigurationListener(final GeoServer geoserver, final JMSPublisher jmsPublisher) {
super();
// store GeoServer reference
this.geoserver = geoserver;
// add this as geoserver listener
this.geoserver.addListener(this);
// the publisher
this.jmsPublisher = jmsPublisher;
}
@Override
public void handleGlobalChange(GeoServerInfo global, List<String> propertyNames,
List<Object> oldValues, List<Object> newValues) {
if (LOGGER.isLoggable(java.util.logging.Level.FINE)) {
LOGGER.fine("Incoming event");
}
// skip incoming events if producer is not Enabled
if (!isEnabled()) {
if (LOGGER.isLoggable(java.util.logging.Level.FINE)) {
LOGGER.fine("skipping incoming event: context is not initted");
}
return;
}
try {
// update properties
final Properties options = getProperties();
// propagate the event
jmsPublisher.publish(getTopic(), getJmsTemplate(), options,
new JMSGlobalModifyEvent(ModificationProxy.unwrap(global), propertyNames,
oldValues, newValues));
} catch (JMSException e) {
if (LOGGER.isLoggable(java.util.logging.Level.SEVERE)) {
LOGGER.severe(e.getLocalizedMessage());
}
}
}
@Override
public void handleLoggingChange(LoggingInfo logging, List<String> propertyNames,
List<Object> oldValues, List<Object> newValues) {
if (LOGGER.isLoggable(java.util.logging.Level.FINE)) {
LOGGER.fine("Incoming event");
}
// skip incoming events if producer is not Enabled
if (!isEnabled()) {
if (LOGGER.isLoggable(java.util.logging.Level.FINE)) {
LOGGER.fine("skipping incoming event: context is not initted");
}
return;
}
try {
// update the logging event with changes
BeanUtils.smartUpdate(ModificationProxy.unwrap(logging), propertyNames, newValues);
// update properties
final Properties options = getProperties();
// propagate the event
jmsPublisher.publish(getTopic(), getJmsTemplate(), options,
logging);
} catch (Exception e) {
if (LOGGER.isLoggable(java.util.logging.Level.SEVERE)) {
LOGGER.severe(e.getLocalizedMessage());
}
}
}
@Override
public void handlePostServiceChange(ServiceInfo service) {
// this handler is invoked when a new service is added or modified
JMSServiceModifyEvent event = new JMSServiceModifyEvent(service, JMSEventType.ADDED);
handleServiceEvent(event);
}
@Override
public void handleServiceRemove(ServiceInfo service) {
// this handler is invoked when a service is removed
JMSServiceModifyEvent event = new JMSServiceModifyEvent(service, JMSEventType.REMOVED);
handleServiceEvent(event);
}
@Override
public void handleServiceChange(ServiceInfo service, List<String> propertyNames,
List<Object> oldValues, List<Object> newValues) {
// this handler is invoked when a service configuration is modified
JMSServiceModifyEvent event = new JMSServiceModifyEvent(service, propertyNames, oldValues, newValues);
handleServiceEvent(event);
}
/**
* Helper method that publish a service modified event.
*/
private void handleServiceEvent(JMSServiceModifyEvent event) {
// if possible log about the received event
if (LOGGER.isLoggable(java.util.logging.Level.FINE)) {
LOGGER.fine(String.format("Incoming service '%s' modified event of type '%s'.",
event.getSource().getId(), event.getEventType()));
}
// skip incoming events if producer is not enabled
if (!isEnabled()) {
if (LOGGER.isLoggable(java.util.logging.Level.FINE)) {
LOGGER.fine(String.format(
"Skipping incoming service '%s' modified event of type '%s' since producer is not enabled.",
event.getSource().getId(), event.getEventType()));
}
return;
}
// let's publish this event
try {
jmsPublisher.publish(getTopic(), getJmsTemplate(), getProperties(), event);
} catch (Exception exception) {
// failed to publish event
LOGGER.severe(String.format("Error publishing service '%s' modified event of type '%s': %s",
event.getSource().getId(), event.getEventType(), exception.getLocalizedMessage()));
}
}
@Override
public void handleSettingsAdded(SettingsInfo settings) {
// this handler is invoked when new settings for a certain workspace are added
SettingsInfo finalSettings = ModificationProxy.unwrap(settings);
JMSSettingsModifyEvent event = new JMSSettingsModifyEvent(finalSettings, JMSEventType.ADDED);
handleSettingsEvent(event);
}
@Override
public void handleSettingsModified(SettingsInfo settings, List<String> propertyNames,
List<Object> oldValues, List<Object> newValues) {
// this handler is invoked when new settings for a certain workspace are added
SettingsInfo finalSettings = ModificationProxy.unwrap(settings);
JMSSettingsModifyEvent event = new JMSSettingsModifyEvent(finalSettings, propertyNames, oldValues, newValues);
handleSettingsEvent(event);
}
@Override
public void handleSettingsRemoved(SettingsInfo settings) {
// this handler is invoked when new settings for a certain workspace are added
SettingsInfo finalSettings = ModificationProxy.unwrap(settings);
JMSSettingsModifyEvent event = new JMSSettingsModifyEvent(finalSettings, JMSEventType.REMOVED);
handleSettingsEvent(event);
}
@Override
public void handleSettingsPostModified(SettingsInfo settings) {
// we rely on settings modified event instead
}
/**
* Helper method that publish a settings modified event.
*/
private void handleSettingsEvent(JMSSettingsModifyEvent event) {
// if possible log about the received event
if (LOGGER.isLoggable(java.util.logging.Level.FINE)) {
LOGGER.fine(String.format("Incoming settings '%s' modified event of type '%s'.",
event.getSource().getId(), event.getEventType()));
}
// skip incoming events if producer is not enabled
if (!isEnabled()) {
if (LOGGER.isLoggable(java.util.logging.Level.FINE)) {
LOGGER.fine(String.format(
"Skipping incoming settings '%s' modified event of type '%s' since producer is not enabled.",
event.getSource().getId(), event.getEventType()));
}
return;
}
// let's publish this event
try {
jmsPublisher.publish(getTopic(), getJmsTemplate(), getProperties(), event);
} catch (Exception exception) {
// failed to publish event
LOGGER.severe(String.format("Error publishing settings '%s' modified event of type '%s': %s",
event.getSource().getId(), event.getEventType(), exception.getLocalizedMessage()));
}
}
@Override
public void handlePostLoggingChange(LoggingInfo logging) {
// send(xstream.toXML(logging), JMSConfigEventType.LOGGING_CHANGE);
}
@Override
public void handlePostGlobalChange(GeoServerInfo global) {
// no op.s
}
@Override
public void reloaded() {
// skip incoming events until context is loaded
if (!isEnabled()) {
if (LOGGER.isLoggable(java.util.logging.Level.FINE)) {
LOGGER.fine("skipping incoming event: context is not initted");
}
return;
}
// EAT EVENT
// TODO check why reloaded here? check differences from CatalogListener
// reloaded() method?
// TODO disable and re-enable the producer!!!!!
// this is potentially a problem since this listener should be the first
// called by the GeoServer.
}
}