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 2000-2004 (C) Exoffice Technologies Inc. All Rights Reserved.
42 *
43 * $Id: JmsConnection.java,v 1.26 2004/01/11 02:33:44 tanderson Exp $
44 *
45 * Date Author Changes
46 * 3/21/2000 jima Created
47 */
48 package org.exolab.jms.client;
49
50
51 import java.util.Enumeration;
52 import java.util.Vector;
53
54 import javax.jms.Connection;
55 import javax.jms.ConnectionMetaData;
56 import javax.jms.ExceptionListener;
57 import javax.jms.IllegalStateException;
58 import javax.jms.JMSException;
59
60 import org.apache.commons.logging.Log;
61 import org.apache.commons.logging.LogFactory;
62
63 import org.exolab.core.service.ServiceException;
64 import org.exolab.jms.events.BasicEventManager;
65
66
67 /***
68 * Client side implementation of the <code>javax.jms.Connection</code>
69 * interface.
70 *
71 * @version $Revision: 1.26 $ $Date: 2004/01/11 02:33:44 $
72 * @author <a href="mailto:jima@exoffice.com">Jim Alateras</a>
73 * @author <a href="mailto:tma@netspace.net.au">Tim Anderson</a>
74 */
75 abstract class JmsConnection implements Connection {
76
77 /***
78 * This flag indicates whether or not the connection is closed. It
79 * is set to false on creation
80 */
81 private boolean _closed = false;
82
83 /***
84 * This flag indicates whether the connection is in the start or
85 * stopped state
86 */
87 private boolean _stopped = true;
88
89 /***
90 * This flag indicates whether the connection has been modified.
91 * If so, subsequent attempts to invoke {@link #setClientID} will
92 * cause an <code>IllegalStateException</code> being thrown
93 */
94 private boolean _modified = false;
95
96 /***
97 * This is the identity associated with the client. The identity is
98 * generated and assigned by the connection factory
99 */
100 private String _clientId = null;
101
102 /***
103 * Gates the setting of the clientId more than once
104 */
105 private boolean _clientIdSet = false;
106
107 /***
108 * A connection can maintain one and only one exception listener, The
109 * listener will be informed of unusual and exception events
110 */
111 private ExceptionListener _exceptionListener = null;
112
113 /***
114 * Manage the list of activated and registered sessions. Everytime a
115 * session is created through this connection it is registered.
116 */
117 private Vector _sessions = new Vector();
118
119 /***
120 * This is a back pointer to the connection factory which created this
121 * connection. It is assigned during object construction
122 */
123 private JmsConnectionFactory _factory = null;
124
125 /***
126 * This is a stub to a remote object on the JMSSever. This abstraction
127 * allows us to use different underlying ORBS.
128 */
129 private JmsConnectionStubIfc _stub = null;
130
131 /***
132 * The id is generated by the server and is unique for each connection
133 * created. This id is then used to generate other ids
134 */
135 private String _connectionId = null;
136
137 /***
138 * The number of connections created
139 */
140 private static volatile int _activeConnections = 0;
141
142 /***
143 * The connection data is immutable at this stage. This enables us to cache
144 * a single copy in memory
145 */
146 private static final JmsConnectionMetaData _metaData =
147 new JmsConnectionMetaData();
148
149 /***
150 * The logger
151 */
152 private static final Log _log = LogFactory.getLog(JmsConnection.class);
153
154
155 /***
156 * Construct a new <code>JmsConnection</code>
157 * <p>
158 * This attempts to establish a connection to the JMS server
159 *
160 * @param factory the connection factory responsible for creating this
161 * @param id the client identity
162 * @param username the client username
163 * @param password the client password
164 * @throws JMSException if a connection cannot be established
165 */
166 protected JmsConnection(JmsConnectionFactory factory, String id,
167 String username, String password)
168 throws JMSException {
169
170 if (factory == null) {
171 throw new IllegalArgumentException("Argument 'factory' is null");
172 }
173 if (id == null) {
174 throw new IllegalArgumentException("Argument 'id' is null");
175 }
176 if (id.trim().length() == 0) {
177 throw new IllegalArgumentException("Argument 'id' is invalid: '"
178 + id + "'");
179 }
180
181 _factory = factory;
182 _clientId = id;
183
184 _stopped = true;
185
186 // use the factory object to retrieve the proxy that
187 // will be used to get a JmsConnectionStubIfc instance
188 // and cache its identity locally
189 _stub = factory.getProxy().createConnection(_clientId, username,
190 password);
191 _connectionId = _stub.getConnectionId();
192
193 // start the event manager
194 startEventManager();
195 }
196
197 // implementation of Connection.getClientID
198 public String getClientID() throws JMSException {
199 ensureOpen();
200 setModified();
201
202 return _clientId;
203 }
204
205
206 // implementation of Connection.setClientID
207 public void setClientID(String id) throws JMSException {
208 ensureOpen();
209
210 // check if the client id has already been set
211 if (_clientIdSet) {
212 throw new IllegalStateException(
213 "The client id has already been set");
214 }
215
216 if (_modified) {
217 throw new IllegalStateException(
218 "The client identifier must be set before any other operation "
219 + "is performed");
220 }
221
222 // gate the client id from being set more than once.
223 _clientId = id;
224 _clientIdSet = true;
225 }
226
227
228 // implementation of Connection.getMetaData
229 public ConnectionMetaData getMetaData() throws JMSException {
230 ensureOpen();
231 setModified();
232 return _metaData;
233 }
234
235 // implementation of Connection.getExceptionListener
236 public ExceptionListener getExceptionListener() throws JMSException {
237 ensureOpen();
238 setModified();
239 return _exceptionListener;
240 }
241
242
243 // implementation of Connection.setExceptionListener
244 public void setExceptionListener(ExceptionListener listener)
245 throws JMSException {
246 ensureOpen();
247 setModified();
248 _exceptionListener = listener;
249 }
250
251 /***
252 * Notify the exception listener of a JMSException. If the exception
253 * listener is not set then ignore it
254 *
255 * @param message message to deliver
256 */
257 public void notifyExceptionListener(JMSException message) {
258 // check the error code
259 if (message.getErrorCode() != null &&
260 message.getErrorCode().equals(
261 JmsErrorCodes.CONNECTION_TO_SERVER_DROPPED)) {
262 // the connection to the server has been dropped so we need to
263 // release all local resources.
264 try {
265 destroy();
266 } catch (JMSException exception) {
267 _log.error(exception.getMessage(), exception);
268 }
269 }
270
271 // finally notify registered exception listener
272 if (_exceptionListener != null) {
273 _exceptionListener.onException(message);
274 }
275 }
276
277 // implementation of Connection.start
278 public synchronized void start() throws JMSException {
279 ensureOpen();
280 setModified();
281
282 try {
283 if (_stopped) {
284 // propagate the start to all the associated sessions. When
285 // that is complete transition the state of the connection to
286 // RUNNING
287 Enumeration sessions = _sessions.elements();
288 while (sessions.hasMoreElements()) {
289 JmsSession session = (JmsSession) sessions.nextElement();
290 session.start();
291 }
292 // set the state of the connection to start
293 _stopped = false;
294 }
295 } catch (JMSException exception) {
296 // do we need to change _stopped to true if the any of the
297 // sessions fail to start
298 throw exception;
299 }
300 }
301
302 // implementation of Connection.stop
303 public synchronized void stop() throws JMSException {
304 ensureOpen();
305 setModified();
306
307 if (!_stopped) {
308 // propagate the stop to all the encapsulated sessions before
309 // changing the state of the connection. Only when all the
310 // sessions have successfully transitioned, without exception,
311 // do we change the state of the connection
312 Enumeration sessions = _sessions.elements();
313 while (sessions.hasMoreElements()) {
314 JmsSession session = (JmsSession) sessions.nextElement();
315 session.stop();
316 }
317
318 // set the state of the connection to stopped before stopping
319 // all the enclosed sessions
320 _stopped = true;
321 }
322 }
323
324 // implementation of Connection.close
325 public synchronized void close() throws JMSException {
326 if (!_closed) {
327 try {
328 // before we close we should stop the connection and any
329 // associated sessions
330 stop();
331
332 // propagate the close to all the encapsulated sessions before
333 // changing the state of the connection. Only when all the
334 // sessions have successfully transitioned, without exception,
335 // do we change the state of the connection. All the sessions
336 // are removed from the connection.
337 Object sessions[] = _sessions.toArray();
338 for (int i = 0; i < sessions.length; ++i) {
339 JmsSession session = (JmsSession) sessions[i];
340 session.close();
341 // the session deregisters itself with the connection via
342 // removeSession()
343 }
344
345 // send a close to the stub and then null the stub
346 getJmsConnectionStub().close();
347 _stub = null;
348
349 // remove yourself from the list of connections managed by the
350 // connection factory and then null the factory.
351 _factory.removeConnection(this);
352 _factory = null;
353
354 // set the closed flag so calling it multiple times is
355 // cool
356 _closed = true;
357 } finally {
358 stopEventManager();
359 }
360 }
361 }
362
363 /***
364 * Release all resources used by this connection, including supporting
365 * sessions
366 *
367 * @throws JMSException - error completing this request
368 */
369 public synchronized void destroy() throws JMSException {
370 if (!_closed) {
371 try {
372 // propagate the close to all the encapsulated sessions before
373 // changing the state of the connection. Only when all the
374 // sessions have successfully transitioned, without exception,
375 // do we change the state of the connection. All the sessions
376 // are removed from the connection.
377 Object sessions[] = _sessions.toArray();
378 for (int i = 0; i < sessions.length; ++i) {
379 JmsSession session = (JmsSession) sessions[i];
380 session.destroy();
381 }
382
383 // send a close to the stub and then null the stub
384 getJmsConnectionStub().destroy();
385 _stub = null;
386
387 // remove yourself from the list of connections managed by the
388 // connection factory and then null the factory.
389 _factory.removeConnection(this);
390 _factory = null;
391
392 // set the closed flag so calling it multiple times is
393 // cool
394 _closed = true;
395 } finally {
396 stopEventManager();
397 }
398 }
399 }
400
401 /***
402 * Return the identity of this connection. An identity is created by the
403 * server and is unique within that servers environment
404 *
405 * @return String
406 */
407 public String getConnectionId() {
408 return _connectionId;
409 }
410
411 /***
412 * Return an instance of the remote server connection. If one has not
413 * been assigned then throw the JMSException exception
414 *
415 * @return JmsConnectionStub connection to the server
416 * @throws JMSException
417 */
418 JmsConnectionStubIfc getJmsConnectionStub() throws JMSException {
419 if (_stub == null) {
420 throw new JMSException("The connectionstub is set to null");
421 }
422
423 return _stub;
424 }
425
426 /***
427 * Add the specified session to the list of managed sessions
428 *
429 * @param session session to register
430 */
431 protected void addSession(JmsSession session) {
432 _sessions.addElement(session);
433 }
434
435 /***
436 * Remove the specified session from the list of managed sessions.
437 * If it doesn't exist then fail silently
438 *
439 * @param session session to remove
440 */
441 protected void removeSession(JmsSession session) {
442 _sessions.removeElement(session);
443 }
444
445 /***
446 * Test whether the specified session is managed by this connection
447 *
448 * @param session session to test against
449 * @return boolean true if managed
450 */
451 protected boolean isManaged(JmsSession session) {
452 return _sessions.contains(session);
453 }
454
455 /***
456 * Returns an enumeration of all sessions managed by this connection
457 *
458 * @return an enumeration of all sessions managed by this connection
459 */
460 protected Enumeration getSessions() {
461 return _sessions.elements();
462 }
463
464 /***
465 * Return the running state of the connection
466 *
467 * @return <code>true</code> if stopped
468 */
469 protected boolean isStopped() {
470 return _stopped;
471 }
472
473 /***
474 * Flags this connection as being modified. Subsequent attempts
475 * to invoke {@link #setClientID} will result in an
476 * <code>IllegalStateException</code> being thrown
477 */
478 protected void setModified() {
479 _modified = true;
480 }
481
482 /***
483 * Delete the temporary destination and all the registered sessions
484 * consumers waiting to receive messages from this destination will
485 * be stopped.
486 * <p>
487 * It will throw a JMSException if the specified destination is not
488 * temporary or if the destination is null or if the destination is
489 * not owned by this connection
490 *
491 * @param destination temporary destination to delete
492 * @throws JMSException
493 */
494 synchronized void deleteTemporaryDestination(JmsDestination destination)
495 throws JMSException {
496 if ((destination != null) &&
497 (destination instanceof JmsTemporaryDestination)) {
498 JmsTemporaryDestination temp_dest =
499 (JmsTemporaryDestination) destination;
500
501 // check to see that this destination was actually created by
502 // this connection
503 if (temp_dest.getOwningConnection() == this) {
504 // this is currently a no-op but we probably need a way to
505 // clean up on the server side
506 } else {
507 throw new JMSException("The temp destination cannot be " +
508 "used outside the scope of the connection creating it");
509 }
510 } else {
511 throw new JMSException("The destination is not temporary");
512 }
513 }
514
515 /***
516 * Verifies that the connection is open
517 *
518 * @throws IllegalStateException if the connection is closed
519 */
520 protected void ensureOpen() throws IllegalStateException {
521 if (_closed) {
522 throw new IllegalStateException(
523 "Cannot perform operation - session has been closed");
524 }
525 }
526
527 /***
528 * This method will start the event manager service on the first connection
529 * that is created
530 */
531 private static void startEventManager() {
532 try {
533 if (_activeConnections++ == 0) {
534 try {
535 BasicEventManager.instance().start();
536 } catch (ServiceException exception) {
537 // ignore
538 }
539 }
540 } catch (Exception exception) {
541 _log.error(exception.getMessage(), exception);
542 }
543 }
544
545 /***
546 * This method will terminate the event manager service when the last
547 * connection has been closed
548 */
549 private static void stopEventManager() {
550 try {
551 if (--_activeConnections == 0) {
552 BasicEventManager.instance().stop();
553 }
554 } catch (Exception exception) {
555 exception.printStackTrace();
556 }
557 }
558
559 } //-- JmsConnection
560
This page was automatically generated by Maven