View Javadoc

1   /**********************************************************************
2    * ConnectionHeader.java
3    * created on 04.03.2005 by netseeker
4    * $Source: /cvsroot/ejoe/EJOE/src/de/netseeker/ejoe/ConnectionHeader.java,v $
5    * $Date: 2007/03/22 21:01:35 $
6    * $Revision: 1.35 $
7    *
8    * ====================================================================
9    *
10   *  Copyright 2005-2006 netseeker aka Michael Manske
11   *
12   *  Licensed under the Apache License, Version 2.0 (the "License");
13   *  you may not use this file except in compliance with the License.
14   *  You may obtain a copy of the License at
15   *
16   *      http://www.apache.org/licenses/LICENSE-2.0
17   *
18   *  Unless required by applicable law or agreed to in writing, software
19   *  distributed under the License is distributed on an "AS IS" BASIS,
20   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21   *  See the License for the specific language governing permissions and
22   *  limitations under the License.
23   * ====================================================================
24   *
25   * This file is part of the ejoe framework.
26   * For more information on the author, please see
27   * <http://www.manskes.de/>.
28   *
29   *********************************************************************/
30  package de.netseeker.ejoe;
31  
32  import java.io.Serializable;
33  import java.nio.ByteBuffer;
34  import java.nio.channels.SocketChannel;
35  import java.text.ParseException;
36  
37  import de.netseeker.ejoe.cache.ByteBufferAllocator;
38  import de.netseeker.ejoe.io.IOUtil;
39  
40  /***
41   * A simple connection header contining informations about compression and blocking/non-blocking io features. The header
42   * bits are structured as follows:
43   * |compression|nio|persistent|http|directMode|mixedMode|useAdapter|canHandleServerHandshakeResponse|
44   * 
45   * @author netseeker
46   * @since 0.3.0
47   */
48  public class ConnectionHeader implements Serializable
49  {
50      private static final long       serialVersionUID  = 1L;
51  
52      private boolean[]               _header;
53  
54      private transient ByteBuffer    _waitingBuffer;
55  
56      private transient SocketChannel _channel;
57  
58      private transient Object        _attachment;
59  
60      private transient Object        _attachementInfo;
61  
62      private int                     _compressionLevel = EJConstants.DEFAULT_COMPRESSION_LEVEL;
63  
64      private String                  _adapterClass;
65  
66      private String                  _host;
67  
68      private boolean                 _isClient;
69  
70      private boolean                 _isConnected;
71  
72      /***
73       * Creates a new instance of ConnectionHeader.
74       */
75      public ConnectionHeader(boolean isClient)
76      {
77          this( null, isClient );
78      }
79  
80      /***
81       * Creates a new instance of ConnectionHeader. The host member will be set to the given host.
82       * 
83       * @param host the host string in the form IP address : port, eg. 127.0.0.1:12577
84       */
85      public ConnectionHeader(String host, boolean isClient)
86      {
87          setHost( host );
88          _header = new boolean[] { false, true, true, false, false, false, false, true };
89          this._isClient = isClient;
90      }
91  
92      /***
93       * Creates a new instance of ConnectionHeader. The host member will be set to the given host and the channel member
94       * will be set to given channel.
95       * 
96       * @param channel The channel to which this connection header applies.
97       * @param host the host string in the form IP address : port, eg. 127.0.0.1:12577
98       */
99      public ConnectionHeader(SocketChannel channel, String host, boolean isClient)
100     {
101         this( host, isClient );
102         this._channel = channel;
103     }
104 
105     /***
106      * Creates a new instance of ConnectionHeader. The host member will be set to the given host and the channel member
107      * will be set to given channel.
108      * 
109      * @param channel The channel to which this connection header applies.
110      * @param host the host string in the form IP address : port, eg. 127.0.0.1:12577
111      * @param header The byte representing the settings used for:
112      *            <ul>
113      *            <li>has compression</li>
114      *            <li>has non blocking io</li>
115      *            <li>is connected</li>
116      *            <li>use persistent connection</li>
117      *            </ul>
118      */
119     public ConnectionHeader(SocketChannel channel, String host, boolean isClient, byte header)
120     {
121         this( channel, host, isClient );
122         fromByte( header );
123     }
124 
125     /***
126      * @param channel
127      * @param host
128      * @param isClient
129      * @param header
130      */
131     private ConnectionHeader(String host, boolean isClient, final boolean[] header)
132     {
133         this( null, host, isClient );
134         this._header = header;
135     }
136 
137     /***
138      * @return
139      */
140     public boolean hasCompression()
141     {
142         return _header[0];
143     }
144 
145     /***
146      * @param enable
147      */
148     public void setCompression( boolean enable )
149     {
150         _header[0] = enable;
151     }
152 
153     /***
154      * @param level
155      */
156     public void setCompressionLevel( int level )
157     {
158         this._compressionLevel = level;
159     }
160 
161     /***
162      * @return
163      */
164     public int getCompressionLevel()
165     {
166         return this._compressionLevel;
167     }
168 
169     /***
170      * @return
171      */
172     public boolean hasNonBlockingReadWrite()
173     {
174         return _header[1];
175     }
176 
177     /***
178      * @param enable
179      */
180     public void setNonBlockingReadWrite( boolean enable )
181     {
182         _header[1] = enable;
183     }
184 
185     /***
186      * @return
187      */
188     public boolean isPersistent()
189     {
190         return _header[2];
191     }
192 
193     /***
194      * @param enable
195      */
196     public void setPersistent( boolean enable )
197     {
198         _header[2] = enable;
199     }
200 
201     /***
202      * @return
203      */
204     public boolean isHttp()
205     {
206         return _header[3];
207     }
208 
209     /***
210      * @param http
211      */
212     public void setHttp( boolean enable )
213     {
214         _header[3] = enable;
215     }
216 
217     /***
218      * @return
219      */
220     public boolean isDirect()
221     {
222         return _header[4];
223     }
224 
225     /***
226      * @param enable
227      */
228     public void setIsDirect( boolean enable )
229     {
230         _header[4] = enable;
231     }
232 
233     public boolean isMixed()
234     {
235         return _header[5];
236     }
237 
238     public void setIsMixed( boolean enable )
239     {
240         _header[5] = enable;
241     }
242 
243     /***
244      * @return
245      */
246     public boolean hasAdapter()
247     {
248         return _header[6];
249     }
250 
251     /***
252      * @param name
253      */
254     public void setAdapterName( String name )
255     {
256         this._header[6] = !(name == null || name.length() == 0);
257         this._adapterClass = name;
258     }
259 
260     /***
261      * @return
262      */
263     public boolean isHandshakeResponseAware()
264     {
265         return _header[7];
266     }
267 
268     /***
269      * @param enable
270      */
271     public void setIsHandshakeResponseAware( boolean enable )
272     {
273         _header[7] = enable;
274     }
275 
276     /***
277      * @return
278      */
279     public String getAdapterName()
280     {
281         return this._adapterClass;
282     }
283 
284     /***
285      * @return
286      */
287     public boolean isConnected()
288     {
289         return _isConnected;
290     }
291 
292     /***
293      * @param enable
294      */
295     public void setConnected( boolean enable )
296     {
297         _isConnected = enable;
298     }
299 
300     /***
301      * @return
302      */
303     public boolean hasWaitingBuffer()
304     {
305         return this._waitingBuffer != null; // && this._waitingBuffer.remaining() > 0;
306     }
307 
308     /***
309      * @return
310      */
311     public ByteBuffer getWaitingBuffer()
312     {
313         return this._waitingBuffer;
314     }
315 
316     /***
317      * @param buf
318      */
319     public void setWaitingBuffer( ByteBuffer buf )
320     {
321         this._waitingBuffer = buf;
322     }
323 
324     /***
325      * 
326      */
327     public void releaseWaitingBuffer()
328     {
329         if ( this._waitingBuffer != null )
330         {
331             this._waitingBuffer.clear();
332             this._waitingBuffer = null;
333         }
334     }
335 
336     /***
337      * @return
338      */
339     public boolean hasAttachment()
340     {
341         return this._attachment != null;
342     }
343 
344     /***
345      * @param attachment
346      */
347     public void setAttachment( Object attachment )
348     {
349         this._attachment = attachment;
350     }
351 
352     /***
353      * @param attachment
354      * @param attachmentInfo
355      */
356     public void setAttachment( Object attachment, Object attachmentInfo )
357     {
358         this._attachment = attachment;
359         this._attachementInfo = attachmentInfo;
360     }
361 
362     /***
363      * @return
364      */
365     public boolean isClient()
366     {
367         return this._isClient;
368     }
369 
370     /***
371      * @return
372      */
373     public Object getAttachment()
374     {
375         return this._attachment;
376     }
377 
378     /***
379      * @return
380      */
381     public Object getAttachementInfo()
382     {
383         return this._attachementInfo;
384     }
385 
386     /***
387      * 
388      */
389     public void releaseAttachment()
390     {
391         this._attachment = null;
392         this._attachementInfo = null;
393     }
394 
395     /***
396      * @param channel
397      */
398     public void setChannel( SocketChannel channel )
399     {
400         this._channel = channel;
401     }
402 
403     /***
404      * @return
405      */
406     public SocketChannel getChannel()
407     {
408         return this._channel;
409     }
410 
411     /***
412      * @return
413      */
414     public String getHost()
415     {
416         return this._host;
417     }
418 
419     /***
420      * @param host
421      */
422     public void setHost( String host )
423     {
424         this._host = host;
425     }
426 
427     /***
428      * Returns a byte representation of the eight most important bits of the connection header
429      * 
430      * @return a byte containing the eight most important bits of the connection header
431      */
432     public byte toByte()
433     {
434         return IOUtil.bBitsToByte( _header );
435     }
436 
437     /***
438      * sets the eight most important bits of the connection header based on the bits contained in the given byte
439      * 
440      * @param header
441      */
442     public void fromByte( byte header )
443     {
444         _header = IOUtil.byteToBBits( header );
445     }
446 
447     /*
448      * (non-Javadoc)
449      * 
450      * @see java.lang.Object#toString()
451      */
452     public String toString()
453     {
454         StringBuffer buf = new StringBuffer();
455         buf.append( "/" );
456         buf.append( IOUtil.bBitsToSBits( _header ) );
457 
458         if ( hasAdapter() )
459         {
460             buf.append( "/" );
461             buf.append( getAdapterName().replaceAll( "//.", "/" ) );
462         }
463 
464         return buf.toString();
465     }
466 
467     /***
468      * Extracts the header settings from a formatted header string which follows the mask:
469      * 
470      * <pre>
471      *        header=12345678[&amp;adapter=somepackage.someadapter]
472      * </pre>
473      * 
474      * @param header
475      */
476     public void fromString( String header ) throws ParseException
477     {
478         String[] parts = null;
479         if ( header.startsWith( "/" ) )
480         {
481             parts = header.substring( 1 ).split( "/", 2 );
482         }
483         else
484         {
485             parts = header.split( "/", 2 );
486         }
487 
488         if ( parts.length >= 1 )
489         {
490             if ( parts[0].length() != 8 )
491             {
492                 throw new ParseException( "Length of header bytes part must equals eight!", 0 );
493             }
494             _header = IOUtil.sBitsToBBits( parts[0] );
495 
496             if ( parts.length > 0 )
497             {
498                 setAdapterName( parts[1].replaceAll( "/", "." ) );
499             }
500         }
501         else
502         {
503             throw new ParseException( "Missing or wrong formatted header part", 0 );
504         }
505     }
506 
507     /***
508      * Returns a {@link ByteBuffer} containg all transportable settings of the connection header
509      * 
510      * @return a {@link ByteBuffer} representation of the connection header
511      */
512     public ByteBuffer toByteBuffer()
513     {
514         byte[] adapterArr = null;
515         int length = 0;
516 
517         if ( _adapterClass != null )
518         {
519             adapterArr = IOUtil.encodeToBytes( _adapterClass );
520             length = adapterArr.length;
521         }
522 
523         // use indirect ByteBuffer because the buffer size will be just some
524         // bytes and allocation of indirect buffers is much faster
525         ByteBuffer buf = ByteBufferAllocator.allocate( 5 + length );
526         buf.put( toByte() );
527         buf.putInt( length );
528         if ( adapterArr != null )
529         {
530             buf.put( adapterArr );
531         }
532         buf.flip();
533 
534         return buf;
535     }
536 
537     /***
538      * Returns the byte array representation of this connection header
539      * 
540      * @return an array of bytes containing all transportable settings in the connection header
541      */
542     public byte[] toBytes()
543     {
544         ByteBuffer buf = toByteBuffer();
545         byte[] result = new byte[buf.limit()];
546         buf.get( result );
547 
548         return result;
549     }
550 
551     /***
552      * Returns a clean copy of the current instance containing all transportable settings of the connection header
553      * 
554      * @return a clean copy
555      */
556     public ConnectionHeader copy()
557     {
558         ConnectionHeader header = new ConnectionHeader( this._host, this._isClient, (boolean[]) this._header.clone() );
559         if ( hasAdapter() )
560         {
561             header.setAdapterName( this._adapterClass );
562         }
563 
564         return header;
565     }
566 }