package cern.cmw.mom.pubsub.impl; import cern.cmw.mom.pubsub.MOMException; import cern.cmw.mom.pubsub.NotificationHelper; import cern.cmw.mom.pubsub.Subscriber; import cern.cmw.mom.pubsub.SubscriptionListener; import cern.cmw.mom.util.MomConfig; import cern.cmw.mom.util.TopicAdminHelper; import org.apache.log4j.Category; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Properties; import javax.jms.DeliveryMode; import javax.jms.ExceptionListener; import javax.jms.JMSException; import javax.jms.JMSSecurityException; import javax.jms.Message; import javax.jms.Topic; import javax.jms.TopicPublisher; import javax.jms.TopicSession; import javax.jms.TopicSubscriber; import javax.naming.NamingException; /** * Implementation class. * @version 1.0 23 Jan 2001 * @author Controls Middleware Project * @see Subscriber */ public class DefaultSubscriberImpl implements Subscriber, ExceptionListener { static Category cat = Category.getInstance(DefaultSubscriberImpl.class.getName()); private Boolean loadBalancing; private Boolean selectorAtBroker; private Boolean sequential; private cern.cmw.mom.pubsub.ExceptionListener listener = null; private HashMap subscribers = null; private JMSTopicConnection connection = null; private Map topicDirectory = null; private Message notificationMessage = null; private Properties momProperties = null; private String brokerList; private String password; private String subscriberId = null; private String username; private Thread keepAliveThread = null; private TopicPublisher notificationPublisher = null; private TopicSession serviceSession = null; private boolean closed = true; private boolean keepAliveEnabled = false; private boolean notificationsEnabled = false; private int keepAliveInterval = 0; /** * Constructor DefaultSubscriberImpl * * * @throws MOMException * */ public DefaultSubscriberImpl() throws MOMException { this.username = null; this.password = null; this.brokerList = null; this.loadBalancing = null; this.selectorAtBroker = null; this.sequential = null; open(); } /** * Constructor DefaultSubscriberImpl * * * @throws MOMException * */ public DefaultSubscriberImpl(String username, String password, String brokerList, Boolean loadBalancing, Boolean sequential, Boolean selectorAtBroker) throws MOMException { this.username = username; this.password = password; this.brokerList = brokerList; this.loadBalancing = loadBalancing; this.selectorAtBroker = selectorAtBroker; this.sequential = sequential; open(); } /** * Method isClosed * * @return boolean true iff the subscriber instance has been closed */ public boolean isClosed() { return closed; } /** * Method setExceptionListener * * @param listener */ public void setExceptionListener(cern.cmw.mom.pubsub.ExceptionListener listener) { this.listener = listener; } /** * Method close * * */ public void close() { cat.debug("close()"); synchronized (subscribers) { SubscriptionHandle handle = null; keepAliveEnabled = false; try { Iterator iterator = subscribers.values().iterator(); while (iterator.hasNext()) { handle = (SubscriptionHandle) iterator.next(); if (!NotificationHelper.isNotification(handle.getSubscriptionTopic())) { publishNotification(NotificationHelper.CONSUMER_CLOSE_NOTIFICATION, handle); } handle.getSubscriber().close(); handle.getSession().close(); } subscribers.clear(); if (notificationPublisher != null) { notificationPublisher.close(); notificationPublisher = null; } if (serviceSession != null) { serviceSession.close(); serviceSession = null; } if (topicDirectory != null) { topicDirectory.clear(); topicDirectory = null; } } catch (JMSException e) { cat.error("Exception raised closing a Subscriber : " + e.getMessage()); } if (connection != null) { try { connection.stop(); } catch (ConnectionException ce) { ce.printStackTrace(); } connection.close(); connection = null; } closed = true; } cat.debug("closed."); } /** * Method onException * * * @param ex * */ public void onException(JMSException ex) { cat.warn("onException(): " + ex); /*if (progress.message.jclient.ErrorCodes.testException(ex, progress.message.jclient.ErrorCodes.ERR_CONNECTION_DROPPED)) { cat.error("ERR_CONNECTION_DROPPED detected."); if (!isClosed()) { if (listener != null) { listener.onException(new MOMException("ERR_CONNECTION_DROPPED detected.", MOMException.CONNECTION_LOST_EXCEPTION)); } connection.disconnect(); try { initialize(true); recoverSubscriptions(); if (listener != null) { listener.onException(new MOMException("Connection reestabilished", MOMException.CONNECTION_RECOVERED_EXCEPTION)); } } catch (MOMException e) { cat.error("unable to recover", e); } } }*/ } /** * Method open * * @throws MOMException */ public void open() throws MOMException { cat.debug("open()"); // initialize the subscriber initialize(false); // set the subscriber identifier setConnectionIdentifier(); // subscribers vector is initialised subscribers = new HashMap(); // keep alive thread is started iff keepAliveInterval>0 if (notificationsEnabled && (keepAliveInterval > 0)) { keepAliveEnabled = true; keepAliveThread = createKeepAliveThread(); keepAliveThread.setPriority(Thread.MIN_PRIORITY); keepAliveThread.setDaemon(true); keepAliveThread.start(); } closed = false; } /** * Method subscribe * * * @param topic * @param listener * @param selector * * @return long the subscription handle identifier * * @throws JMSException * @throws NamingException * */ public long subscribe(String topic, SubscriptionListener listener, String selector) throws JMSException, NamingException { cat.info("subscribe(" + topic + ", listener, " + selector + ")"); if (closed) { throw (new JMSException("Subscriber closed.")); } SubscriptionHandle handle = new SubscriptionHandle(); handle.setSubscriptionTopic(topic); handle.setSubscriptionSelector(selector); handle.setSubscriptionListener(listener); StringBuffer local_selector = new StringBuffer(); if (NotificationHelper.isNotification(topic)) { // this is a subscription to notifications, no further selection is required local_selector.append(selector); } else { // subscription to a generic topic, adding subscriber specific selection if (selector != null) { local_selector.append(selector); local_selector.append(" AND "); } local_selector.append("( ("); local_selector.append(NotificationHelper.SUBSCRIPTION_ID_PROPERTY); local_selector.append(" IS NULL) OR ("); local_selector.append(NotificationHelper.SUBSCRIPTION_ID_PROPERTY); local_selector.append(" = '"); local_selector.append(subscriberId); local_selector.append("@"); local_selector.append(handle.getSubscriptionToken()); local_selector.append("') )"); } TopicSession session = null; TopicSubscriber subscriber = null; Topic the_topic = createTopic(topic); try { session = connection.createTopicSession(); subscriber = session.createSubscriber(the_topic, local_selector.toString(), false); } catch (JMSSecurityException jse) { cat.error("JMSSecurityException caught"); throw (new NamingException(jse.getMessage())); } catch (JMSException je) { cat.error("JMSException caught"); throw (new NamingException(je.getMessage())); } catch (ConnectionException ce) { cat.error("ConnectionException caught"); throw (new JMSException(ce.getMessage())); } catch (Exception e) { cat.error("Generic exception caught", e); } subscriber.setMessageListener(listener); handle.setSubscriber(subscriber); handle.setSession(session); synchronized (subscribers) { subscribers.put(new Long(handle.getSubscriptionToken()), handle); } if (!NotificationHelper.isNotification(topic)) { publishNotification(NotificationHelper.CONSUMER_OPEN_NOTIFICATION, handle); } return handle.getSubscriptionToken(); } /** * Method unSubscribe * * * @param token * * @throws JMSException * */ public void unSubscribe(long token) throws JMSException { cat.info("unSubscribe(" + token + ")"); if (closed) { throw (new JMSException("Subscriber closed.")); } synchronized (subscribers) { Long key = new Long(token); SubscriptionHandle handle = (SubscriptionHandle) subscribers.get(key); if (handle != null) { handle.getSubscriber().close(); handle.getSession().close(); if (!NotificationHelper.isNotification(handle.getSubscriptionTopic())) { publishNotification(NotificationHelper.CONSUMER_CLOSE_NOTIFICATION, handle); } subscribers.remove(key); } } } /** * Close all the opened subscriptions. * @exception JMSException if JMS fails to unsubscribe due to some internal JMS error. */ public void unSubscribeAll() throws JMSException { cat.info("unSubscribeAll()"); System.out.println("### DefaultSubscriberImpl::unSubscribeAll"); if (closed) { throw (new JMSException("Subscriber closed.")); } synchronized (subscribers) { SubscriptionHandle handle = null; Iterator iterator = subscribers.values().iterator(); while (iterator.hasNext()) { handle = (SubscriptionHandle) iterator.next(); if (!NotificationHelper.isNotification(handle.getSubscriptionTopic())) { publishNotification(NotificationHelper.CONSUMER_CLOSE_NOTIFICATION, handle); } handle.getSubscriber().close(); handle.getSession().close(); } subscribers.clear(); } } /** * Method createTopic * * @param topic * @return Topic the topic * @throws JMSException * @throws NamingException */ protected Topic createTopic(String topic) throws JMSException, NamingException { cat.debug("createTopic(" + topic + ")"); Topic the_topic = null; if (NotificationHelper.isNotification(topic) || TopicAdminHelper.validateTopic(topic,connection.getUsername(),TopicAdminHelper.FOR_SUBSCRIBING)) { the_topic = (Topic) topicDirectory.get(topic); if (the_topic == null) { the_topic = serviceSession.createTopic(topic); topicDirectory.put(topic, the_topic); } } else { throw (new NamingException("[" + topic + "] is not a valid topic name")); } return the_topic; } /** * Method finalize * * @throws Throwable */ protected void finalize() throws Throwable { cat.debug("finalize()"); close(); super.finalize(); cat.debug(subscriberId + " finalized."); } /** * Method recoverSubscriptions * */ protected void recoverSubscriptions() { cat.info("recoverSubscriptions()"); try { SubscriptionHandle handle = null; Iterator iterator = subscribers.values().iterator(); while (iterator.hasNext()) { handle = (SubscriptionHandle) iterator.next(); if (!NotificationHelper.isNotification(handle.getSubscriptionTopic())) { publishNotification(NotificationHelper.CONSUMER_CLOSE_NOTIFICATION, handle); } cat.debug("Recovering subscription to : " + handle.getSubscriptionTopic()); TopicSession session = connection.createTopicSession(); TopicSubscriber subscriber = session.createSubscriber(createTopic(handle.getSubscriptionTopic()), handle.getSubscriptionSelector(), false); subscriber.setMessageListener(handle.getSubscriptionListener()); handle.setSession(session); handle.setSubscriber(subscriber); if (!NotificationHelper.isNotification(handle.getSubscriptionTopic())) { publishNotification(NotificationHelper.CONSUMER_OPEN_NOTIFICATION, handle); } } cat.info("Subscriptions succesfully recovered."); } catch (Exception e) { cat.error("unable to recover subscriptions", e); } } /** * Method setId * */ private void setConnectionIdentifier() { String hostname = null; try { hostname = java.net.InetAddress.getLocalHost().getHostName(); } catch (Exception e) { cat.warn("Exception raised attempting to get the hostname.", e); hostname = new String("HOSTNAME"); } subscriberId = new String(hostname + "@" + connection.getConnectId()); } /** * Method getSubscriptionHandle * * @param token * @return SubscriptionHandle the subscription handle */ private SubscriptionHandle getSubscriptionHandle(long token) { synchronized (subscribers) { return (SubscriptionHandle) subscribers.get(new Long(token)); } } /** * Method run * * * @return Thread the keep alive thread */ private Thread createKeepAliveThread() { Thread keep_alive_thread = new Thread() { public void run() { cat.info("KeepAliveThread started."); while (keepAliveEnabled) { publishKeepAliveNotifications(); try { Thread.sleep(keepAliveInterval * 1000); } catch (InterruptedException ie) { ie.printStackTrace(); } } cat.info("KeepAliveThread exited."); } }; return keep_alive_thread; } /** * Method initialize * * @param retry * @throws MOMException */ private void initialize(boolean retry) throws MOMException { cat.debug("initialize()"); momProperties = MomConfig.getProperties(this.getClass().getClassLoader()); keepAliveInterval = Integer.valueOf(momProperties.getProperty(MomConfig.KEEP_ALIVE_PROPERTY)).intValue(); notificationsEnabled = Boolean.valueOf(momProperties.getProperty(MomConfig.NOTIFICATION_PROPERTY)).booleanValue(); topicDirectory = new HashMap(); try { connection = JMSTopicConnectionFactory.createJMSTopicConnection(username, password, brokerList, loadBalancing, sequential, selectorAtBroker); connection.connect(retry); connection.setExceptionListener(this); serviceSession = connection.createTopicSession(); connection.start(); } catch (ConnectionException e) { throw new MOMException("Unable to estabilish a connection, cause is : " + e.getMessage()); } try { notificationPublisher = serviceSession.createPublisher(null); notificationPublisher.setDeliveryMode(DeliveryMode.NON_PERSISTENT); notificationPublisher.setTimeToLive(60000); notificationMessage = serviceSession.createMessage(); } catch (JMSException e) { System.out.println("Exception caught: "+e.getMessage()); e.printStackTrace(System.err); throw (new MOMException("Exception raised attempting to create the notifications publisher, cause is : " + e.getMessage())); } } /** * Method publishKeepAliveNotifications * */ private void publishKeepAliveNotifications() { cat.debug("publishKeepAliveNotifications()"); if (connection.isConnected()) { SubscriptionHandle handle = null; Collection local_subscribers = null; synchronized (subscribers) { local_subscribers = ((Map) subscribers.clone()).values(); } try { Iterator iterator = local_subscribers.iterator(); while (iterator.hasNext()) { handle = (SubscriptionHandle) iterator.next(); if (!NotificationHelper.isNotification(handle.getSubscriptionTopic())) { publishNotification(NotificationHelper.CONSUMER_ALIVE_NOTIFICATION, handle); } } } catch (JMSException je) { cat.error("unable to publish keep alive notifications", je); } } } /** * Method publishNotification * * @param type * @param handle * @throws JMSException */ private void publishNotification(int type, SubscriptionHandle handle) throws JMSException { if (notificationsEnabled) { cat.info("publishNotification(" + type + ", " + handle.getSubscriptionTopic() + ", " + handle.getSubscriptionSelector() + ")"); notificationMessage.setIntProperty(NotificationHelper.NOTIFICATION_TYPE_PROPERTY, type); notificationMessage.setStringProperty(NotificationHelper.SELECTOR_PROPERTY, handle.getSubscriptionSelector()); notificationMessage.setStringProperty(NotificationHelper.TOPIC_PROPERTY, handle.getSubscriptionTopic()); notificationMessage.setStringProperty(NotificationHelper.SUBSCRIPTION_ID_PROPERTY, subscriberId + "@" + handle.getSubscriptionToken()); Topic t = null; try { t = createTopic(NotificationHelper.CarrierTopics[type]); } catch (NamingException ne) { ne.printStackTrace(); } notificationPublisher.publish(t, notificationMessage); } } } /*--- Formatted in Sun Java Convention Style on Mon, Aug 6, '01 ---*/ /*------ Formatted by Jindent 3.23 Gold 1.02 --- http://www.jindent.de ------*/