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;
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
449
450
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[&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
524
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 }