/*******************************************************************************
* Copyright (c) 2004, 2007 IBM Corporation and Cambridge Semantics Incorporated.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* File: $Source: /cvsroot/slrp/boca/com.ibm.adtech.boca.notification.web/JavaSource/com/ibm/adtech/boca/notification/web/NotificationServer.java,v $
* Created by: Matthew Roy ( <a href="mailto:mroy@us.ibm.com">mroy@us.ibm.com </a>)
* Created on: 3/22/2006
* Revision: $Id: NotificationServer.java 163 2007-07-31 14:11:08Z mroy $
*
* Contributors:
* IBM Corporation - initial API and implementation
* Cambridge Semantics Incorporated - Fork to Anzo
*******************************************************************************/
package org.openanzo.combus.endpoint.internal;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.HashSet;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.ExceptionListener;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.Queue;
import javax.jms.Session;
import org.apache.activemq.broker.BrokerStoppedException;
import org.apache.activemq.transport.TransportDisposedIOException;
import org.openanzo.analysis.RequestRecorder;
import org.openanzo.combus.IJmsProvider;
import org.openanzo.combus.endpoint.ICombusEndpointListener;
import org.openanzo.exceptions.AnzoException;
import org.openanzo.exceptions.LogUtils;
import org.openanzo.services.ServicesDictionary;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* NotificationServer connects to JMS server to handle update messages from model server, and sends updates to notification clients
*
* @author Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com </a>)
*
*/
class CombusEndpoint {
private static final Logger log = LoggerFactory.getLogger(CombusEndpoint.class);
/** End-point Listeners */
private HashSet<ICombusEndpointListener> endpointListeners = null;
/** CombusListener listeners */
private ArrayList<CombusListener> combusListeners = null;
/** JMS connection */
private Connection connection = null;
/** Retries to connect to JMS server */
private int retries = 0;
/** IJmsProvider */
private final IJmsProvider jmsProvider;
// private ThreadGroup threadGroup = null;
private final Dictionary<? extends Object, ? extends Object> configProperties;
private RequestRecorder recorder;
/**
* Create new NotificationServer
*
*/
protected CombusEndpoint(IJmsProvider jmsProvider, Dictionary<? extends Object, ? extends Object> configProperties, RequestRecorder recorder) {
this.configProperties = configProperties;
this.jmsProvider = jmsProvider;
this.recorder = recorder;
}
protected void setRecorder(RequestRecorder recorder) {
this.recorder = recorder;
}
protected void start() throws AnzoException {
combusListeners = new ArrayList<CombusListener>();
// this.threadGroup = new ThreadGroup("ComBusThreads");
endpointListeners = new HashSet<ICombusEndpointListener>();
Thread connectThread = new Thread("CombusEndpointConnect") {
@Override
public void run() {
try {
connect();
} catch (AnzoException ae) {
log.error(LogUtils.COMBUS_MARKER, "Error in connect:", ae);
}
}
};
connectThread.setDaemon(true);
connectThread.start();
}
protected void stop() throws AnzoException {
disconnect(true);
}
void connect() throws AnzoException {
if (retries > 5)
return;
String user = ServicesDictionary.getUser(configProperties, null);
String password = ServicesDictionary.getPassword(configProperties, null);
try {
ConnectionFactory factory = jmsProvider.createConnectionFactory(configProperties);
connection = factory.createConnection(user, password);
connection.start();
connection.setExceptionListener(new ExceptionListener() {
public void onException(JMSException exception) {
if (exception.getCause() instanceof BrokerStoppedException || exception.getCause() instanceof TransportDisposedIOException) {
disconnect(false);
} else {
disconnect(true);
log.error(LogUtils.COMBUS_MARKER, "Closing combus endpoint connection due to jms exception", exception);
try {
connect();
} catch (AnzoException ae) {
log.error(LogUtils.COMBUS_MARKER, "Error in connect:", ae);
}
}
}
});
synchronized (endpointListeners) {
for (ICombusEndpointListener listener : endpointListeners) {
setupConsumer(listener);
}
}
retries = 0;
} catch (JMSException jmsex) {
log.error(LogUtils.COMBUS_MARKER, "Error connecting combus endpoint connection", jmsex);
retries++;
try {
Thread.sleep(5000);
} catch (InterruptedException ie) {
}
connect();
}
}
private void setupConsumer(ICombusEndpointListener listener) throws JMSException {
if (recorder != null) {
listener.setRecorder(recorder);
}
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue(listener.getQueueName());
MessageConsumer consumer = session.createConsumer(queue, "JMSPriority >= 4");
MessageConsumer lowConsumer = session.createConsumer(queue, "JMSPriority < 4");
CombusListener combusListener = new CombusListener(session, listener, consumer, lowConsumer);
combusListeners.add(combusListener);
listener.setConsumer(consumer, lowConsumer);
log.info(LogUtils.COMBUS_MARKER, "Created listener on queue:{} ", listener.getQueueName());
}
synchronized void disconnect(boolean clean) {
if (connection != null) {
try {
for (CombusListener listener : combusListeners) {
listener.close(clean);
}
combusListeners.clear();
} finally {
try {
connection.close();
} catch (JMSException jmsex) {
if (clean) {
log.warn(LogUtils.COMBUS_MARKER, "Error disconnecting combus endpoint connection", jmsex);
} else {
log.debug(LogUtils.COMBUS_MARKER, "Error disconnecting combus endpoint connection", jmsex);
}
} finally {
connection = null;
}
}
}
}
protected void registerListener(ICombusEndpointListener listener) {
synchronized (endpointListeners) {
endpointListeners.add(listener);
if (connection != null) {
try {
setupConsumer(listener);
} catch (JMSException jmsex) {
log.error(LogUtils.SERVER_INTERNAL_MARKER, "Error registering combus endpoint listener", jmsex);
}
}
}
}
protected void unregisterListener(ICombusEndpointListener listener) {
synchronized (endpointListeners) {
endpointListeners.remove(listener);
if (connection != null) {
try {
listener.stop();
} catch (AnzoException jmsex) {
log.error(LogUtils.SERVER_INTERNAL_MARKER, "Error unregistering combus endpoint listener", jmsex);
}
}
}
}
static class CombusListener {
private final Session session;
private final MessageConsumer[] messageConsumers;
private final ICombusEndpointListener messageListener;
CombusListener(Session session, ICombusEndpointListener messageListener, MessageConsumer... messageConsumers) {
this.session = session;
this.messageConsumers = messageConsumers;
this.messageListener = messageListener;
this.messageListener.setSession(session);
}
void close(boolean clean) {
try {
messageListener.stop();
} catch (AnzoException ae) {
log.warn(LogUtils.COMBUS_MARKER, "Error stopping combus endpoint listener", ae);
}
if (clean) {
for (MessageConsumer messageConsumer : messageConsumers) {
try {
messageConsumer.close();
} catch (NullPointerException npe) {
//Catch exception due to defect within activemq's ActiveMQMessageConsumer.dispose() method
} catch (JMSException jmsex) {
log.warn(LogUtils.COMBUS_MARKER, "Error closing combus endpoint listener consumer", jmsex);
}
}
try {
session.close();
} catch (JMSException jmsex) {
log.warn(LogUtils.COMBUS_MARKER, "Error closing combus endpoint listener session", jmsex);
}
}
}
}
}