001    package com.mockrunner.mock.jms;
002    
003    import java.io.Serializable;
004    import java.util.ArrayList;
005    import java.util.Collections;
006    import java.util.List;
007    
008    import javax.jms.BytesMessage;
009    import javax.jms.Destination;
010    import javax.jms.InvalidDestinationException;
011    import javax.jms.JMSException;
012    import javax.jms.MapMessage;
013    import javax.jms.Message;
014    import javax.jms.MessageConsumer;
015    import javax.jms.MessageListener;
016    import javax.jms.MessageProducer;
017    import javax.jms.ObjectMessage;
018    import javax.jms.Queue;
019    import javax.jms.QueueBrowser;
020    import javax.jms.Session;
021    import javax.jms.StreamMessage;
022    import javax.jms.TemporaryQueue;
023    import javax.jms.TemporaryTopic;
024    import javax.jms.TextMessage;
025    import javax.jms.Topic;
026    import javax.jms.TopicSubscriber;
027    
028    import com.mockrunner.jms.GenericTransmissionManager;
029    import com.mockrunner.jms.MessageManager;
030    import com.mockrunner.jms.QueueTransmissionManager;
031    import com.mockrunner.jms.TopicTransmissionManager;
032    import com.mockrunner.jms.TransmissionManagerWrapper;
033    
034    /**
035     * Mock implementation of JMS <code>Session</code>.
036     * 
037     * Please note that this implementation does not
038     * implement transaction isolation at the moment.
039     * Messages are immediately sent. If acknowledge
040     * mode is AUTO_ACKNOWLEDGE or DUPS_OK_ACKNOWLEDGE,
041     * the message will be automatically acknowledged,
042     * otherwise, it will not be acknowledged. According
043     * to JMS specification, the acknowledged mode must
044     * be ignored for transacted sessions. This is currently
045     * not implemented, i.e. transacted sessions behave like
046     * sessions with acknowledge mode AUTO_ACKNOWLEDGE. 
047     * Messages are acknowledged even if the transaction is 
048     * rolled back. However, the framework keeps track if a
049     * transaction is committed or rolled back, so you can test 
050     * this and rely on the container for the rest.
051     * You can set a <code>MessageListener</code> directly to
052     * the session. This is an application server internal feature 
053     * and not meant for application use in JMS. 
054     * This mock session dispatches any message of any 
055     * known <code>Queue</code> and <code>Topic</code> to the 
056     * distinguished <code>MessageListener</code>, if such 
057     * a <code>MessageListener</code> is registered.
058     */
059    public class MockSession implements Session
060    {
061        private MockConnection connection;
062        private QueueTransmissionManager queueTransManager;
063        private TopicTransmissionManager topicTransManager;
064        private GenericTransmissionManager genericTransManager;
065        private TransmissionManagerWrapper transManager;
066        private MessageManager messageManager;
067        private MessageListener messageListener;
068        private List tempQueues;
069        private List tempTopics;
070        private boolean transacted;
071        private int acknowledgeMode;
072        private int numberCommits;
073        private int numberRollbacks;
074        private boolean recovered;
075        private boolean closed;
076        
077        public MockSession(MockConnection connection, boolean transacted, int acknowledgeMode)
078        {
079            this.connection = connection;
080            this.transacted = transacted;
081            this.acknowledgeMode = acknowledgeMode;
082            queueTransManager = new QueueTransmissionManager(connection, this);
083            topicTransManager = new TopicTransmissionManager(connection, this);
084            genericTransManager = new GenericTransmissionManager(connection, this);
085            transManager = new TransmissionManagerWrapper(queueTransManager, topicTransManager, genericTransManager);
086            messageManager = new MessageManager();
087            tempQueues = new ArrayList();
088            tempTopics = new ArrayList();
089            messageListener = null;
090            numberCommits = 0;
091            numberRollbacks = 0;
092            recovered = false;
093            closed = false;
094        }
095        
096        /**
097         * Returns the {@link com.mockrunner.jms.QueueTransmissionManager}.
098         * @return the {@link com.mockrunner.jms.QueueTransmissionManager}
099         */
100        public QueueTransmissionManager getQueueTransmissionManager()
101        {
102            return queueTransManager;
103        }
104        
105        /**
106         * Returns the {@link com.mockrunner.jms.TopicTransmissionManager}.
107         * @return the {@link com.mockrunner.jms.TopicTransmissionManager}
108         */
109        public TopicTransmissionManager getTopicTransmissionManager()
110        {
111            return topicTransManager;
112        }
113        
114        /**
115         * Returns the {@link com.mockrunner.jms.GenericTransmissionManager}.
116         * @return the {@link com.mockrunner.jms.GenericTransmissionManager}
117         */
118        public GenericTransmissionManager getGenericTransmissionManager()
119        {
120            return genericTransManager;
121        }
122        
123        /**
124         * @deprecated use {@link #getTransmissionManagerWrapper}
125         */
126        public TransmissionManagerWrapper getTransmissionManager()
127        {
128            return getTransmissionManagerWrapper();
129        }
130        
131        /**
132         * Returns the {@link com.mockrunner.jms.TransmissionManagerWrapper}.
133         * @return the {@link com.mockrunner.jms.TransmissionManagerWrapper}
134         */
135        public TransmissionManagerWrapper getTransmissionManagerWrapper()
136        {
137            return transManager;
138        }
139        
140        /**
141         * Returns the {@link MessageManager} for this session.
142         * @return the {@link MessageManager}
143         */
144        public MessageManager getMessageManager()
145        {
146            return messageManager;
147        }
148        
149        /**
150         * Returns the list of temporary queues.
151         * @return the <code>TemporaryQueue</code> list
152         */
153        public List getTemporaryQueueList()
154        {
155            return Collections.unmodifiableList(tempQueues);
156        }
157    
158        /**
159         * Returns a <code>TemporaryQueue</code> by its index. The
160         * index represent the number of the queue. Returns <code>null</code>
161         * if no such <code>TemporaryQueue</code> is present.
162         * @param index the index
163         * @return the <code>TemporaryQueue</code>
164         */
165        public MockTemporaryQueue getTemporaryQueue(int index)
166        {
167            if(tempQueues.size() <= index || index < 0) return null;
168            return (MockTemporaryQueue)tempQueues.get(index);
169        }
170        
171        /**
172         * Returns the list of temporary topics.
173         * @return the <code>TemporaryTopic</code> list
174         */
175        public List getTemporaryTopicList()
176        {
177            return Collections.unmodifiableList(tempTopics);
178        }
179    
180        /**
181         * Returns a <code>TemporaryTopic</code> by its index. The
182         * index represent the number of the topic. Returns <code>null</code>
183         * if no such <code>TemporaryTopic</code> is present.
184         * @param index the index
185         * @return the <code>TemporaryTopic</code>
186         */
187        public MockTemporaryTopic getTemporaryTopic(int index)
188        {
189            if(tempTopics.size() <= index || index < 0) return null;
190            return (MockTemporaryTopic)tempTopics.get(index);
191        }
192        
193        /**
194         * Returns if this session was closed.
195         * @return <code>true</code> if this session is closed
196         */
197        public boolean isClosed()
198        {
199            return closed;
200        }
201    
202        /**
203         * Returns if this session was recovered.
204         * @return <code>true</code> if this session was recovered
205         */
206        public boolean isRecovered()
207        {
208            return recovered;
209        }
210    
211        /**
212         * Returns if the current transaction was committed.
213         * @return <code>true</code> if the transaction was committed
214         */
215        public boolean isCommitted()
216        {
217            return (numberCommits > 0);
218        }
219        
220        /**
221         * Returns the number of commits.
222         * @return the number of commits
223         */
224        public int getNumberCommits()
225        {
226            return numberCommits;
227        }
228    
229        /**
230         * Returns if the current transaction was rolled back.
231         * @return <code>true</code> if the transaction was rolled back
232         */
233        public boolean isRolledBack()
234        {
235            return (numberRollbacks > 0);
236        }
237        
238        /**
239         * Returns the number of rollbacks.
240         * @return the number of rollbacks
241         */
242        public int getNumberRollbacks()
243        {
244            return numberRollbacks;
245        }
246        
247        /**
248         * Returns if messages should be automatically acknowledged,
249         * i.e. if the acknowledge mode is not <code>CLIENT_ACKNOWLEDGE</code>.
250         * @return <code>true</code> if messages are automatically acknowledged
251         */
252        public boolean isAutoAcknowledge()
253        {
254            return acknowledgeMode != CLIENT_ACKNOWLEDGE;
255        }
256        
257        /**
258         * Note: Returns <code>0</code> if the session is transacted.
259         * This method does not exist in JMS 1.0.2. In JMS 1.1 it
260         * should return <code>Session.SESSION_TRANSACTED</code>
261         * which is specified as <code>0</code>. In order to avoid
262         * different versions for JMS 1.0.2 and 1.1 
263         * (<code>Session.SESSION_TRANSACTED</code> does not
264         * exist in 1.0.2) this method returns hardcoded <code>0</code>,
265         * if the session is transacted.
266         * @return the acknowledge mode
267         */
268        public int getAcknowledgeMode() throws JMSException
269        {
270            if(getTransacted()) return 0;
271            return acknowledgeMode;
272        }
273        
274        public boolean getTransacted() throws JMSException
275        {
276            connection.throwJMSException();
277            return transacted;
278        }
279        
280        public BytesMessage createBytesMessage() throws JMSException
281        {
282            connection.throwJMSException();
283            return getMessageManager().createBytesMessage();
284        }
285    
286        public MapMessage createMapMessage() throws JMSException
287        {
288            connection.throwJMSException();
289            return getMessageManager().createMapMessage();
290        }
291    
292        public Message createMessage() throws JMSException
293        {
294            connection.throwJMSException();
295            return getMessageManager().createMessage();
296        }
297    
298        public ObjectMessage createObjectMessage() throws JMSException
299        {
300            connection.throwJMSException();
301            return createObjectMessage(null);
302        }
303    
304        public ObjectMessage createObjectMessage(Serializable object) throws JMSException
305        {
306            connection.throwJMSException();
307            return getMessageManager().createObjectMessage(object);
308        }
309    
310        public StreamMessage createStreamMessage() throws JMSException
311        {
312            connection.throwJMSException();
313            return getMessageManager().createStreamMessage();
314        }
315    
316        public TextMessage createTextMessage() throws JMSException
317        {
318            connection.throwJMSException();
319            return createTextMessage(null);
320        }
321    
322        public TextMessage createTextMessage(String text) throws JMSException
323        {
324            connection.throwJMSException();
325            return getMessageManager().createTextMessage(text);
326        }
327        
328        public MessageListener getMessageListener() throws JMSException
329        {
330            connection.throwJMSException();
331            return messageListener;
332        }
333    
334        public void setMessageListener(MessageListener messageListener) throws JMSException
335        {
336            connection.throwJMSException();
337            this.messageListener = messageListener;
338        }
339        
340        public void run()
341        {
342        
343        }
344            
345        public void commit() throws JMSException
346        {
347            connection.throwJMSException();
348            numberCommits++;
349        }
350    
351        public void rollback() throws JMSException
352        {
353            connection.throwJMSException();
354            recover();
355            numberRollbacks++;
356        }
357    
358        public void close() throws JMSException
359        {
360            connection.throwJMSException();
361            if(getTransacted() && !isCommitted())
362            {
363                rollback();
364            }
365            getQueueTransmissionManager().closeAll();
366            getTopicTransmissionManager().closeAll();
367            getGenericTransmissionManager().closeAll();
368            closed = true;
369        }
370    
371        public void recover() throws JMSException
372        {
373            connection.throwJMSException();
374            recovered = true;
375        }
376        
377        public void unsubscribe(String name) throws JMSException
378        {
379            getConnection().throwJMSException();
380            topicTransManager.removeTopicDurableSubscriber(name);
381        }
382        
383        public Queue createQueue(String name) throws JMSException
384        {
385            getConnection().throwJMSException();
386            MockQueue queue = ((MockConnection)getConnection()).getDestinationManager().getQueue(name);
387            if(null == queue)
388            {
389                throw new JMSException("Queue with name " + name + " not found");
390            }
391            addSessionToQueue(queue);
392            return queue;
393        }
394    
395        public TemporaryQueue createTemporaryQueue() throws JMSException
396        {
397            getConnection().throwJMSException();
398            MockTemporaryQueue queue = new MockTemporaryQueue();
399            tempQueues.add(queue);
400            addSessionToQueue(queue);
401            return queue;
402        }
403        
404        public Topic createTopic(String name) throws JMSException
405        {
406            getConnection().throwJMSException();
407            MockTopic topic = ((MockConnection)getConnection()).getDestinationManager().getTopic(name);
408            if(null == topic)
409            {
410                throw new JMSException("Topic with name " + name + " not found");
411            }
412            addSessionToTopic(topic);
413            return topic;
414        }
415    
416        public TemporaryTopic createTemporaryTopic() throws JMSException
417        {
418            getConnection().throwJMSException();
419            MockTemporaryTopic topic = new MockTemporaryTopic();
420            tempTopics.add(topic);
421            addSessionToTopic(topic);
422            return topic;
423        }
424        
425        public MessageConsumer createConsumer(Destination destination) throws JMSException
426        {
427            getConnection().throwJMSException();
428            return createConsumer(destination, null);
429        }
430        
431        public MessageConsumer createConsumer(Destination destination, String messageSelector) throws JMSException
432        {
433            getConnection().throwJMSException();
434            return createConsumer(destination, messageSelector, false);
435        }
436        
437        public MessageConsumer createConsumer(Destination destination, String messageSelector, boolean noLocal) throws JMSException
438        {
439                    if(null == destination)
440                    {
441                            throw new IllegalArgumentException("destination must not be null");
442                    }
443            getConnection().throwJMSException();      
444            if(destination instanceof MockQueue)
445            {
446                addSessionToQueue((Queue)destination);
447                return getQueueTransmissionManager().createQueueReceiver((MockQueue)destination, messageSelector);
448            }
449            else if(destination instanceof MockTopic)
450            {
451                addSessionToTopic((Topic)destination);
452                return getTopicTransmissionManager().createTopicSubscriber((MockTopic)destination, messageSelector, noLocal);
453            }
454            else
455            {
456                throw new InvalidDestinationException("destination must be an instance of MockQueue or MockTopic");
457            }
458        }
459        
460        public MessageProducer createProducer(Destination destination) throws JMSException
461        {
462            getConnection().throwJMSException();
463            if(null == destination)
464            {
465                return createProducerForNullDestination();
466            }
467            if(destination instanceof MockQueue)
468            {
469                addSessionToQueue((Queue)destination);
470                return getQueueTransmissionManager().createQueueSender((MockQueue)destination);
471            }
472            else if(destination instanceof MockTopic)
473            {
474                addSessionToTopic((Topic)destination);
475                return getTopicTransmissionManager().createTopicPublisher((MockTopic)destination);
476            }
477            else
478            {
479                throw new InvalidDestinationException("destination must be an instance of MockQueue or MockTopic");
480            }
481        }
482        
483        public QueueBrowser createBrowser(Queue queue) throws JMSException
484        {
485            getConnection().throwJMSException();
486            return createBrowser(queue, null);
487        }
488    
489        public QueueBrowser createBrowser(Queue queue, String messageSelector) throws JMSException
490        {
491            getConnection().throwJMSException();
492            if(!(queue instanceof MockQueue))
493            {
494                throw new InvalidDestinationException("queue must be an instance of MockQueue");
495            }
496            addSessionToQueue(queue);
497            return queueTransManager.createQueueBrowser((MockQueue)queue, messageSelector);
498        }
499        
500        public TopicSubscriber createDurableSubscriber(Topic topic, String name) throws JMSException
501        {
502            getConnection().throwJMSException();
503            return createDurableSubscriber(topic, name, null, false);
504        }
505    
506        public TopicSubscriber createDurableSubscriber(Topic topic, String name, String messageSelector, boolean noLocal) throws JMSException
507        {
508            getConnection().throwJMSException();
509            if(!(topic instanceof MockTopic))
510            {
511                throw new InvalidDestinationException("topic must be an instance of MockTopic");
512            }
513            addSessionToTopic(topic);
514            return topicTransManager.createDurableTopicSubscriber((MockTopic)topic, name, messageSelector, noLocal);
515        }
516        
517        protected MockConnection getConnection()
518        {
519            return connection;
520        }
521        
522        public void addSessionToQueue(Queue queue)
523        {
524            if(queue instanceof MockQueue)
525            {
526                ((MockQueue)queue).addSession(this);
527            }
528        }
529        
530        public void addSessionToTopic(Topic topic)
531        {
532            if(topic instanceof MockTopic)
533            {
534                ((MockTopic)topic).addSession(this);
535            }
536        }
537        
538        protected MessageProducer createProducerForNullDestination()
539        {
540            return getGenericTransmissionManager().createMessageProducer();
541        }
542    }