Java tutorial
/* * This file is part of FFMQ. * * FFMQ 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 of the License, or * (at your option) any later version. * * FFMQ 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 FFMQ; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package net.timewalker.ffmq4.common.session; import java.io.Serializable; import java.util.ArrayList; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.jms.BytesMessage; import javax.jms.Destination; import javax.jms.IllegalStateException; import javax.jms.JMSException; import javax.jms.MapMessage; import javax.jms.Message; import javax.jms.MessageConsumer; import javax.jms.MessageListener; import javax.jms.MessageProducer; import javax.jms.ObjectMessage; import javax.jms.Queue; import javax.jms.QueueBrowser; import javax.jms.Session; import javax.jms.StreamMessage; import javax.jms.TextMessage; import javax.jms.Topic; import javax.jms.TopicSubscriber; import net.timewalker.ffmq4.FFMQException; import net.timewalker.ffmq4.common.connection.AbstractConnection; import net.timewalker.ffmq4.common.destination.QueueRef; import net.timewalker.ffmq4.common.destination.TopicRef; import net.timewalker.ffmq4.common.message.BytesMessageImpl; import net.timewalker.ffmq4.common.message.EmptyMessageImpl; import net.timewalker.ffmq4.common.message.MapMessageImpl; import net.timewalker.ffmq4.common.message.ObjectMessageImpl; import net.timewalker.ffmq4.common.message.StreamMessageImpl; import net.timewalker.ffmq4.common.message.TextMessageImpl; import net.timewalker.ffmq4.local.destination.LocalQueue; import net.timewalker.ffmq4.local.destination.LocalTopic; import net.timewalker.ffmq4.utils.id.IntegerID; import net.timewalker.ffmq4.utils.id.IntegerIDProvider; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * <p>Base implementation for a JMS {@link Session}</p> */ public abstract class AbstractSession implements Session { private static final Log log = LogFactory.getLog(AbstractSession.class); // Parent connection protected AbstractConnection connection; // Attributes protected IntegerID id; protected boolean transacted; protected int acknowledgeMode; protected boolean closed; public Object deliveryLock = new Object(); // Children protected Map<IntegerID, AbstractMessageConsumer> consumersMap = new Hashtable<>(); private Map<IntegerID, AbstractMessageProducer> producersMap = new Hashtable<>(); private Map<IntegerID, AbstractQueueBrowser> browsersMap = new Hashtable<>(); // Runtime protected IntegerIDProvider idProvider = new IntegerIDProvider(); protected ReadWriteLock externalAccessLock = new ReentrantReadWriteLock(); /** * Constructor */ public AbstractSession(AbstractConnection connection, boolean transacted, int acknowledgeMode) { this.connection = connection; this.transacted = transacted; this.acknowledgeMode = acknowledgeMode; } /** * Constructor */ public AbstractSession(IntegerID id, AbstractConnection connection, boolean transacted, int acknowledgeMode) { this(connection, transacted, acknowledgeMode); this.id = id; } /** * Get the session id * @return the id */ public final IntegerID getId() { return id; } /** * Check that the session is not closed */ protected final void checkNotClosed() throws JMSException { if (closed) throw new IllegalStateException("Session is closed"); // [JMS SPEC] } /* * (non-Javadoc) * @see javax.jms.Session#close() */ @Override public final void close() throws JMSException { externalAccessLock.writeLock().lock(); try { if (closed) return; closed = true; onSessionClose(); } finally { externalAccessLock.writeLock().unlock(); } onSessionClosed(); } protected void onSessionClose() { connection.unregisterSession(this); closeRemainingConsumers(); closeRemainingProducers(); closeRemainingBrowsers(); } protected void onSessionClosed() { // Nothing } /** * Check temporary destinations scope (JMS Spec 4.4.3 p2) * @param destination destination to check */ public final void checkTemporaryDestinationScope(Destination destination) throws JMSException { if (destination instanceof LocalQueue) { LocalQueue localQueue = (LocalQueue) destination; if (localQueue.isTemporary() && !connection.isRegisteredTemporaryQueue(localQueue.getQueueName())) throw new IllegalStateException("Temporary queue does not belong to session's connection."); } else if (destination instanceof LocalTopic) { LocalTopic localTopic = (LocalTopic) destination; if (localTopic.isTemporary() && !connection.isRegisteredTemporaryTopic(localTopic.getTopicName())) throw new IllegalStateException("Temporary topic does not belong to session's connection."); } else throw new FFMQException("Unexpected destination type : " + destination, "INTERNAL_ERROR"); } /** * Wake up all children consumers */ public final void wakeUpConsumers() throws JMSException { synchronized (consumersMap) { Iterator<AbstractMessageConsumer> allConsumers = consumersMap.values().iterator(); while (allConsumers.hasNext()) { AbstractMessageConsumer consumer = allConsumers.next(); consumer.wakeUp(); } } } /** * Lookup a registered consumer */ public final AbstractMessageConsumer lookupRegisteredConsumer(IntegerID consumerId) { return consumersMap.get(consumerId); } /** * Lookup a registered browser */ public final AbstractQueueBrowser lookupRegisteredBrowser(IntegerID browserId) { return browsersMap.get(browserId); } /** * Register a consumer */ protected final void registerConsumer(AbstractMessageConsumer consumer) { if (consumersMap.put(consumer.getId(), consumer) != null) throw new IllegalArgumentException("Consumer " + consumer.getId() + " already exists"); } /** * Register a producer */ protected final void registerProducer(AbstractMessageProducer producer) { if (producersMap.put(producer.getId(), producer) != null) throw new IllegalArgumentException("Producer " + producer.getId() + " already exists"); } /** * Register a browser */ protected final void registerBrowser(AbstractQueueBrowser browser) { if (browsersMap.put(browser.getId(), browser) != null) throw new IllegalArgumentException("Browser " + browser.getId() + " already exists"); } /** * Unregister a consumer */ protected final void unregisterConsumer(AbstractMessageConsumer consumerToRemove) { if (consumersMap.remove(consumerToRemove.getId()) == null) log.warn("Unknown consumer : " + consumerToRemove); } /** * Unregister a producer */ protected final void unregisterProducer(AbstractMessageProducer producerToRemove) { if (producersMap.remove(producerToRemove.getId()) == null) log.warn("Unknown producer : " + producerToRemove); } /** * Unregister a browser */ protected final void unregisterBrowser(AbstractQueueBrowser browserToRemove) { if (browsersMap.remove(browserToRemove.getId()) == null) log.warn("Unknown browser : " + browserToRemove); } /** * Close remaining consumers */ private void closeRemainingConsumers() { List<AbstractMessageConsumer> consumersToClose = new ArrayList<>(consumersMap.size()); synchronized (consumersMap) { consumersToClose.addAll(consumersMap.values()); } for (int n = 0; n < consumersToClose.size(); n++) { MessageConsumer consumer = consumersToClose.get(n); log.debug("Auto-closing unclosed consumer : " + consumer); try { consumer.close(); } catch (Exception e) { log.error("Could not close consumer " + consumer, e); } } } /** * Close remaining consumers */ private void closeRemainingProducers() { List<AbstractMessageProducer> producersToClose = new ArrayList<>(producersMap.size()); synchronized (producersMap) { producersToClose.addAll(producersMap.values()); } for (int n = 0; n < producersToClose.size(); n++) { MessageProducer producer = producersToClose.get(n); log.debug("Auto-closing unclosed producer : " + producer); try { producer.close(); } catch (Exception e) { log.error("Could not close producer " + producer, e); } } } /** * Close remaining browsers */ private void closeRemainingBrowsers() { List<AbstractQueueBrowser> browsersToClose = new ArrayList<>(browsersMap.size()); synchronized (browsersMap) { browsersToClose.addAll(browsersMap.values()); } for (int n = 0; n < browsersToClose.size(); n++) { QueueBrowser browser = browsersToClose.get(n); log.debug("Auto-closing unclosed browser : " + browser); try { browser.close(); } catch (Exception e) { log.error("Could not close browser " + browser, e); } } } /** * Acknowledge the given message */ public abstract void acknowledge() throws JMSException; /* * (non-Javadoc) * @see javax.jms.Session#createMessage() */ @Override public final Message createMessage() throws JMSException { return new EmptyMessageImpl(); } /* (non-Javadoc) * @see javax.jms.Session#createBytesMessage() */ @Override public final BytesMessage createBytesMessage() throws JMSException { return new BytesMessageImpl(); } /* (non-Javadoc) * @see javax.jms.Session#createMapMessage() */ @Override public final MapMessage createMapMessage() throws JMSException { return new MapMessageImpl(); } /* (non-Javadoc) * @see javax.jms.Session#createObjectMessage() */ @Override public final ObjectMessage createObjectMessage() throws JMSException { return new ObjectMessageImpl(); } /* (non-Javadoc) * @see javax.jms.Session#createStreamMessage() */ @Override public final StreamMessage createStreamMessage() throws JMSException { return new StreamMessageImpl(); } /* (non-Javadoc) * @see javax.jms.Session#createTextMessage() */ @Override public final TextMessage createTextMessage() throws JMSException { return new TextMessageImpl(); } /* * (non-Javadoc) * @see javax.jms.Session#createQueue(java.lang.String) */ @Override public Queue createQueue(String queueName) throws JMSException { return new QueueRef(queueName); } /* (non-Javadoc) * @see javax.jms.Session#getAcknowledgeMode() */ @Override public final int getAcknowledgeMode() throws JMSException { if (transacted) return Session.SESSION_TRANSACTED; // [JMS Spec] return acknowledgeMode; } /* (non-Javadoc) * @see javax.jms.Session#getTransacted() */ @Override public final boolean getTransacted() throws JMSException { return transacted; } /* (non-Javadoc) * @see javax.jms.Session#createObjectMessage(java.io.Serializable) */ @Override public final ObjectMessage createObjectMessage(Serializable object) throws JMSException { return new ObjectMessageImpl(object); } /* (non-Javadoc) * @see javax.jms.Session#createTextMessage(java.lang.String) */ @Override public final TextMessage createTextMessage(String text) throws JMSException { return new TextMessageImpl(text); } /* (non-Javadoc) * @see javax.jms.Session#getMessageListener() */ @Override public final MessageListener getMessageListener() throws JMSException { throw new FFMQException("Unsupported feature", "UNSUPPORTED_FEATURE"); } /* (non-Javadoc) * @see javax.jms.Session#setMessageListener(javax.jms.MessageListener) */ @Override public final void setMessageListener(MessageListener listener) throws JMSException { throw new FFMQException("Unsupported feature", "UNSUPPORTED_FEATURE"); } /* (non-Javadoc) * @see javax.jms.Session#createTopic(java.lang.String) */ @Override public Topic createTopic(String topicName) throws JMSException { return new TopicRef(topicName); } /* (non-Javadoc) * @see javax.jms.Session#createConsumer(javax.jms.Destination, java.lang.String) */ @Override public final MessageConsumer createConsumer(Destination destination, String messageSelector) throws JMSException { return createConsumer(destination, messageSelector, false); } /* (non-Javadoc) * @see javax.jms.Session#createConsumer(javax.jms.Destination) */ @Override public final MessageConsumer createConsumer(Destination destination) throws JMSException { return createConsumer(destination, null, false); } /* (non-Javadoc) * @see javax.jms.Session#createDurableSubscriber(javax.jms.Topic, java.lang.String) */ @Override public TopicSubscriber createDurableSubscriber(Topic topic, String name) throws JMSException { return createDurableSubscriber(topic, name, null, false); } /* (non-Javadoc) * @see javax.jms.Session#createBrowser(javax.jms.Queue) */ @Override public QueueBrowser createBrowser(Queue queue) throws JMSException { return createBrowser(queue, null); } /* (non-Javadoc) * @see javax.jms.Session#run() */ @Override public final void run() { // Not implemented } /** * @return the connection */ public final AbstractConnection getConnection() { return connection; } /** * Get the number of active producers for this session * @return the number of active producers for this session */ public final int getConsumersCount() { return consumersMap.size(); } /** * Get the number of active producers for this session * @return the number of active producers for this session */ public final int getProducersCount() { return producersMap.size(); } /* * (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Session[#"); sb.append(id); sb.append("]("); if (transacted) sb.append("transacted"); else { sb.append("not transacted, acknowledgeMode="); sb.append(acknowledgeMode); } sb.append(")"); return sb.toString(); } /** * Get a description of entities held by this object */ public final void getEntitiesDescription(StringBuilder sb) { sb.append(toString()); sb.append("{"); synchronized (consumersMap) { if (!consumersMap.isEmpty()) { int pos = 0; Iterator<AbstractMessageConsumer> consumers = consumersMap.values().iterator(); while (consumers.hasNext()) { AbstractMessageHandler handler = consumers.next(); if (pos++ > 0) sb.append(","); handler.getEntitiesDescription(sb); } } } synchronized (producersMap) { if (!producersMap.isEmpty()) { int pos = 0; Iterator<AbstractMessageProducer> producers = producersMap.values().iterator(); while (producers.hasNext()) { AbstractMessageHandler handler = producers.next(); if (pos++ > 0) sb.append(","); handler.getEntitiesDescription(sb); } } } sb.append("}"); } public final void waitForDeliverySync() { synchronized (deliveryLock) { // Just waiting for lock ... } } }