/* (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.impl; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.Properties; import java.util.logging.Level; import javax.jms.ConnectionFactory; import javax.jms.Destination; import javax.jms.Topic; import org.apache.activemq.broker.BrokerService; import org.apache.activemq.pool.PooledConnectionFactory; import org.geoserver.cluster.JMSFactory; import org.geoserver.cluster.configuration.BrokerConfiguration; import org.geoserver.cluster.configuration.ConnectionConfiguration; import org.geoserver.cluster.configuration.ConnectionConfiguration.ConnectionConfigurationStatus; import org.geoserver.cluster.configuration.EmbeddedBrokerConfiguration; import org.geoserver.cluster.configuration.JMSConfiguration; import org.geoserver.cluster.configuration.TopicConfiguration; import org.geotools.util.logging.Logging; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; /** * * @author Carlo Cancellieri - carlo.cancellieri@geo-solutions.it * */ public class JMSActiveMQFactory extends JMSFactory implements ApplicationListener<ContextRefreshedEvent>, ApplicationContextAware { private final static java.util.logging.Logger LOGGER = Logging .getLogger(JMSActiveMQFactory.class); @Autowired private JMSConfiguration config; @Autowired private JMSXBeanBrokerFactory bf; // used to track changes to the configuration private String brokerURI; private String topicName; private PooledConnectionFactory cf; private Topic topic; // times to test (connection) private static int max; // millisecs to wait between tests (connection) private static long maxWait; // embedded brokerURI private BrokerService brokerService; private String brokerName; private ApplicationContext applicationContext; // <bean id="JMSClientDestination" // class="org.apache.activemq.command.ActiveMQQueue"> // <value="Consumer.${instance.name}.VirtualTopic.${topic.name}" /> // </bean> @Override public Destination getClientDestination(Properties configuration) { StringBuilder builder = new StringBuilder("Consumer."); String instanceName = configuration .getProperty(JMSConfiguration.INSTANCE_NAME_KEY); String topicName = configuration .getProperty(TopicConfiguration.TOPIC_NAME_KEY); return new org.apache.activemq.command.ActiveMQQueue(builder .append(instanceName).append(".").append(topicName).toString()); } // <!-- DESTINATION --> // <!-- A Destination in ActiveMQ --> // <bean id="JMSServerDestination" // class="org.apache.activemq.command.ActiveMQTopic"> // <!-- <constructor-arg value="VirtualTopic.${topic.name}" /> --> // <constructor-arg value="VirtualTopic.>" /> // </bean> @Override public Topic getTopic(Properties configuration) { // TODO move me to implementation jmsFactory implementation // if topicName is changed final String topicConfiguredName = configuration .getProperty(TopicConfiguration.TOPIC_NAME_KEY); if (topic == null || topicName.equals(topicConfiguredName)) { topicName = topicConfiguredName; topic = new org.apache.activemq.command.ActiveMQTopic( configuration .getProperty(TopicConfiguration.TOPIC_NAME_KEY)); } return topic; } // <!-- A connection to ActiveMQ --> @Override public ConnectionFactory getConnectionFactory(Properties configuration) { final String _brokerURI = config .getConfiguration(BrokerConfiguration.BROKER_URL_KEY); final boolean changed = checkBrokerURI(_brokerURI); if (LOGGER.isLoggable(Level.INFO)) { LOGGER.info("Using brokerURI: " + brokerURI); } if (cf == null) { // need to be initialized cf = new PooledConnectionFactory(brokerURI); } else { if (changed) { // clear pending connections try { destroyConnectionFactory(); } catch (Exception e) { // eat } // create a new connection cf = new PooledConnectionFactory(brokerURI); // cf.start(); } } return cf; } /** * @return true if brokerURI is modified */ private boolean checkBrokerURI(final String _brokerURI) { if (brokerURI == null) { if (_brokerURI == null || _brokerURI.length() == 0) { brokerURI = getDefaultURI(); return true; } else { // initialize to the new configuration brokerURI = _brokerURI; // check the URI syntax try { new URI(brokerURI); } catch (URISyntaxException e) { if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.severe(e.getLocalizedMessage()); } brokerURI = getDefaultURI(); } return true; } } else { if (_brokerURI == null || _brokerURI.length() == 0) { // use the default return checkBrokerURI(getDefaultURI()); } else if (brokerURI.equalsIgnoreCase(_brokerURI)) { return false; } else { // something is changed: reset brokerURI = null; return checkBrokerURI(_brokerURI); } } } private String getDefaultURI() { // default brokerURI return "vm://" + config.getConfiguration(JMSConfiguration.INSTANCE_NAME_KEY) + "?create=false&waitForStart=5000"; } private void destroyConnectionFactory(){ if (cf != null) { // close all the connections cf.clear(); // stop the factory cf.stop(); // set null cf = null; } } private void destroyBrokerService() throws Exception{ if (brokerService != null) { brokerService.stop(); } } @Override public void destroy() throws Exception { destroyConnectionFactory(); destroyBrokerService(); } @Override public boolean startEmbeddedBroker(final Properties configuration) throws Exception { if (LOGGER.isLoggable(Level.INFO)) { LOGGER.info("Starting the embedded brokerURI"); } if (brokerService == null) { final String xBeanBroker = configuration .getProperty(ActiveMQEmbeddedBrokerConfiguration.BROKER_URL_KEY); // final XBeanBrokerFactory bf = new XBeanBrokerFactory(); brokerService = bf.createBroker(new URI(xBeanBroker)); brokerService.setEnableStatistics(false); // override the name of the brokerURI using the instance name which // should be unique within the network brokerName = configuration.getProperty(JMSConfiguration.INSTANCE_NAME_KEY); brokerService.setBrokerName(brokerName); brokerService.setUseLocalHostBrokerName(false); brokerService.setVmConnectorURI(new URI("vm://"+brokerName)); } else { if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.warning("The embedded brokerURI service already exists, probably it is already started"); } if (brokerService.isStarted()) { if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.warning("SKIPPING: The embedded brokerURI is already started"); } return true; } } if (!brokerService.isStarted()) { brokerService.start(); } for (int i = -1; i < max; ++i) { try { if (brokerService.isStarted()) { if (LOGGER.isLoggable(Level.INFO)) { LOGGER.info("Embedded brokerURI is now started"); } return true; } Thread.sleep(maxWait); } catch (Exception e1) { if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.severe("Unable to start the embedded brokerURI" + e1.getLocalizedMessage()); } return false; } } if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.severe("Unable to start the embedded brokerURI"); } return false; } @Override public boolean isEmbeddedBrokerStarted() { return brokerService == null ? false : brokerService.isStarted(); } @Override public boolean stopEmbeddedBroker() throws Exception { if (LOGGER.isLoggable(Level.INFO)) { LOGGER.info("Embedded brokerURI is now stopped"); } if (brokerService == null) { return true; } brokerService.stop(); for (int i = -1; i < max; ++i) { try { if (!brokerService.isStarted()) { if (LOGGER.isLoggable(Level.INFO)) { LOGGER.info("Embedded brokerURI is now stopped"); } brokerService = null; return true; } if (LOGGER.isLoggable(Level.INFO)) { LOGGER.info("Embedded brokerURI is going to stop: waiting..."); } Thread.sleep(maxWait); } catch (Exception e1) { if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.severe("Unable to start the embedded brokerURI" + e1.getLocalizedMessage()); } return false; } } if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.severe("Unable to stop the embedded brokerURI"); } return false; } private void init() { // // times to test (connection) max = Integer.parseInt(config.getConfiguration( ConnectionConfiguration.CONNECTION_RETRY_KEY).toString()); // millisecs to wait between tests (connection) maxWait = Long.parseLong(config.getConfiguration( ConnectionConfiguration.CONNECTION_MAXWAIT_KEY).toString()); // check configuration for connection and try to start if needed if (EmbeddedBrokerConfiguration.isEnabled(config)) { if (!isEmbeddedBrokerStarted()) { try { if (!startEmbeddedBroker(config.getConfigurations())) { if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.severe("Unable to start the embedded brokerURI, force status to disabled"); } // change configuration status config.putConfiguration( ConnectionConfiguration.CONNECTION_KEY, ConnectionConfigurationStatus.disabled .toString()); } else { if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.info("Started the embedded brokerURI: " + brokerService.toString()); } } } catch (Exception e) { LOGGER.log(Level.SEVERE, e.getMessage(), e); // change configuration status config.putConfiguration( ConnectionConfiguration.CONNECTION_KEY, ConnectionConfigurationStatus.disabled.toString()); } // store changes to the configuration try { config.storeConfig(); } catch (IOException e) { LOGGER.log(Level.SEVERE, e.getMessage(), e); } } else { if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.warning("The brokerURI seems to be already started"); } } } } @Override public void onApplicationEvent(ContextRefreshedEvent event) { if (event.getApplicationContext() == applicationContext) { init(); } } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }