KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > walend > somnifugi > SomniConnection


1 package net.walend.somnifugi;
2
3 import java.util.Set JavaDoc;
4 import java.util.HashSet JavaDoc;
5 import java.util.Map JavaDoc;
6 import java.util.HashMap JavaDoc;
7 import java.util.Iterator JavaDoc;
8 import java.util.Enumeration JavaDoc;
9 import java.util.NoSuchElementException JavaDoc;
10
11 import javax.naming.Context JavaDoc;
12
13 import javax.jms.Connection JavaDoc;
14 import javax.jms.ConnectionFactory JavaDoc;
15 import javax.jms.JMSException JavaDoc;
16 import javax.jms.ConnectionMetaData JavaDoc;
17 import javax.jms.ExceptionListener JavaDoc;
18 import javax.jms.Session JavaDoc;
19 import javax.jms.Destination JavaDoc;
20 import javax.jms.ServerSessionPool JavaDoc;
21 import javax.jms.ConnectionConsumer JavaDoc;
22 import javax.jms.Topic JavaDoc;
23
24
25 /**
26 SomniConnection holds on to a client id string, an ExceptionListener, and a collection of Sessions to start, stop and close.
27
28 @author <a HREF="http://walend.net">David Walend</a> <a HREF="mailto:david@walend.net">david@walend.net</a>
29  */

30
31 public abstract class SomniConnection
32     implements Connection JavaDoc
33 {
34     protected final Object JavaDoc guard = new Object JavaDoc();
35     private ConnectionFactory JavaDoc factory;
36     private String JavaDoc clientID;
37     private Set JavaDoc<SomniSession> sessionSet = new HashSet JavaDoc<SomniSession>();
38     private boolean started = false;
39     private boolean closed = false;
40     private int sessionCounter = 0;
41
42     private Context JavaDoc context;
43
44     private ExceptionListener JavaDoc exceptionListener = new SomniDefaultExceptionListener();
45
46     protected SomniConnection(ConnectionFactory JavaDoc factory,String JavaDoc clientID,Context JavaDoc context)
47     {
48         this.factory = factory;
49         this.clientID = clientID;
50         this.context = context;
51     }
52
53     /**
54     Creates a report of how many messages are pending and how old the next message is in each message consumer in each session. The result is a guess.
55     
56     This method synchronizes on the protected guard, so you won't be able to create or remove sessions while it runs.
57     
58     @return a Map of the consumer names in all sessions stored by this connection to a guess at the number of pending messages and time stamp of the next message out.
59     */

60     public Map JavaDoc<String JavaDoc,SomniConsumerReport> createSessionReport()
61     {
62         Map JavaDoc<String JavaDoc,SomniConsumerReport> report = new HashMap JavaDoc<String JavaDoc,SomniConsumerReport>();
63         
64         synchronized(guard)
65         {
66             for(SomniSession session : sessionSet)
67             {
68                 report.putAll(session.createConsumerReports());
69             }
70         }
71         
72         return report;
73     }
74     
75     protected Context JavaDoc getContext()
76     {
77         return context;
78     }
79
80     protected String JavaDoc createSessionName()
81         throws JMSException JavaDoc
82     {
83         StringBuffer JavaDoc buffy = new StringBuffer JavaDoc();
84         buffy.append(getClientID());
85         buffy.append(":"+sessionCounter);
86         sessionCounter++;
87
88         return buffy.toString();
89     }
90
91  /** Creates a <CODE>Session</CODE> object.
92       *
93       * @param transacted indicates whether the session is transacted
94       * @param acknowledgeMode indicates whether the consumer or the
95       * client will acknowledge any messages it receives; ignored if the session
96       * is transacted. Legal values are <code>Session.AUTO_ACKNOWLEDGE</code>,
97       * <code>Session.CLIENT_ACKNOWLEDGE</code>, and
98       * <code>Session.DUPS_OK_ACKNOWLEDGE</code>.
99       *
100       * @return a newly created session
101       *
102       * @exception JMSException if the <CODE>Connection</CODE> object fails
103       * to create a session due to some internal error or
104       * lack of support for the specific transaction
105       * and acknowledgement mode.
106       * @since 1.1
107       *
108       * @see Session#AUTO_ACKNOWLEDGE
109       * @see Session#CLIENT_ACKNOWLEDGE
110       * @see Session#DUPS_OK_ACKNOWLEDGE
111   
112       */

113
114     public abstract Session JavaDoc createSession(boolean transacted,int acknowledgeMode)
115         throws JMSException JavaDoc;
116  
117     /**
118 Gets the client identifier for this connection.
119  
120 <P>This value is specific to the JMS provider. It is either preconfigured
121 by an administrator in a <CODE>ConnectionFactory</CODE> object
122 or assigned dynamically by the application by calling the
123 <code>setClientID</code> method.
124
125
126 @return the unique client identifier
127  
128 @exception JMSException if the JMS provider fails to return
129                         the client ID for this connection due
130                         to some internal error.
131       *
132       **/

133     public String JavaDoc getClientID()
134         throws JMSException JavaDoc
135     {
136         return clientID;
137     }
138
139     /**
140 Sets the client identifier for this connection.
141  
142 <P>The preferred way to assign a JMS client's client identifier is for
143 it to be configured in a client-specific <CODE>ConnectionFactory</CODE>
144 object and transparently assigned to the <CODE>Connection</CODE> object
145 it creates.
146
147 <P>Alternatively, a client can set a connection's client identifier
148 using a provider-specific value. The facility to set a connection's
149 client identifier explicitly is not a mechanism for overriding the
150 identifier that has been administratively configured. It is provided
151 for the case where no administratively specified identifier exists.
152 If one does exist, an attempt to change it by setting it must throw an
153 <CODE>IllegalStateException</CODE>. If a client sets the client identifier
154 explicitly, it must do so immediately after it creates the connection
155 and before any other
156 action on the connection is taken. After this point, setting the
157 client identifier is a programming error that should throw an
158 <CODE>IllegalStateException</CODE>.
159       *
160 <P>The purpose of the client identifier is to associate a connection and
161 its objects with a state maintained on behalf of the client by a
162 provider. The only such state identified by the JMS API is that required
163 to support durable subscriptions.
164       *
165 <P>If another connection with the same <code>clientID</code> is already running when
166 this method is called, the JMS provider should detect the duplicate ID and throw
167 an <CODE>InvalidClientIDException</CODE>.
168       *
169 @param clientID the unique client identifier
170
171 @exception JMSException if the JMS provider fails to
172                         set the client ID for this connection due
173                         to some internal error.
174       *
175 @exception InvalidClientIDException if the JMS client specifies an
176                         invalid or duplicate client ID.
177 @exception IllegalStateException if the JMS client attempts to set
178       a connection's client ID at the wrong time or
179       when it has been administratively configured.
180       */

181     public void setClientID(String JavaDoc clientID)
182         throws JMSException JavaDoc
183     {
184         checkClosed();
185         if(!started)
186             {
187                 SomniLogger.IT.config(this.clientID+" renamed "+clientID);
188                 this.clientID=clientID;
189             }
190         else
191             {
192                 throw new IllegalStateException JavaDoc("Do not attempt to change the clientID after the connection has started.");
193             }
194     }
195
196     private static class SomniConnectionMetaData
197         implements ConnectionMetaData JavaDoc
198     {
199         public String JavaDoc getJMSVersion()
200             throws JMSException JavaDoc
201         {
202             return new String JavaDoc("1.0.2b");
203         }
204         
205         public int getJMSMajorVersion()
206             throws JMSException JavaDoc
207         {
208             return 1;
209         }
210
211         public int getJMSMinorVersion()
212             throws JMSException JavaDoc
213         {
214             return 0;
215         }
216
217         public String JavaDoc getJMSProviderName()
218             throws JMSException JavaDoc
219         {
220             return "SomnifugiJMS from http://walend.net";
221         }
222
223         public String JavaDoc getProviderVersion()
224             throws JMSException JavaDoc
225         {
226             return "0.2.0";
227         }
228
229         public int getProviderMajorVersion()
230             throws JMSException JavaDoc
231         {
232             return 0;
233         }
234
235         public int getProviderMinorVersion()
236             throws JMSException JavaDoc
237         {
238             return 2;
239         }
240
241         private class EmptyEnumeration
242             implements Enumeration JavaDoc
243         {
244             public EmptyEnumeration()
245             {}
246
247             public boolean hasMoreElements()
248             {
249                 return false;
250             }
251
252             public Object JavaDoc nextElement()
253             {
254                 throw new NoSuchElementException JavaDoc("Use hasMoreElements() before calling nextElement().");
255             }
256         }
257
258         public Enumeration JavaDoc getJMSXPropertyNames()
259             throws JMSException JavaDoc
260         {
261             return new EmptyEnumeration();
262         }
263
264     }
265
266
267     /**
268 Gets the metadata for this connection.
269  
270 @return the connection metadata
271  
272 @exception JMSException if the JMS provider fails to
273                         get the connection metadata for this connection.
274       *
275 @see <{ConnectionMetaData}>
276       */

277     public ConnectionMetaData JavaDoc getMetaData()
278         throws JMSException JavaDoc
279     {
280         return new SomniConnectionMetaData();
281     }
282
283     /**
284 Gets the <CODE>ExceptionListener</CODE> object for this connection.
285      *
286 @return the <CODE>ExceptionListener</CODE> for this connection
287      *
288 @exception JMSException if the JMS provider fails to
289                         get the <CODE>ExceptionListener</CODE> for this
290                         connection.
291      */

292     public ExceptionListener JavaDoc getExceptionListener()
293         throws JMSException JavaDoc
294     {
295         synchronized(guard)
296             {
297                 return exceptionListener;
298             }
299     }
300
301     /**
302 Sets an exception listener for this connection.
303       *
304 <P>If a JMS provider detects a serious problem with a connection, it
305 informs the connection's <CODE>ExceptionListener</CODE>, if one has been
306 registered. It does this by calling the listener's
307 <CODE>onException</CODE> method, passing it a <CODE>JMSException</CODE>
308 object describing the problem.
309       *
310 <P>An exception listener allows a client to be notified of a problem
311 asynchronously.
312 Some connections only consume messages, so they would have no other
313 way to learn their connection has failed.
314       *
315 <P>A connection serializes execution of its
316 <CODE>ExceptionListener</CODE>.
317       *
318 <P>A JMS provider should attempt to resolve connection problems
319 itself before it notifies the client of them.
320       *
321 @param listener the exception listener
322       *
323 @exception JMSException if the JMS provider fails to
324                         set the exception listener for this connection.
325       */

326     public void setExceptionListener(ExceptionListener JavaDoc listener)
327         throws JMSException JavaDoc
328     {
329         if(!(listener instanceof SomniExceptionListener))
330         {
331             throw new ClassCastException JavaDoc("listener, a "+listener.getClass().getName()+", must implement "+SomniExceptionListener.class.getName());
332         }
333         synchronized(guard)
334             {
335                 checkClosed();
336                 exceptionListener = listener;
337             }
338         SomniLogger.IT.config("exceptionListener in "+getClientID()+" set to a "+exceptionListener.getClass().getName());
339     }
340
341     protected void addSession(SomniSession session)
342     {
343         synchronized(guard)
344             {
345                 checkClosed();
346                 sessionSet.add(session);
347                 if(started)
348                     {
349                         session.start();
350                     }
351             }
352         try
353             {
354                 SomniLogger.IT.fine("Session "+session.getName()+" added to "+getClientID());
355             }
356         catch(JMSException JavaDoc jmse)
357             {
358                 exceptionListener.onException(jmse);
359             }
360     }
361     
362     protected void removeSession(SomniSession session)
363     {
364         synchronized(guard)
365             {
366                 checkClosed();
367                 session.stop();
368                 sessionSet.remove(session);
369             }
370         try
371             {
372                 SomniLogger.IT.fine(session.getName()+" removed from "+getClientID());
373             }
374         catch(JMSException JavaDoc jmse)
375             {
376                 exceptionListener.onException(jmse);
377             }
378     }
379     
380
381     /**
382 Starts (or restarts) a connection's delivery of incoming messages.
383 A call to <CODE>start</CODE> on a connection that has already been
384 started is ignored.
385
386 @exception JMSException if the JMS provider fails to start
387                         message delivery due to some internal error.
388       *
389 @see javax.jms.Connection#stop
390       */

391     public void start()
392         throws JMSException JavaDoc
393     {
394         synchronized(guard)
395             {
396                 checkClosed();
397                 started = true;
398                 Iterator JavaDoc it = sessionSet.iterator();
399                 while(it.hasNext())
400                     {
401                         SomniSession session = (SomniSession)it.next();
402                         session.start();
403                     }
404             }
405         SomniLogger.IT.fine(getClientID()+" started successfully.");
406     }
407
408     protected void checkClosed()
409     {
410         synchronized(guard)
411             {
412                 if(closed)
413                     {
414                         throw new IllegalStateException JavaDoc("This Connection is closed.");
415                     }
416             }
417     }
418
419     protected boolean isStarted()
420     {
421         synchronized(guard)
422             {
423                 return started;
424             }
425     }
426
427     /**
428 Temporarily stops a connection's delivery of incoming messages.
429 Delivery can be restarted using the connection's <CODE>start</CODE>
430 method. When the connection is stopped,
431 delivery to all the connection's message consumers is inhibited:
432 synchronous receives block, and messages are not delivered to message
433 listeners.
434       *
435 <P>This call blocks until receives and/or message listeners in progress
436 have completed.
437       *
438 <P>Stopping a connection has no effect on its ability to send messages.
439 A call to <CODE>stop</CODE> on a connection that has already been
440 stopped is ignored.
441       *
442 <P>A call to <CODE>stop</CODE> must not return until delivery of messages
443 has paused. This means that a client can rely on the fact that none of
444 its message listeners will be called and that all threads of control
445 waiting for <CODE>receive</CODE> calls to return will not return with a
446 message until the
447 connection is restarted. The receive timers for a stopped connection
448 continue to advance, so receives may time out while the connection is
449 stopped.
450
451 <P>If message listeners are running when <CODE>stop</CODE> is invoked,
452 the <CODE>stop</CODE> call must
453 wait until all of them have returned before it may return. While these
454 message listeners are completing, they must have the full services of the
455 connection available to them.
456  
457 @exception JMSException if the JMS provider fails to stop
458                         message delivery due to some internal error.
459       *
460 @see javax.jms.Connection#start
461       */

462     public void stop()
463         throws JMSException JavaDoc
464     {
465         synchronized(guard)
466             {
467                 started = false;
468                 Iterator JavaDoc it = sessionSet.iterator();
469                 while(it.hasNext())
470                     {
471                         SomniSession session = (SomniSession)it.next();
472                         session.stop();
473                     }
474             }
475         SomniLogger.IT.fine(getClientID()+" stopped successfully.");
476     }
477
478     /**
479 Closes the connection.
480       *
481 <P>Since a provider typically allocates significant resources outside
482 the JVM on behalf of a connection, clients should close these resources
483 when they are not needed. Relying on garbage collection to eventually
484 reclaim these resources may not be timely enough.
485       *
486 <P>There is no need to close the sessions, producers, and consumers
487 of a closed connection.
488       *
489 <P>Closing a connection causes all temporary destinations to be
490 deleted.
491       *
492 <P>When this method is invoked, it should not return until message
493 processing has been shut down in an orderly fashion. This means that all
494 message
495 listeners that may have been running have returned, and that all pending
496 receives have returned. A close terminates all pending message receives
497 on the connection's sessions' consumers. The receives may return with a
498 message or with null, depending on whether there was a message available
499 at the time of the close. If one or more of the connection's sessions'
500 message listeners is processing a message at the time when connection
501 <CODE>close</CODE> is invoked, all the facilities of the connection and
502 its sessions must remain available to those listeners until they return
503 control to the JMS provider.
504       *
505 <P>Closing a connection causes any of its sessions' transactions
506 in progress to be rolled back. In the case where a session's
507 work is coordinated by an external transaction manager, a session's
508 <CODE>commit</CODE> and <CODE>rollback</CODE> methods are
509 not used and the result of a closed session's work is determined
510 later by the transaction manager.
511       *
512 Closing a connection does NOT force an
513 acknowledgment of client-acknowledged sessions.
514
515 <P>Invoking the <CODE>acknowledge</CODE> method of a received message
516 from a closed connection's session must throw an
517 <CODE>IllegalStateException</CODE>. Closing a closed connection must
518 NOT throw an exception.
519  
520 @exception JMSException if the JMS provider fails to close the
521                         connection due to some internal error. For
522                         example, a failure to release resources
523                         or to close a socket connection can cause
524                         this exception to be thrown.
525       *
526       */

527     public void close()
528         throws JMSException JavaDoc
529     {
530         synchronized(guard)
531             {
532                 if(closed)
533                     {
534                         SomniLogger.IT.info("Attempting to close "+getClientID()+" which is already closed.");
535                     }
536                 Iterator JavaDoc it = sessionSet.iterator();
537                 while(it.hasNext())
538                     {
539                         SomniSession session = (SomniSession)it.next();
540                         session.close();
541                     }
542                 started = false;
543                 closed = true;
544             }
545         SomniLogger.IT.fine(getClientID()+" closed successfully.");
546     }
547
548       /** Creates a connection consumer for this connection (optional operation).
549       * This is an expert facility not used by regular JMS clients.
550       *
551       * @param destination the destination to access
552       * @param messageSelector only messages with properties matching the
553       * message selector expression are delivered. A value of null or
554       * an empty string indicates that there is no message selector
555       * for the message consumer.
556       * @param sessionPool the server session pool to associate with this
557       * connection consumer
558       * @param maxMessages the maximum number of messages that can be
559       * assigned to a server session at one time
560       *
561       * @return the connection consumer
562       *
563       * @exception JMSException if the <CODE>Connection</CODE> object fails
564       * to create a connection consumer due to some
565       * internal error or invalid arguments for
566       * <CODE>sessionPool</CODE> and
567       * <CODE>messageSelector</CODE>.
568       * @exception InvalidDestinationException if an invalid destination is specified.
569       * @exception InvalidSelectorException if the message selector is invalid.
570       *
571       * @since 1.1
572       * @see javax.jms.ConnectionConsumer
573       */

574
575     public abstract ConnectionConsumer JavaDoc createConnectionConsumer(Destination JavaDoc destination,
576                                                                 String JavaDoc messageSelector,
577                                                                 ServerSessionPool JavaDoc sessionPool,
578                                                                 int maxMessages)
579                                                                 throws JMSException JavaDoc;
580
581
582     /** Create a durable connection consumer for this connection (optional operation).
583       * This is an expert facility not used by regular JMS clients.
584       *
585       * @param topic topic to access
586       * @param subscriptionName durable subscription name
587       * @param messageSelector only messages with properties matching the
588       * message selector expression are delivered. A value of null or
589       * an empty string indicates that there is no message selector
590       * for the message consumer.
591       * @param sessionPool the server session pool to associate with this
592       * durable connection consumer
593       * @param maxMessages the maximum number of messages that can be
594       * assigned to a server session at one time
595       *
596       * @return the durable connection consumer
597       *
598       * @exception JMSException if the <CODE>Connection</CODE> object fails
599       * to create a connection consumer due to some
600       * internal error or invalid arguments for
601       * <CODE>sessionPool</CODE> and
602       * <CODE>messageSelector</CODE>.
603       * @exception InvalidDestinationException if an invalid destination
604       * is specified.
605       * @exception InvalidSelectorException if the message selector is invalid.
606       * @since 1.1
607       * @see javax.jms.ConnectionConsumer
608       */

609
610     public abstract ConnectionConsumer JavaDoc createDurableConnectionConsumer(Topic JavaDoc topic,
611                                                                         String JavaDoc subscriptionName,
612                                                                         String JavaDoc messageSelector,
613                                                                         ServerSessionPool JavaDoc sessionPool,
614                                                                         int maxMessages)
615                                                                         throws JMSException JavaDoc;
616
617 }
618
619 /* Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 David Walend
620 All rights reserved.
621
622 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
623
624 Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
625
626 Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
627
628 Neither the name of the SomnifugiJMS Project, walend.net, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission from David Walend.
629
630 Credits in redistributions in source or binary forms must include a link to http://somnifugi.sourceforge.net .
631
632 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
633 The net.walend.somnifugi.sql92 package is modified code from the openmq project, https://mq.dev.java.net/ , Copyright (c) of Sun, and carries the CDDL license, repeated here: You can obtain a copy of the license at https://glassfish.dev.java.net/public/CDDLv1.0.html. See the License for the specific language governing permissions and limitations under the License.
634
635 =================================================================================
636
637 For more information and the latest version of this software, please see http://somnifugi.sourceforge.net and http://walend.net or email <a HREF="mailto:david@walend.net">david@walend.net</a>.
638  */

639
Popular Tags