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 2000-2003 (C) Exoffice Technologies Inc. All Rights Reserved.
42   *
43   * $Id: RmiJmsConnectionStub.java,v 1.20 2003/08/07 13:32:54 tanderson Exp $
44   *
45   * Date         Author  Changes
46   * 04/12/2000    jima    Created
47   */
48  package org.exolab.jms.client.rmi;
49  
50  import java.rmi.ConnectException;
51  import java.rmi.RemoteException;
52  
53  import javax.jms.JMSException;
54  
55  import org.apache.commons.logging.Log;
56  import org.apache.commons.logging.LogFactory;
57  
58  import org.exolab.jms.client.JmsConnectionStubIfc;
59  import org.exolab.jms.client.JmsErrorCodes;
60  import org.exolab.jms.client.JmsSessionStubIfc;
61  import org.exolab.jms.server.rmi.RemoteJmsServerConnectionIfc;
62  import org.exolab.jms.server.rmi.RemoteJmsServerSessionIfc;
63  
64  
65  /***
66   * This class is repsonsible for returning a reference to a remote JMS
67   * connection. If it cannot access get a remote connection then the constructor
68   * will fail with a JMSException
69   *
70   * @version     $Revision: 1.20 $ $Date: 2003/08/07 13:32:54 $
71   * @author      <a href="mailto:jima@exoffice.com">Jim Alateras</a>
72   */
73  public class RmiJmsConnectionStub
74      implements JmsConnectionStubIfc {
75  
76      /***
77       * This is a reference to the remote connection stub that is constructed
78       * during object initialisation.
79       */
80      protected volatile RemoteJmsServerConnectionIfc _delegate = null;
81  
82      /***
83       * This is a reference to the server stub that created this connection
84       */
85      protected RmiJmsServerStub _owner = null;
86  
87      /***
88       * Cache a reference to the pinging thread. This thread is used to
89       * determine whether the server is still active
90       */
91      private PingThread _pinger = null;
92  
93      /***
94       * System property to override the max retries for failed rmi requests.
95       * The default is 10
96       */
97      public final String MAX_RETRY_PROP = "org.exolab.jms.rmi.retryCount";
98  
99      /***
100      * System property, which specifies the interval between successive
101      * retries.
102      * The value is specified in milliseconds and defaults to 100ms
103      */
104     public final String RETRY_INTERVAL_PROP =
105         "org.exolab.jms.rmi.retryInterval";
106 
107     /***
108      * The logger
109      */
110     private static final Log _log =
111         LogFactory.getLog(RmiJmsConnectionStub.class);
112 
113 
114     /***
115      * Instantiate an instance of this class with the specified remote object.
116      * This object is a delegate for all other requests. If a null connection
117      * is specified then throw a JMSException exception
118      *
119      * @param       RemoteJmsServerConnectionIfc
120      * @param       pingInterval            interval between client pings
121      * @param       server                  the server creating the connection
122      * @throws      JMSException
123      */
124     public RmiJmsConnectionStub(RemoteJmsServerConnectionIfc connection,
125                                 int pingInterval, RmiJmsServerStub server)
126         throws JMSException {
127 
128         if (connection != null) {
129             _owner = server;
130             _delegate = connection;
131 
132             // this is used to determine whether or not the server is
133             // active
134             if (pingInterval > 0) {
135                 (_pinger = new PingThread(pingInterval)).start();
136             }
137         } else {
138             throw new JMSException("Cannot instantiate with a null " +
139                 "connection");
140         }
141     }
142 
143     // implementation of JmsConnectionStubIfc.createSession
144     public JmsSessionStubIfc createSession(int ackMode, boolean transacted)
145         throws JMSException {
146         RmiJmsSessionStub stub = null;
147         try {
148             RemoteJmsServerSessionIfc session = _delegate.createSession(
149                 ackMode, transacted);
150             stub = new RmiJmsSessionStub(session);
151             session.setMessageListener(stub);
152 
153         } catch (RemoteException exception) {
154             // rethrow as a JMSException
155             throw new JMSException("Failed to createSession  " + exception);
156         }
157 
158         return stub;
159     }
160 
161     // implementation of JmsConnectionStubIfc.close
162     public void close() throws JMSException {
163         // stop the pinging thread
164         if (_pinger != null) {
165             _pinger.close();
166         }
167 
168         // close the delegate
169         try {
170             _delegate.close();
171             _delegate = null;
172         } catch (RemoteException exception) {
173             // rethrow as a JMSException
174             throw new JMSException("Failed to close  " + exception);
175         }
176     }
177 
178     // implementation of JmsConnectionStubIfc.getConnectionId
179     public String getConnectionId() throws JMSException {
180         try {
181             return _delegate.getConnectionId();
182         } catch (RemoteException exception) {
183             // rethrow as a JMSException
184             throw new JMSException("Failed to getConnectionId  " + exception);
185         }
186     }
187 
188     // implementation of JmsConnectionStubIfc.destroy
189     public void destroy() {
190         if (_pinger != null) {
191             _pinger.close();
192         }
193         _delegate = null;
194         _owner = null;
195     }
196 
197     /***
198      * This thread is used to send ping requests to the server. The server uses
199      * these requests to determine whether a client has disconnected.
200      * The frequency of these requests is determined by the server.
201      */
202     private class PingThread extends Thread {
203 
204         /***
205          * Maintains the interval at which ping requests are generated to the
206          * server. The value is in seconds
207          */
208         private int _interval;
209 
210         /***
211          * The maximum no. of times to retry failed pings
212          */
213         private int _retries;
214 
215         /***
216          * The interval between retrying failed pings
217          */
218         private int _retryInterval;
219 
220         /***
221          * Determines if the thread should stop
222          */
223         private volatile boolean _stop = false;
224 
225 
226         /***
227          * Instantiate a daemon thread that will be used to periodically send
228          * ping requests to the server at the specified interval
229          *
230          * @param interval the interval between ping requests in seconds
231          */
232         PingThread(int interval) {
233             _interval = interval;
234             setName("PingThread-" + Math.abs(hashCode()));
235             setDaemon(true);
236 
237             _retries = getProperty(MAX_RETRY_PROP, 10, 1);
238             _retryInterval = getProperty(RETRY_INTERVAL_PROP, 100, 100);
239         }
240 
241         /***
242          * This will run forever generating ping requests to the clients
243          */
244         public void run() {
245             while (!_stop) {
246                 if (ping()) {
247                     // sleep for the specified interval
248                     try {
249                         Thread.currentThread().sleep(_interval * 1000);
250                     } catch (InterruptedException ignore) {
251                     }
252                 } else {
253                     // the server is no longer available so notify the
254                     // client
255                     try {
256                         JMSException error = new JMSException(
257                             "Connection to server terminated",
258                             JmsErrorCodes.CONNECTION_TO_SERVER_DROPPED);
259                         _log.warn("Server is not responding. "
260                             + "Generating onException",
261                             error);
262                         _owner.getExceptionListener().onException(error);
263                     } catch (Throwable ignore) {
264                     }
265                     break;
266                 }
267             }
268         }
269 
270         public void close() {
271             _stop = true;
272             try {
273                 interrupt();
274             } catch (SecurityException ignore) {
275             }
276         }
277 
278         private boolean ping() {
279             boolean successful = false;
280 
281             // send the ping request. We need to wrap this up in a
282             // retry loop to cater for a ConnectException that happens
283             // when the server is busy or out of resources.
284             RemoteJmsServerConnectionIfc delegate = _delegate;
285             if (delegate != null) {
286                 int count = 0;
287                 while ((count < _retries) && !successful && !_stop) {
288                     try {
289                         delegate.ping();
290                         successful = true;
291                     } catch (RemoteException exception) {
292                         _log.warn("Failed to ping openjms server. "
293                             + "Retry count=" + count, exception);
294                         if ((exception.detail instanceof ConnectException) &&
295                             (count < _retries - 1)) {
296                             // underlying exception is connection
297                             // related...let's retry the request after sleeping
298                             try {
299                                 Thread.sleep(_retryInterval);
300                             } catch (InterruptedException ignore) {
301                             }
302                         }
303                     } catch (Throwable exception) {
304                         // print the exception but continue
305                         _log.warn("Exception pinging server", exception);
306                     }
307                     ++count;
308                 }
309             }
310             return successful;
311         }
312 
313         private int getProperty(String name, int defaultValue, int minimum) {
314             int result = defaultValue;
315             String value = null;
316             try {
317                 value = System.getProperty(name);
318             } catch (SecurityException ignore) {
319             }
320 
321             if (value != null) {
322                 try {
323                     result = Integer.parseInt(value);
324                     if (result <= minimum) {
325                         result = minimum;
326                     }
327                 } catch (NumberFormatException ignore) {
328                     // keep the default
329                 }
330             }
331             return result;
332         }
333 
334     } //-- PingThread
335 
336 } //-- RmiJmsConnectionStub