/******************************************************************************* * ALMA - Atacama Large Millimeter Array * Copyright (c) COSYLAB - Control System Laboratory, 2011 * (in the framework of the ALMA collaboration). * All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *******************************************************************************/ /* * Created on Jun 27, 2004 * * To change the template for this generated file go to * Window>Preferences>Java>Code Generation>Code and Comments */ package com.cosylab.acs.jms; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.Topic; import javax.jms.TopicPublisher; import alma.acs.container.ContainerServicesBase; import alma.acs.exceptions.AcsJException; import alma.acs.logging.AcsLogLevel; import alma.acs.logging.AcsLogger; import alma.acs.nc.AcsEventPublisher; import alma.acscommon.ACS_NC_DOMAIN_ALARMSYSTEM; /** * To avoid creating an <code>AcsEventPublisher</code> for each message to publish, * an pool of <code>AcsEventPublisher</code> has been introduced by means * of a <code>HashMap</code>. * The key of the <code>HasMap</code> is the name of the NC. * To make the publisher reusable for all the topics, the <code>HashMap</code> * is static. * * The <code>DefaultPublisherImpl</code> instantiates an object of this class and * publishes messages passing the topic i.e. it never calls the method without topic. * In particular it calls: * - <code>publish(Topic,Message)</code> for Heartbeat, Category, clients * - <code>publish(Topic topic, Message message, int deliveryMode, int priority, long timeToLive)</code> * for Sources * * For our point of view the two methods have the same functioning because we * do not use the deliveryMode, priority and timeToLive at least for now ;-) * * @see {@link cern.cmw.mom.pubsub.impl.DefaultPublisherImpl} * * @author kzagar * */ public class ACSJMSTopicPublisher extends ACSJMSProducer implements TopicPublisher { /** * Objects from this class associate a time to the <code>AcsEventPublisher</code> * in order to remember when the NC has been accessed the last time. * In this way it is possible to implement a policy to free not used NC. * * @author acaproni * */ private class PublisherPoolItem { private final String ncName; /** * The publisher */ private AcsEventPublisher<ACSJMSMessageEntity> publisher; /** * The last time the publisher has been accessed */ private long lastAccessTime; /** * Constructor * * Create a </code>PoolMenuItem</code> building a new * <code>AcsEventPublisher</code> and updating the access time * * @param name The name of the topic (i.e. of the NC) * @throws AcsJException In case of error building the <code>AcsEventPublisher</code> */ public PublisherPoolItem(String name, ContainerServicesBase contSvcs) throws AcsJException { ncName = name; publisher = contSvcs.createNotificationChannelPublisher(ncName, ACS_NC_DOMAIN_ALARMSYSTEM.value, ACSJMSMessageEntity.class); lastAccessTime=System.currentTimeMillis(); } /** * Publish a message on this publisher * @param message */ public void sendMessage(Message message) throws JMSException { if (publisher==null) { throw new IllegalStateException("Trying to publish on a null publisher"); } if(message instanceof ACSJMSMessage) { ((ACSJMSMessage)message).getEntity().type = message.getClass().getName(); try { publisher.publishEvent(((ACSJMSMessage)message).getEntity()); } catch (AcsJException ex) { JMSException ex2 = new JMSException("Failed to publish on NC " + ncName); ex2.setLinkedException(ex); throw ex2; } lastAccessTime=System.currentTimeMillis(); } else { throw new IllegalArgumentException("ACSJMSProducer can only send ACSJMSMessage messages!"); } } /** * Release the publisher * * @see {@link AcsEventPublisher} */ public void close() { if (publisher==null) { throw new IllegalStateException("Trying to close a null publisher"); } try { publisher.disconnect(); } catch (Throwable t) { // Nothing to do... bu we log a warning logger.log(AcsLogLevel.WARNING, "Ingnored error disconnecting the channel "+ncName+": "+t.getMessage()); } publisher=null; } /** * Return the last access time for this item. * * The value returned is the number of milliseconds * as returned by <code>System.currentTimeMillis()</code> * * @return The instant (msec) when this item has been accessed the last time */ public long getLastAccessTime() { return lastAccessTime; } } /** * The name of the topic passed in the constructor */ private String topicName; /** The publishers * The key is the name of the NC (i.e. the name of the topic) * The value is the PublisherPoolItem */ private static HashMap<String, PublisherPoolItem> publishersPool = new HashMap<String, PublisherPoolItem>(); /** * The logger */ private final AcsLogger logger; /** * Constructor * * @param topic The topic to publish messages into * @param containerServices The container service * @throws JMSException */ public ACSJMSTopicPublisher(Topic topic, ContainerServicesBase containerServices) throws JMSException { super(topic, containerServices); logger=containerServices.getLogger(); if(topic != null) { if (topic.getTopicName()==null || topic.getTopicName().isEmpty()) { throw new IllegalArgumentException("Invalid topic name"); } topicName=topic.getTopicName(); synchronized(publishersPool) { if (publishersPool.containsKey(topic.getTopicName())) { return; } } try { PublisherPoolItem item = new PublisherPoolItem(topic.getTopicName(), containerServices); synchronized(publishersPool) { publishersPool.put(topic.getTopicName(),item); } } catch (Exception e) { e.printStackTrace(System.err); throw new JMSException("Exception building a PublisherPoolItem "+e.getMessage()); } } } /* (non-Javadoc) * @see javax.jms.TopicPublisher#getTopic() */ public Topic getTopic() throws JMSException { return (Topic)this.destination; } /** * Publish a message in the topic * * The topic is the topic whose name has been passed in the constructor. * However for this implementation there is no difference when the topic * has been built because all the topics are in the pool. * The name of the topic built in the constructor can be accessed through * the <code>topicName</code> field. * * @see javax.jms.TopicPublisher#publish(javax.jms.Message) */ public void publish(Message message) throws JMSException { if (message==null) { throw new IllegalArgumentException("The message can't be null"); } PublisherPoolItem item; synchronized (publishersPool) { item = publishersPool.get(topicName); } if (item==null) { throw new IllegalStateException("Impossible to use this method without building the publisher passing valid topic"); } item.sendMessage(message); } /* (non-Javadoc) * @see javax.jms.TopicPublisher#publish(javax.jms.Message, int, int, long) */ public void publish(Message message, int deliveryMode, int priority, long timeToLive) throws JMSException { message.setJMSDeliveryMode(deliveryMode); message.setJMSPriority(priority); message.setJMSExpiration(timeToLive + System.currentTimeMillis()); publish(message); } /* (non-Javadoc) * @see javax.jms.TopicPublisher#publish(javax.jms.Topic, javax.jms.Message) */ public void publish(Topic topic, Message message) throws JMSException { if (message==null) { throw new NullPointerException("The message can't be null"); } if (topic==null) { throw new NullPointerException("The topic can't be null"); } if (topic.getTopicName()==null || topic.getTopicName().isEmpty()) { throw new IllegalArgumentException("Invalid topic name"); } PublisherPoolItem item; synchronized (publishersPool) { item= publishersPool.get(topic.getTopicName()); } if (item==null) { try { item = new PublisherPoolItem(topic.getTopicName(),containerServices); } catch (Exception e) { throw new JMSException("Error creating an AcsEventPublisher"); } synchronized (publishersPool) { publishersPool.put(topic.getTopicName(), item); } } item.sendMessage(message); } /* (non-Javadoc) * @see javax.jms.TopicPublisher#publish(javax.jms.Topic, javax.jms.Message, int, int, long) */ public void publish(Topic topic, Message message, int deliveryMode, int priority, long timeToLive) throws JMSException { message.setJMSDeliveryMode(deliveryMode); message.setJMSPriority(priority); message.setJMSExpiration(timeToLive + System.currentTimeMillis()); publish(topic,message); } /* (non-Javadoc) * @see javax.jms.MessageProducer#close() */ public void close() throws JMSException { Collection<PublisherPoolItem> values; Iterator<PublisherPoolItem> iter; synchronized (publishersPool) { values= publishersPool.values(); iter = values.iterator(); while (iter.hasNext()) { PublisherPoolItem item = iter.next(); item.close(); iter.remove(); } } } /* (non-Javadoc) * @see javax.jms.MessageProducer#send(javax.jms.Message) */ public void send(Message message) throws JMSException { publish(message); } }