View Javadoc

1   /* (c) Copyright 2003 Caleigo AB, All rights reserved. 
2    * 
3    * This library is free software; you can redistribute it and/or
4    * modify it under the terms of the GNU Lesser General Public
5    * License as published by the Free Software Foundation; either
6    * version 2.1 of the License, or (at your option) any later version.
7    * 
8    * This library is distributed in the hope that it will be useful,
9    * but WITHOUT ANY WARRANTY; without even the implied warranty of
10   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   * Lesser General Public License for more details.
12   * 
13   * You should have received a copy of the GNU Lesser General Public
14   * License along with this library; if not, write to the Free Software
15   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16   *  
17   */
18  
19  package org.caleigo.toolkit.tunnel;
20  
21  import java.util.*;
22  
23  import org.caleigo.toolkit.log.Log;
24  
25  
26  /***
27   *
28   * @author  Mattias Hagstrand
29   * @version 1.00
30   * 
31   *//* 
32   *
33   * WHEN        WHO               WHY & WHAT
34   * -----------------------------------------------------------------------------
35   * 2002-07-02  Mattias Hagstrand Creation
36   */
37  public class AbstractTunnelServer implements ITunnelServer, ITunnelListener
38  {
39      // Data members ------------------------------------------------------------
40      protected List mTunnels;
41      protected List mMessageConsumers;
42      protected Map mRegisteredCodecs;
43      protected Map mRegisteredPackers;
44      protected boolean mIsInitialized;
45      protected List mTunnelServerListeners;
46          
47      // Constructors ------------------------------------------------------------
48      public AbstractTunnelServer()
49      {
50          mTunnels = new ArrayList();
51          mRegisteredCodecs = new HashMap();
52          mRegisteredPackers = new HashMap();
53          mMessageConsumers = new ArrayList();
54          mTunnelServerListeners = new ArrayList();
55          
56          this.registerCodec(SerializeCodec.class);
57          this.registerPacker(ZipPacker.class);
58          this.registerPacker(CRCPacker.class);
59          this.registerPacker(ScramblePacker.class);
60      }
61      
62      // ITunnelServer implementation --------------------------------------------
63      
64      /***
65       * Initializes the ITunnelServer. This method must be called before connections
66       * can be accepted.
67       */
68      public synchronized void initialize()
69          throws IllegalStateException, TunnelException
70      {
71          if (mIsInitialized)
72              throw new IllegalStateException("The tunnel is initialized");
73          
74          mIsInitialized = true;
75      }
76      
77      /***
78       * Performs cleanup for this ITunnelServer. This involves closing all connections.
79       * No connections can be accepted after this method has been called.
80       */
81      public synchronized void finalize()
82          throws IllegalStateException, TunnelException
83      {
84          if (!mIsInitialized)
85              throw new IllegalStateException("The tunnel is not initialized");
86          
87          for (int i = 0; i < mTunnels.size(); i++)
88              try
89              {
90                  ((ITunnel) mTunnels.get(i)).finalize();
91              }
92              catch (Exception e)
93              {
94                  Log.printError(this, "Coludn't finalize tunnel", e);
95              }
96          mIsInitialized = false;
97      }
98      
99      /***
100      * Registers an ITunnelPacker that can be used when setting up a connection.
101      */
102     public synchronized void registerPacker(Class packer)
103     {
104         if (!ITunnelPacker.class.isAssignableFrom(packer))
105             throw new IllegalArgumentException("Packer is not an ITunnelPacker");
106         try
107         {
108             packer.getConstructor(new Class[0]);
109         }
110         catch (NoSuchMethodException e)
111         {
112             Log.printError(this, "Couldn't find any default constructor for packer", e);
113             throw new IllegalArgumentException("Packer has no default constructor");
114         }
115         mRegisteredPackers.put(packer.getName(), packer);
116     }
117     
118     /***
119      * Registers an ITunnelCodec that can be used when setting up a connection.
120      */
121     public synchronized void registerCodec(Class codec)
122     {
123         if (!ITunnelCodec.class.isAssignableFrom(codec))
124             throw new IllegalArgumentException("Codec is not an ITunnelCodec");
125         try
126         {
127             codec.getConstructor(new Class[0]);
128         }
129         catch (NoSuchMethodException e)
130         {
131             Log.printError(this, "Couldn't find any default constructor for codec", e);
132             throw new IllegalArgumentException("Codec has no default constructor");
133         }
134         mRegisteredCodecs.put(codec.getName(), codec);
135     }
136     
137     /***
138      * Adds the IMessageConsumer that receives messages from all connections that
139      * this ITunnelServer has set up. This is a convenience method. Calling this
140      * method has the same effect as calling addMessageConsumer on all of the
141      * individual ITunnels returned by getTunnels. All IMessageConsumers that have
142      * been added with this method will automatically be added to all new ITunnels
143      * that are create.
144      */
145     public synchronized void addMessageConsumer(IMessageConsumer consumer)
146     {
147         for (int i = 0; i < mTunnels.size(); i++)
148             ((ITunnel) mTunnels.get(i)).addMessageConsumer(consumer);
149         
150         mMessageConsumers.add(consumer);
151     }
152     
153     /***
154      * Removes the IMessageConsumer all connections that this ITunnelServer has
155      * set up. This is a convenience method. Calling this method has the same
156      * effect as calling removeMessageConsumer on all of the individual ITunnels
157      * returned by getTunnels.
158      */
159     public synchronized void removeMessageConsumer(IMessageConsumer consumer)
160     {
161         for (int i = 0; i < mTunnels.size(); i++)
162             ((ITunnel) mTunnels.get(i)).remomveMessageConsumer(consumer);
163         
164         mMessageConsumers.remove(consumer);
165     }
166     
167     /***
168      * Returns the ITunnel with the provided index.
169      */
170     public synchronized ITunnel getTunnel(int index)
171     {
172         if (index >= 0 && index < mTunnels.size())
173             return (ITunnel) mTunnels.get(index);
174         return null;
175     }
176     
177     /***
178      * Returns an Iterator for all ITunnels that this ITunnelServer has created.
179      */
180     public synchronized Iterator getTunnels()
181     {
182         return mTunnels.iterator();
183     }
184     
185     /***
186      * Returns the number of ITunnels that this ITunnelServer has created.
187      */
188     public synchronized int getTunnelCount()
189     {
190         return mTunnels.size();
191     }
192         
193     public void addTunnelServerListener(ITunnelServerListener listener)
194     {
195         mTunnelServerListeners.add(listener);
196     }
197     
198     public void removeTunnelServerListener(ITunnelServerListener listener)
199     {
200         mTunnelServerListeners.remove(listener);
201     }
202     
203     // ITunnelListener implementation ------------------------------------------
204     
205     /***
206      * Invoked when the ITunnel has been closed. This happens on the server side
207      * when the tunnel on the client side is closed.
208      * The source ITunnel is finalized before this method is invoked.
209      */
210     public void tunnelClosed(TunnelEvent event)
211     {
212         this.removeTunnel(event.getSourceTunnel());
213     }
214     
215     /***
216      * Invoked on an ITunnel on the client side when the connection to the server
217      * has been lost.
218      * The source ITunnel is finalized before this method is invoked.
219      */
220     public void connectionLost(TunnelEvent event)
221     {
222         this.removeTunnel(event.getSourceTunnel());
223     }
224     
225     // Access methods ----------------------------------------------------------
226     protected synchronized void addTunnel(ITunnel tunnel)
227     {
228         Log.print(this, "Tunnel added");
229         
230         mTunnels.add(tunnel);
231         tunnel.addTunnelListener(this);
232         
233         for (int i = 0; i < mMessageConsumers.size(); i++)
234             tunnel.addMessageConsumer((IMessageConsumer) mMessageConsumers.get(i));
235         
236         this.fireTunnelAddedEvent(tunnel);
237     }
238     
239     protected synchronized void removeTunnel(ITunnel tunnel)
240     {
241         Log.print(this, "Tunnel removed");
242         
243         if (mTunnels.contains(tunnel))
244         {
245             mTunnels.remove(tunnel);
246             this.fireTunnelRemovedEvent(tunnel);
247         }
248     }
249     
250     protected synchronized ITunnelCodec getCodec(String codecClassName)
251     {
252         ITunnelCodec codec = null;
253         Class codecClass = (Class) mRegisteredCodecs.get(codecClassName);
254         if (codecClass == null)
255             return null;
256         
257         try
258         {
259             codec = (ITunnelCodec) codecClass.newInstance();
260         }
261         catch (Exception e)
262         {
263             Log.printError(this, "Couldn't create codec instance", e);
264             throw new IllegalStateException("Couldn't create codec instance");
265         }
266         
267         return codec;
268     }
269     
270     protected synchronized ITunnelPacker getPacker(String packerClassName)
271     {
272         ITunnelPacker packer = null;
273         Class packerClass = (Class) mRegisteredPackers.get(packerClassName);
274         if (packerClass == null)
275             return null;
276         
277         try
278         {
279             packer = (ITunnelPacker) packerClass.newInstance();
280         }
281         catch (Exception e)
282         {
283             Log.printError(this, "Couldn't create packer instance", e);
284             throw new IllegalStateException("Couldn't create packer instance");
285         }
286         
287         return packer;
288     }
289     
290     // Help methods ------------------------------------------------------------
291     protected void fireTunnelAddedEvent(ITunnel tunnel)
292     {
293         TunnelServerEvent event = new TunnelServerEvent(this, TunnelServerEvent.TUNNEL_ADDED, tunnel);
294         for (int i = 0; i < mTunnelServerListeners.size(); i++)
295             ((ITunnelServerListener) mTunnelServerListeners.get(i)).tunnelAdded(event);
296     }
297     
298     protected void fireTunnelRemovedEvent(ITunnel tunnel)
299     {
300         TunnelServerEvent event = new TunnelServerEvent(this, TunnelServerEvent.TUNNEL_REMOVED, tunnel);
301         for (int i = 0; i < mTunnelServerListeners.size(); i++)
302             ((ITunnelServerListener) mTunnelServerListeners.get(i)).tunnelRemoved(event);
303     }
304     
305     /***
306      * Try to set up the provided ITunnel based on the information contined
307      * in the connection message. If the tunnel was set up successfully it will
308      * be added to this tunnel server. The invoker of this method is responsible
309      * for calling initialize on the tunnel.
310      *
311      * @return  a status message that is to be returned by the invoker of this method.
312      */
313     protected void setUpTunnel(Object connectionMessage, ITunnel tunnel)
314         throws TunnelException
315     {
316         try
317         {
318             if (connectionMessage instanceof AbstractTunnel.IConnectionMessage)
319             {
320                 // Set up tunnel
321                 ITunnelCodec codec = this.getCodec(((AbstractTunnel.IConnectionMessage) connectionMessage).getCodecClassName());
322                 String[] packerClassNames = ((AbstractTunnel.IConnectionMessage) connectionMessage).getPackerClassNames();
323                 ITunnelPacker[] packers = new ITunnelPacker[packerClassNames.length];
324                 for (int i = 0; i < packers.length; i++)
325                     packers[i] = this.getPacker(packerClassNames[i]);
326 
327                 if (codec != null)
328                 {
329                     tunnel.setCodec(codec);
330                     
331                     // Try to find all packers and initialize the tunnels packers
332                     for (int i = 0; i < packers.length; i++)
333                         if (packers[i] != null)
334                             tunnel.addPacker(packers[i]);
335                         else
336                             throw new TunnelException("Unrecongnized packer: " + packerClassNames[i]);
337 
338                     // Add the tunnel
339                     addTunnel(tunnel);
340                 }
341                 else
342                     throw new TunnelException("Unrecongnized codec: " + ((AbstractTunnel.IConnectionMessage) connectionMessage).getCodecClassName());
343             }
344             else
345                 throw new TunnelException("Invalid format of connection message");
346         }
347         catch (Exception e)
348         {
349             Log.printError(this, "Couldn't set up tunnel", e);
350             throw new TunnelException("Couldn't set up tunnel: " + e.getMessage());
351         }
352     }
353     
354     // Nested classes 
355 }