View Javadoc

1   /***
2    * Redistribution and use of this software and associated documentation
3    * ("Software"), with or without modification, are permitted provided
4    * that the following conditions are met:
5    *
6    * 1. Redistributions of source code must retain copyright
7    *    statements and notices.  Redistributions must also contain a
8    *    copy of this document.
9    *
10   * 2. Redistributions in binary form must reproduce the
11   *    above copyright notice, this list of conditions and the
12   *    following disclaimer in the documentation and/or other
13   *    materials provided with the distribution.
14   *
15   * 3. The name "Exolab" must not be used to endorse or promote
16   *    products derived from this Software without prior written
17   *    permission of Exoffice Technologies.  For written permission,
18   *    please contact info@exolab.org.
19   *
20   * 4. Products derived from this Software may not be called "Exolab"
21   *    nor may "Exolab" appear in their names without prior written
22   *    permission of Exoffice Technologies. Exolab is a registered
23   *    trademark of Exoffice Technologies.
24   *
25   * 5. Due credit should be given to the Exolab Project
26   *    (http://www.exolab.org/).
27   *
28   * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS
29   * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
32   * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39   * OF THE POSSIBILITY OF SUCH DAMAGE.
40   *
41   * Copyright 2001-2003 (C) Exoffice Technologies Inc. All Rights Reserved.
42   *
43   * $Id: PersistentMessageHandle.java,v 1.17 2003/08/07 13:33:04 tanderson Exp $
44   *
45   * Date         Author  Changes
46   * 3/1/2001     jima    Created
47   */
48  package org.exolab.jms.messagemgr;
49  
50  import java.io.IOException;
51  import java.io.ObjectInput;
52  import java.io.ObjectOutput;
53  import java.sql.Connection;
54  
55  import javax.jms.DeliveryMode;
56  import javax.jms.JMSException;
57  import javax.transaction.TransactionManager;
58  
59  import org.exolab.core.foundation.PersistentObject;
60  import org.exolab.jms.client.JmsDestination;
61  import org.exolab.jms.client.JmsQueue;
62  import org.exolab.jms.client.JmsTopic;
63  import org.exolab.jms.message.MessageHandle;
64  import org.exolab.jms.message.MessageId;
65  import org.exolab.jms.message.MessageImpl;
66  import org.exolab.jms.persistence.DatabaseService;
67  import org.exolab.jms.persistence.PersistenceException;
68  
69  
70  /***
71   * A persistent message handle extends {@link MessageHandle} and references a
72   * persistent message. These messages can be discarded from the cache and later
73   * faulted in through the 'resolve' method.
74   *
75   * @version     $Revision: 1.17 $ $Date: 2003/08/07 13:33:04 $
76   * @author      <a href="mailto:jima@exoffice.com">Jim Alateras</a>
77   **/
78  public class PersistentMessageHandle
79      extends PersistentObject
80      implements MessageHandle, Cloneable {
81  
82      /***
83       * Used for serialization
84       */
85      static final long serialVersionUID = 1;
86  
87      /***
88       * This is the message id of the underlying message
89       */
90      private MessageId _id = null;
91  
92      /***
93       * This is the priority of the inderlying message
94       */
95      private int _priority;
96  
97      /***
98       * A flag indicating whether an attempt was made to deliver the message
99       */
100     private boolean _delivered;
101 
102     /***
103      * a transient attribute, to associate this handle with an endpoint
104      */
105     private transient long _clientId = -1;
106 
107     /*
108      * The time that the underlying message was accepted by the server
109      */
110     private long _acceptedTime;
111 
112     /***
113      * This is the sequence number assigned to it by the message manager
114      * that created this handle
115      */
116     private long _sequenceNumber;
117 
118     /***
119      * The expiry time of this message. 0 if it doesn't expire
120      */
121     private long _expiryTime;
122 
123     /***
124      * The name of the durable consumer that this handle belongs too
125      */
126     private String _consumerName;
127 
128     /***
129      * The destination that this handle belongs too
130      */
131     private JmsDestination _destination;
132 
133 
134     /***
135      * Cache the hashcode for this object
136      */
137     private transient int _hashCode = -1;
138 
139 
140     /***
141      * Default constructor
142      */
143     public PersistentMessageHandle() {
144         super();
145     }
146 
147     /***
148      * Create a persistent handle from a message
149      *
150      * @param message - persistent message
151      * @exception JMSException - if the object cannot be constructed
152      */
153     public PersistentMessageHandle(MessageImpl message)
154         throws JMSException {
155         super();
156 
157         if ((message != null) &&
158             (message.getJMSDeliveryMode() == DeliveryMode.PERSISTENT)) {
159             _id = message.getMessageId();
160             _priority = message.getJMSPriority();
161             _delivered = message.getJMSRedelivered();
162             _destination = (JmsDestination) message.getJMSDestination();
163             _acceptedTime = message.getAcceptedTime();
164             _expiryTime = message.getJMSExpiration();
165             _sequenceNumber = message.getSequenceNumber();
166             _clientId = message.getClientId();
167         } else {
168             throw new JMSException(
169                 "Cannot create persistent handle from non-persistent message");
170         }
171     }
172 
173     /***
174      * Set the identity of the underlying message
175      *
176      * @param id - the message id
177      */
178     public void setMessageId(MessageId id) {
179         _id = id;
180     }
181 
182     /***
183      * Return the identity of the underlying message
184      *
185      * @return MessageId
186      */
187     public MessageId getMessageId() {
188         return _id;
189     }
190 
191     /***
192      * Return the associated message or null if it is invalid
193      *
194      * @return  MessageImpl
195      */
196     public MessageImpl getMessage() {
197         return (MessageImpl) MessageMgr.instance().getMessage(this);
198     }
199 
200     /***
201      * Set the messagee priority
202      *
203      * @param id the persistent id
204      */
205     public void setPriority(int priority) {
206         _priority = priority;
207     }
208 
209     /***
210      * Return the priority of the corresponding message
211      *
212      * @return int
213      */
214     public int getPriority() {
215         return _priority;
216     }
217 
218     /***
219      * Set the delivered flag for the underlying message. True if an attempt
220      * has been made to deliver the message
221      *
222      * @param value - true if delivered
223      */
224     public void setDelivered(boolean value) {
225         _delivered = value;
226     }
227 
228     /***
229      * Return the delivered state of the message
230      *
231      * @return long
232      */
233     public boolean getDelivered() {
234         return _delivered;
235     }
236 
237     /***
238      * Set the time that the message was accepted by the server
239      *
240      * @param long - time since epoch
241      */
242     public void setAcceptedTime(long time) {
243         _acceptedTime = time;
244     }
245 
246     /***
247      * Return the time that the message was accepted by the server
248      *
249      * @return long - time in ms since epoch
250      */
251     public long getAcceptedTime() {
252         return _acceptedTime;
253     }
254 
255     /***
256      * Set the message's sequence number
257      *
258      * @param seq - sequence time
259      */
260     public void setSequenceNumber(long seq) {
261         _sequenceNumber = seq;
262     }
263 
264     /***
265      * Return message's sequence number
266      *
267      * @return long - the sequence number
268      */
269     public long getSequenceNumber() {
270         return _sequenceNumber;
271     }
272 
273     /***
274      * Set the message expiry time
275      *
276      * @long time - time in ms since epoch
277      */
278     public void setExpiryTime(long time) {
279         _expiryTime = time;
280     }
281 
282     /***
283      * Return the message expiry time
284      *
285      * @return long - time in ms since epoch
286      */
287     public long getExpiryTime() {
288         return _expiryTime;
289     }
290 
291     /***
292      * Determines if the message has expired
293      *
294      * @return <code>true</code> if the message has expired, otherwise
295      * <code>false</code>
296      */
297     public boolean hasExpired() {
298         return (_expiryTime != 0 && _expiryTime <= System.currentTimeMillis());
299     }
300 
301     // implementation of MessageHandle.setConsumerName
302     public void setConsumerName(String name) {
303         _consumerName = name;
304     }
305 
306     // implementation of MessageHandle.getConsumerName
307     public String getConsumerName() {
308         return _consumerName;
309     }
310 
311     // implementation of MessageHandle.isPersistent
312     public boolean isPersistent() {
313         return true;
314     }
315 
316     /***
317      * Set the destination that owns this handle
318      *
319      * @param destination - the destination
320      */
321     public void setDestination(JmsDestination destination) {
322         _destination = destination;
323     }
324 
325     /***
326      * Return the destination for this handle
327      *
328      * @return JmsDestination
329      */
330     public JmsDestination getDestination() {
331         return _destination;
332     }
333 
334     /***
335      * Set the client id, that owns this handle
336      *
337      * @param clientId - client identity
338      */
339     public void setClientId(long clientId) {
340         _clientId = clientId;
341     }
342 
343     /***
344      * Retrieve the client identity associated with this handle
345      *
346      * @return long
347      */
348     public long getClientId() {
349         return _clientId;
350     }
351 
352     /***
353      * Override the behaviour of the base class. I don't think we want to
354      * clear the handle unless there are definitely no more references.
355      */
356     public void clear() {
357     }
358 
359     /***
360      * Destroy this handle
361      */
362     public void destroy() {
363         // notify the message manager
364         MessageMgr.instance().handleDestroyed(this);
365 
366         // remove the handle from the database
367         Connection connection = null;
368         TransactionManager tm = null;
369 
370         try {
371             connection = DatabaseService.getConnection();
372             DatabaseService.getAdapter().removeMessageHandle(connection, this);
373             connection.commit();
374         } catch (PersistenceException exception) {
375             if (connection != null) {
376                 try {
377                     connection.rollback();
378                 } catch (Exception nested) {
379                     // ignore
380                 }
381             }
382         } catch (Exception exception) {
383             // ignore for the moment
384         } finally {
385             if (connection != null) {
386                 try {
387                     connection.close();
388                 } catch (Exception nested) {
389                     // ignore
390                 }
391             }
392         }
393     }
394 
395     /***
396      * Clone the persistent message handle object
397      *
398      * @return copy of a PersistentMessageHandle
399      * @throws CloneNotSupportedException
400      */
401     public Object clone() throws CloneNotSupportedException {
402         PersistentMessageHandle handle =
403             (PersistentMessageHandle) super.clone();
404 
405         handle._id = new MessageId(_id.getId());
406         handle._priority = _priority;
407         handle._delivered = _delivered;
408         handle._acceptedTime = _acceptedTime;
409         handle._expiryTime = _expiryTime;
410         handle._consumerName = _consumerName;
411         handle._sequenceNumber = _sequenceNumber;
412 
413         // clone the destination
414         if (_destination instanceof JmsQueue) {
415             handle._destination = new JmsQueue(_destination.getName());
416         } else {
417             handle._destination = new JmsTopic(_destination.getName());
418         }
419         handle._destination.setPersistent(true);
420 
421         return handle;
422     }
423 
424     // override the definition of equals
425     public boolean equals(Object object) {
426 
427         boolean result = false;
428 
429         if ((object != null) &&
430             (object instanceof PersistentMessageHandle)) {
431 
432             PersistentMessageHandle mhdl = (PersistentMessageHandle) object;
433             if (mhdl._id.equals(_id)) {
434                 result = true;
435             }
436 
437             //if ((mhdl._id.equals(_id)) &&
438             //    (mhdl._consumerName.equals(_consumerName))) {
439             //    result = true;
440             //}
441         }
442 
443         return result;
444     }
445 
446     // override the Object.toString()
447     public String toString() {
448         return ("Handle : " + _id.getId() + "@" + _consumerName + ":" +
449             _sequenceNumber + ":" + _acceptedTime + ":" + _priority);
450     }
451 
452 
453     // override the definition of hashCode
454     public int hashCode() {
455         if (_hashCode == -1) {
456             _hashCode = (_id + _consumerName).hashCode();
457         }
458 
459         return _hashCode;
460     }
461 
462     /***
463      * This is used to set the message as delivered. This can only be called
464      * by the delivery engine and should not be used anywhere else. This will
465      * mark the message as being delivered and also update the database
466      * <p>
467      * We do not really throw an exception here....and it seems that this is
468      * a very fine grained approach to dealing with this...basically translates to
469      * a transaction for every message.
470      */
471     public void setDelivered() {
472         if (!_delivered) {
473 
474             Connection connection = null;
475             TransactionManager tm = null;
476 
477             try {
478                 connection = DatabaseService.getConnection();
479 
480                 DatabaseService.getAdapter().updateMessageHandle(connection,
481                     this);
482                 _delivered = true;
483 
484                 // commit the transactions
485                 connection.commit();
486             } catch (PersistenceException exception) {
487                 System.err.println("Failed in setDelivereds b/c " +
488                     exception.toString());
489                 if (connection != null) {
490                     try {
491                         connection.rollback();
492                     } catch (Exception nested) {
493                         // ignore
494                     }
495                 }
496             } catch (Exception exception) {
497                 // ignore for the moment
498             } finally {
499                 if (connection != null) {
500                     try {
501                         connection.close();
502                     } catch (Exception nested) {
503                         // ignore
504                     }
505                 }
506             }
507         }
508     }
509 
510     // implementation of Externalizable.writeExternal
511     public void writeExternal(ObjectOutput stream)
512         throws IOException {
513         stream.writeLong(serialVersionUID);
514         stream.writeObject(_id);
515         stream.writeInt(_priority);
516         stream.writeBoolean(_delivered);
517         stream.writeLong(_acceptedTime);
518         stream.writeLong(_sequenceNumber);
519         stream.writeLong(_expiryTime);
520         stream.writeObject(_consumerName);
521         stream.writeObject(_destination);
522         super.writeExternal(stream);
523     }
524 
525     // implementation of Externalizable.writeExternal
526     public void readExternal(ObjectInput stream)
527         throws IOException, ClassNotFoundException {
528         long version = stream.readLong();
529         if (version == serialVersionUID) {
530             _id = (MessageId) stream.readObject();
531             _priority = stream.readInt();
532             _delivered = stream.readBoolean();
533             _acceptedTime = stream.readLong();
534             _sequenceNumber = stream.readLong();
535             _expiryTime = stream.readLong();
536             _consumerName = (String) stream.readObject();
537             _destination = (JmsDestination) stream.readObject();
538             super.readExternal(stream);
539         } else {
540             throw new IOException("PersistentMessageHandle with version " +
541                 version + " is not supported.");
542         }
543     }
544 }
545