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-2004 (C) Exoffice Technologies Inc. All Rights Reserved.
42   *
43   * $Id: IpcJmsServerStub.java,v 1.19 2004/01/20 14:11:36 tanderson Exp $
44   */
45  
46  package org.exolab.jms.client.mipc;
47  
48  import java.io.IOException;
49  import java.util.HashSet;
50  import java.util.Hashtable;
51  import java.util.Vector;
52  
53  import javax.jms.ExceptionListener;
54  import javax.jms.JMSException;
55  import javax.jms.JMSSecurityException;
56  
57  import org.apache.commons.logging.Log;
58  import org.apache.commons.logging.LogFactory;
59  
60  import org.exolab.core.ipc.IpcIfc;
61  import org.exolab.core.mipc.DisconnectionEventListener;
62  import org.exolab.core.mipc.MultiplexConnection;
63  import org.exolab.core.mipc.MultiplexConnectionIfc;
64  import org.exolab.core.mipc.ObjectChannel;
65  import org.exolab.core.mipc.TcpConstants;
66  import org.exolab.jms.client.JmsConnectionStubIfc;
67  import org.exolab.jms.client.JmsErrorCodes;
68  import org.exolab.jms.client.JmsServerStubIfc;
69  
70  
71  /***
72   * This class is repsonsible for opening an ipc connection to the server
73   * and creating IpcJmsConnections.
74   *
75   * @version     $Revision: 1.19 $ $Date: 2004/01/20 14:11:36 $
76   * @author      <a href="mailto:mourikis@exolab.org">Jim Mourikis</a>
77   * @see         org.exolab.jms.client.mipc.IpcJmsConnectionStub
78   * @see         org.exolab.core.mipc.DisconnectionEventListener
79   **/
80  public class IpcJmsServerStub
81      implements JmsServerStubIfc, DisconnectionEventListener {
82  
83      /***
84       * The multiplex connection to the server
85       */
86      private MultiplexConnectionIfc _mc = null;
87  
88      /***
89       * The server channel
90       */
91      private IpcIfc _connection = null;
92  
93      /***
94       * The server host address
95       */
96      private String _serverAddress = TcpConstants.LOCAL_HOST;
97  
98      /***
99       * The internal server host address if OpenJMS server is serving both
100      * internet connections behind a NAT router and internal connections.
101      */
102     private String _internalServerAddress = null;
103 
104     /***
105      * The port number the server is listening to
106      */
107     private int _port = TcpConstants.DEFAULT_PORT;
108 
109     /***
110      * The set of open IpcJmsConnectionStub instances
111      */
112     private HashSet _connections = new HashSet();
113 
114     /***
115      * The message dispatcher
116      */
117     private IpcJmsMessageListener _listener;
118 
119     /***
120      * The exception listener is used to communicate server connection
121      * problems
122      */
123     private ExceptionListener _exceptionListener = null;
124 
125     /***
126      * The logger
127      */
128     private static final Log _log = LogFactory.getLog(IpcJmsServerStub.class);
129 
130 
131     /***
132      * Default constructor for serialization
133      */
134     public IpcJmsServerStub() {
135     }
136 
137     /***
138      * The constructor has a collection of environment variables
139      * which it uses to construct a connection to the remote
140      * server
141      *
142      * @param           env
143      */
144     public IpcJmsServerStub(Hashtable env) {
145         if (env.containsKey(IpcJmsConstants.IPC_SERVER_HOST)) {
146             _serverAddress = (String) env.get(IpcJmsConstants.IPC_SERVER_HOST);
147         }
148         if (env.containsKey(IpcJmsConstants.IPC_INTERNAL_SERVER_HOST)) {
149             _internalServerAddress = (String) env.get
150                 (IpcJmsConstants.IPC_INTERNAL_SERVER_HOST);
151         }
152 
153         if (env.containsKey(IpcJmsConstants.IPC_SERVER_PORT)) {
154             try {
155                 _port = Integer.parseInt((String) env.get(
156                     IpcJmsConstants.IPC_SERVER_PORT));
157             } catch (NumberFormatException exception) {
158                 // failure to convert exception use the
159                 // default port
160                 _log.warn("Server port is not a valid format."
161                           + " Defaulting to " + _port);
162             }
163         }
164     }
165 
166     /***
167      * Instantiate an instance of this stub with the specified host and
168      * port numbers
169      *
170      * @param       host                server host address
171      * @param       port                server port number
172      */
173     public IpcJmsServerStub(String host, int port) {
174         if ((host != null) &&
175             (host.length() > 0)) {
176             _serverAddress = host;
177         }
178 
179         _port = port;
180     }
181 
182     /***
183      * Create a connection to the JMS Server. A new connectionId will be
184      * returned if the connection is successful. Create a new IpcJmsConnection
185      * object with the given id's.
186      *
187      * @param clientId the identity of client
188      * @param username the client username
189      * @param password the client password
190      * @throws JMSException
191      */
192     public synchronized JmsConnectionStubIfc createConnection(
193         String clientId, String username, String password)
194         throws JMSException {
195         JmsConnectionStubIfc stub = null;
196 
197         openConnection();
198         try {
199             String connectionId;
200             Vector v = pack("createConnection", clientId, username, password);
201             synchronized (_connection) {
202                 _connection.send(v);
203                 connectionId = (String) checkReply("createConnection");
204             }
205 
206             stub = new IpcJmsConnectionStub(
207                 this, _connection, clientId, connectionId, _listener);
208             _connections.add(stub);
209         } catch (JMSException exception) {
210             throw exception;
211         } catch (Exception exception) {
212             JMSException error = new JMSException(
213                 "Failed to create connection");
214             error.setLinkedException(exception);
215             throw error;
216         }
217         return stub;
218     }
219 
220     /***
221      * Set the server host. This is the host that the server runs on
222      *
223      * @param host the name of the host
224      */
225     public void setServerAddress(String host) {
226         _serverAddress = host;
227     }
228 
229     /***
230      * Returns the server host
231      *
232      * @return the server host
233      */
234     public String getServerAddress() {
235         return _serverAddress;
236     }
237 
238     /***
239      * Set the server port. This is the port that the server runs on
240      *
241      * @param port the server port number
242      */
243     public void setServerPort(int port) {
244         _port = port;
245     }
246 
247     /***
248      * Returns the server port
249      *
250      * @return the server port
251      */
252     public int getServerPort() {
253         return _port;
254     }
255 
256     // implementation of DisconnectionEventListener.disconnected
257     public void disconnected(MultiplexConnectionIfc connection) {
258         close();
259 
260         // notify the exception listener if one is registered
261         if (_exceptionListener != null) {
262             JMSException exception = new JMSException(
263                 "Connection to server terminated",
264                 JmsErrorCodes.CONNECTION_TO_SERVER_DROPPED);
265             _exceptionListener.onException(exception);
266         }
267     }
268 
269     // implementation of JmsServerStubIfc.setExceptionListener
270     public void setExceptionListener(ExceptionListener listener) {
271         _exceptionListener = listener;
272     }
273 
274     public synchronized void close() {
275         // local clean up
276         try {
277             if (_connection != null) {
278                 _connection.close();
279                 _listener.stop();
280                 _mc.finish();
281             }
282         } catch (Exception exception) {
283             // just swallow :-)
284         } finally {
285             _mc = null;
286             _connection = null;
287             _listener = null;
288         }
289     }
290 
291     public synchronized void closed(IpcJmsConnectionStub connection) {
292         _connections.remove(connection);
293         if (_connections.isEmpty()) {
294             close();
295         }
296     }
297 
298     /***
299      * Create a multiplexed connection to the server
300      *
301      * @param serverAddress the address of the server
302      * @param internalServerAddress internal server address. May be null.
303      * @param port the port number to use
304      * @return the connection to the server
305      * @throws IOException if the connection cannot be established
306      */
307     protected MultiplexConnectionIfc createClientConnection(
308         String serverAddress, String internalServerAddress, int port)
309         throws Exception {
310 
311         MultiplexConnectionIfc result = null;
312 
313         try {
314             result = new MultiplexConnection(serverAddress, port);
315         } catch (IOException exception) {
316             if (internalServerAddress != null) {
317                 result = new MultiplexConnection(internalServerAddress, port);
318             } else {
319                 // just rethrow it.
320                 throw exception;
321             }
322         }
323         return result;
324     }
325 
326     /***
327      * Check to see if an ipc connection has been created. If not, connect with
328      * the given hostname and port number.
329      *
330      * @throws JMSException if a connection cannot be established
331      */
332     private synchronized void openConnection() throws JMSException {
333         if (_mc == null) {
334             try {
335                 _mc = createClientConnection(
336                     _serverAddress, _internalServerAddress, _port);
337             } catch (Exception exception) {
338                 throw new JMSException(
339                     "Failed to create connection: " + exception);
340             }
341 
342             _mc.setDisconnectionEventListener(this);
343             ((Thread) _mc).start();
344 
345             _connection = new ObjectChannel("server", _mc);
346             _listener = new IpcJmsMessageListener(_mc);
347         }
348     }
349 
350     /***
351      * A convenience method to check the success of operations which return
352      * a true on sucess.
353      *
354      * @param method The requested server function.
355      * @return the result of the call, or <code>null</code> if the call
356      * didn't return a result
357      * @throws JMSException for any failure.
358      */
359     private Object checkReply(String method) throws JMSException {
360         Object result = null;
361         Vector v = null;
362         try {
363             v = (Vector) _connection.receive();
364         } catch (Exception err) {
365             // rethrow as a JMSException
366             throw new JMSException("Operation " + method + " failed: " + err);
367         }
368 
369         if (v != null) {
370             Boolean b = (Boolean) v.get(0);
371             if (!b.booleanValue()) {
372                 if (v.get(1) instanceof JMSException) {
373                     throw (JMSException) v.get(1);
374                 } else {
375                     throw new JMSException("Operation " + method +
376                         " failed:\n" + v.get(1));
377                 }
378             }
379             result = v.get(1);
380         } else {
381             throw new JMSException("Unknown connection error for " + method);
382         }
383 
384         return result;
385     }
386 
387     /***
388      * Pack all the data that is required by the server in a vector.
389      * Set the size of the vector to be exactly the right size for efficiency.
390      *
391      * @param method the function to invoke on the server.
392      * @param id the unique client id.
393      * @param username the client username
394      * @param password the client password
395      * @return Vector The vector containing all the data.
396      */
397     private Vector pack(String method, String id, String username,
398                         String password) {
399         Vector v = new Vector(5);
400         v.add("org.exolab.jms.server.mipc.IpcJmsServerConnection");
401         v.add(method);
402         v.add(id);
403         v.add(username);
404         v.add(password);
405         return v;
406     }
407 
408 }