1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.mina.session;
21
22 import java.nio.ByteBuffer;
23 import java.util.Collections;
24 import java.util.Set;
25 import java.util.concurrent.atomic.AtomicInteger;
26 import java.util.concurrent.locks.Lock;
27 import java.util.concurrent.locks.ReadWriteLock;
28 import java.util.concurrent.locks.ReentrantReadWriteLock;
29
30 import javax.net.ssl.SSLContext;
31
32 import org.apache.mina.api.IdleStatus;
33 import org.apache.mina.api.IoFilter;
34 import org.apache.mina.api.IoFuture;
35 import org.apache.mina.api.IoHandler;
36 import org.apache.mina.api.IoServer;
37 import org.apache.mina.api.IoService;
38 import org.apache.mina.api.IoSession;
39 import org.apache.mina.api.IoSessionConfig;
40 import org.apache.mina.filterchain.ReadFilterChainController;
41 import org.apache.mina.filterchain.WriteFilterChainController;
42 import org.apache.mina.service.executor.CloseEvent;
43 import org.apache.mina.service.executor.IdleEvent;
44 import org.apache.mina.service.executor.IoHandlerExecutor;
45 import org.apache.mina.service.executor.OpenEvent;
46 import org.apache.mina.service.executor.ReceiveEvent;
47 import org.apache.mina.service.executor.SentEvent;
48 import org.apache.mina.service.idlechecker.IdleChecker;
49 import org.apache.mina.transport.nio.SelectorLoop;
50 import org.apache.mina.transport.nio.SslHelper;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54
55
56
57
58
59 public abstract class AbstractIoSession implements IoSession, ReadFilterChainController, WriteFilterChainController {
60
61 private static final Logger LOG = LoggerFactory.getLogger(AbstractIoSession.class);
62
63
64 private static final boolean IS_DEBUG = LOG.isDebugEnabled();
65
66
67 private static final AtomicInteger NEXT_ID = new AtomicInteger(0);
68
69
70 private final long id;
71
72
73 private final long creationTime;
74
75
76 private final IoService service;
77
78
79 private final AttributeContainer attributes = new DefaultAttributeContainer();
80
81
82 protected final IdleChecker idleChecker;
83
84
85 protected IoSessionConfig config;
86
87
88
89
90
91
92 private volatile long readBytes;
93
94
95 private volatile long writtenBytes;
96
97
98 private volatile long lastReadTime;
99
100
101 private volatile long lastWriteTime;
102
103
104
105
106
107
108 protected volatile SessionState state;
109
110
111 private final ReadWriteLock stateLock = new ReentrantReadWriteLock();
112
113
114 private final Lock stateReadLock = stateLock.readLock();
115
116
117 private final Lock stateWriteLock = stateLock.writeLock();
118
119
120 protected volatile boolean secured;
121
122
123
124
125
126
127 private final IoFilter[] chain;
128
129
130 private int writeChainPosition;
131
132
133 private int readChainPosition;
134
135
136
137
138
139
140
141
142 public AbstractIoSession(IoService service, IdleChecker idleChecker) {
143
144 id = NEXT_ID.getAndIncrement();
145 creationTime = System.currentTimeMillis();
146 this.service = service;
147 this.chain = service.getFilters();
148 this.idleChecker = idleChecker;
149 this.config = service.getSessionConfig();
150
151 if (IS_DEBUG) {
152 LOG.debug("Created new session with id : {}", id);
153 }
154
155 this.state = SessionState.CREATED;
156 service.getManagedSessions().put(id, this);
157 }
158
159
160
161
162
163
164
165 @Override
166 public boolean isClosed() {
167 try {
168 stateReadLock.lock();
169
170 return state == SessionState.CLOSED;
171 } finally {
172 stateReadLock.unlock();
173 }
174 }
175
176
177
178
179 @Override
180 public boolean isClosing() {
181 try {
182 stateReadLock.lock();
183
184 return state == SessionState.CLOSING;
185 } finally {
186 stateReadLock.unlock();
187 }
188 }
189
190
191
192
193 @Override
194 public boolean isConnected() {
195 try {
196 stateReadLock.lock();
197
198 return state == SessionState.CONNECTED;
199 } finally {
200 stateReadLock.unlock();
201 }
202 }
203
204
205
206
207 @Override
208 public boolean isCreated() {
209 try {
210 stateReadLock.lock();
211
212 return state == SessionState.CREATED;
213 } finally {
214 stateReadLock.unlock();
215 }
216 }
217
218
219
220
221 @Override
222 public boolean isSecuring() {
223 try {
224 stateReadLock.lock();
225
226 return state == SessionState.SECURING;
227 } finally {
228 stateReadLock.unlock();
229 }
230 }
231
232
233
234
235 @Override
236 public boolean isConnectedSecured() {
237 try {
238 stateReadLock.lock();
239
240 return state == SessionState.SECURED;
241 } finally {
242 stateReadLock.unlock();
243 }
244 }
245
246
247
248
249 @Override
250 public void changeState(SessionState to) {
251 try {
252 stateWriteLock.lock();
253
254 switch (state) {
255 case CREATED:
256 switch (to) {
257 case CONNECTED:
258 case SECURING:
259 case CLOSING:
260 state = to;
261 break;
262
263 default:
264 throw new IllegalStateException("Cannot transit from " + state + " to " + to);
265 }
266
267 break;
268
269 case CONNECTED:
270 switch (to) {
271 case SECURING:
272 case CLOSING:
273 state = to;
274 break;
275
276 default:
277 throw new IllegalStateException("Cannot transit from " + state + " to " + to);
278 }
279
280 break;
281
282 case SECURING:
283 switch (to) {
284 case SECURED:
285 case CLOSING:
286 state = to;
287 break;
288
289 default:
290 throw new IllegalStateException("Cannot transit from " + state + " to " + to);
291 }
292
293 break;
294
295 case SECURED:
296 switch (to) {
297 case CONNECTED:
298 case SECURING:
299 case CLOSING:
300 state = to;
301 break;
302
303 default:
304 throw new IllegalStateException("Cannot transit from " + state + " to " + to);
305 }
306
307 break;
308 case CLOSING:
309 if (to != SessionState.CLOSED) {
310 throw new IllegalStateException("Cannot transit from " + state + " to " + to);
311 }
312
313 state = to;
314
315 break;
316
317 case CLOSED:
318 throw new IllegalStateException("The session is already closed. cannot switch to " + to);
319 }
320 } finally {
321 stateWriteLock.unlock();
322 }
323 }
324
325
326
327
328
329
330
331 @Override
332 public boolean isSecured() {
333 return secured;
334 }
335
336
337
338
339 public void setSecured(boolean secured) {
340 this.secured = secured;
341 }
342
343
344
345
346 @Override
347 public void initSecure(SSLContext sslContext) {
348 SslHelper sslHelper = new SslHelper(this, sslContext);
349 sslHelper.init();
350
351 attributes.setAttribute(SSL_HELPER, sslHelper);
352 setSecured(true);
353 }
354
355
356
357
358 @Override
359 public long getId() {
360 return id;
361 }
362
363
364
365
366 @Override
367 public long getCreationTime() {
368 return creationTime;
369 }
370
371
372
373
374 @Override
375 public long getReadBytes() {
376 return readBytes;
377 }
378
379
380
381
382
383
384 public void incrementWrittenBytes(int bytesCount) {
385 writtenBytes += bytesCount;
386 }
387
388
389
390
391 @Override
392 public long getWrittenBytes() {
393 return writtenBytes;
394 }
395
396
397
398
399 @Override
400 public long getLastReadTime() {
401 return lastReadTime;
402 }
403
404
405
406
407 @Override
408 public long getLastWriteTime() {
409 return lastWriteTime;
410 }
411
412
413
414
415 @Override
416 public final long getLastIoTime() {
417 return Math.max(lastReadTime, lastWriteTime);
418 }
419
420
421
422
423 @Override
424 public IoService getService() {
425 return service;
426 }
427
428
429
430
431
432
433
434 @Override
435 public final <T> T getAttribute(AttributeKey<T> key, T defaultValue) {
436 return attributes.getAttribute(key, defaultValue);
437 }
438
439
440
441
442
443
444
445 @Override
446 public final <T> T getAttribute(AttributeKey<T> key) {
447 return attributes.getAttribute(key);
448 }
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463 @Override
464 public final <T> T setAttribute(AttributeKey<? extends T> key, T value) {
465 return attributes.setAttribute(key, value);
466 };
467
468
469
470
471
472
473 @Override
474 public Set<AttributeKey<?>> getAttributeKeys() {
475 return attributes.getAttributeKeys();
476 }
477
478
479
480
481
482
483 @Override
484 public <T> T removeAttribute(AttributeKey<T> key) {
485 return attributes.removeAttribute(key);
486 }
487
488
489
490
491
492
493
494
495 @Override
496 public void write(Object message) {
497 doWriteWithFuture(message, null);
498 }
499
500
501
502
503 @Override
504 public IoFuture<Void> writeWithFuture(Object message) {
505 IoFuture<Void> future = new DefaultWriteFuture();
506 doWriteWithFuture(message, future);
507
508 return future;
509 }
510
511 private void doWriteWithFuture(Object message, IoFuture<Void> future) {
512 if (IS_DEBUG) {
513 LOG.debug("writing message {} to session {}", message, this);
514 }
515
516 if ((state == SessionState.CLOSED) || (state == SessionState.CLOSING)) {
517 LOG.error("writing to closed or closing session, the message is discarded");
518 return;
519 }
520
521 WriteRequest writeRequest = new DefaultWriteRequest(message);
522
523
524 processMessageWriting(writeRequest, future);
525 }
526
527
528
529
530
531
532 protected void processException(Exception t) {
533 if (IS_DEBUG) {
534 LOG.debug("caught session exception ", t);
535 }
536
537 IoHandler handler = getService().getIoHandler();
538
539 if (handler != null) {
540 handler.exceptionCaught(this, t);
541 }
542 }
543
544
545
546
547 public void processSessionOpen() {
548 if (IS_DEBUG) {
549 LOG.debug("processing session open event");
550 }
551
552 try {
553
554 for (IoFilter filter : chain) {
555 filter.sessionOpened(this);
556 }
557
558 IoHandler handler = getService().getIoHandler();
559
560 if (handler != null) {
561 IoHandlerExecutor executor = getService().getIoHandlerExecutor();
562
563 if (executor != null) {
564
565 executor.execute(new OpenEvent(this));
566 } else {
567
568 handler.sessionOpened(this);
569 }
570 }
571 } catch (RuntimeException e) {
572 processException(e);
573 }
574 }
575
576
577
578
579 public void processSessionClosed() {
580 if (IS_DEBUG) {
581 LOG.debug("processing session closed event");
582 }
583
584 try {
585 for (IoFilter filter : chain) {
586 filter.sessionClosed(this);
587 }
588
589 IoHandler handler = getService().getIoHandler();
590
591 if (handler != null) {
592 IoHandlerExecutor executor = getService().getIoHandlerExecutor();
593 if (executor != null) {
594
595 executor.execute(new CloseEvent(this));
596 } else {
597
598 handler.sessionClosed(this);
599 }
600 }
601 } catch (RuntimeException e) {
602 LOG.error("Exception while closing the session : ", e);
603 }
604 service.getManagedSessions().remove(id);
605 }
606
607
608
609
610 public void processSessionIdle(IdleStatus status) {
611 if (IS_DEBUG) {
612 LOG.debug("processing session idle {} event for session {}", status, this);
613 }
614
615 try {
616 for (IoFilter filter : chain) {
617 filter.sessionIdle(this, status);
618 }
619
620 IoHandler handler = getService().getIoHandler();
621
622 if (handler != null) {
623 IoHandlerExecutor executor = getService().getIoHandlerExecutor();
624
625 if (executor != null) {
626
627 executor.execute(new IdleEvent(this, status));
628 } else {
629
630 handler.sessionIdle(this, status);
631 }
632 }
633 } catch (RuntimeException e) {
634 processException(e);
635 }
636 }
637
638
639 static ThreadLocal<ByteBuffer> tl = new ThreadLocal<ByteBuffer>() {
640 @Override
641 protected ByteBuffer initialValue() {
642 return null;
643 }
644 };
645
646
647
648
649
650
651 public void processMessageReceived(ByteBuffer message) {
652 if (IS_DEBUG) {
653 LOG.debug("processing message '{}' received event for session {}", message, this);
654 }
655
656 tl.set(message);
657 try {
658
659 readBytes += message.remaining();
660 lastReadTime = System.currentTimeMillis();
661
662 if (chain.length < 1) {
663 if (IS_DEBUG) {
664 LOG.debug("Nothing to do, the chain is empty");
665 }
666
667 IoHandler handler = getService().getIoHandler();
668
669 if (handler != null) {
670 IoHandlerExecutor executor = getService().getIoHandlerExecutor();
671
672 if (executor != null) {
673
674
675 if (IS_DEBUG) {
676 LOG.debug("copying bytebuffer before pushing to the executor");
677 }
678
679 ByteBuffer original = message;
680 ByteBuffer clone = ByteBuffer.allocate(original.capacity());
681
682 original.rewind();
683 clone.put(original);
684 original.rewind();
685 clone.flip();
686 executor.execute(new ReceiveEvent(this, clone));
687 } else {
688
689 handler.messageReceived(this, message);
690 }
691 }
692
693 } else {
694 readChainPosition = 0;
695
696 chain[readChainPosition].messageReceived(this, message, this);
697 }
698 } catch (RuntimeException e) {
699 processException(e);
700 }
701
702 }
703
704
705
706
707
708
709 public void processMessageWriting(WriteRequest writeRequest, IoFuture<Void> future) {
710 if (IS_DEBUG) {
711 LOG.debug("processing message '{}' writing event for session {}", writeRequest, this);
712 }
713
714 try {
715
716
717 if (chain.length < 1) {
718 enqueueWriteRequest(writeRequest);
719 } else {
720 writeChainPosition = chain.length - 1;
721
722 int position = writeChainPosition;
723 IoFilter nextFilter = chain[position];
724 nextFilter.messageWriting(this, writeRequest, this);
725 }
726
727
728 if (future != null) {
729 writeRequest.setFuture(future);
730 }
731 } catch (RuntimeException e) {
732 processException(e);
733 }
734
735 }
736
737 public void processMessageSent(Object highLevelMessage) {
738 if (IS_DEBUG) {
739 LOG.debug("processing message '{}' sent event for session {}", highLevelMessage, this);
740 }
741
742 try {
743 int size = chain.length;
744
745 for (int i = size - 1; i >= 0; i--) {
746 chain[i].messageSent(this, highLevelMessage);
747 }
748
749 IoHandler handler = getService().getIoHandler();
750
751 if (handler != null) {
752 IoHandlerExecutor executor = getService().getIoHandlerExecutor();
753
754 if (executor != null) {
755
756 executor.execute(new SentEvent(this, highLevelMessage));
757 } else {
758
759 handler.messageSent(this, highLevelMessage);
760 }
761 }
762 } catch (RuntimeException e) {
763 processException(e);
764 }
765
766 }
767
768
769
770
771
772
773 @Override
774 public void callWriteNextFilter(WriteRequest message) {
775 if (IS_DEBUG) {
776 LOG.debug("calling next filter for writing for message '{}' position : {}", message, writeChainPosition);
777 }
778
779 writeChainPosition--;
780
781 if (writeChainPosition < 0 || chain.length == 0) {
782
783 enqueueWriteRequest(message);
784 } else {
785 chain[writeChainPosition].messageWriting(this, message, this);
786 }
787
788 writeChainPosition++;
789 }
790
791
792
793
794 @Override
795 public void callReadNextFilter(Object message) {
796 readChainPosition++;
797
798 if (readChainPosition >= chain.length) {
799
800 IoHandler handler = getService().getIoHandler();
801
802 if (handler != null) {
803 IoHandlerExecutor executor = getService().getIoHandlerExecutor();
804
805 if (executor != null) {
806
807 if (message == tl.get()) {
808
809 if (IS_DEBUG) {
810 LOG.debug("copying bytebuffer before pushing to the executor");
811 }
812
813 ByteBuffer original = (ByteBuffer) message;
814 ByteBuffer clone = ByteBuffer.allocate(original.capacity());
815
816 original.rewind();
817 clone.put(original);
818 original.rewind();
819 clone.flip();
820 executor.execute(new ReceiveEvent(this, clone));
821 } else {
822 executor.execute(new ReceiveEvent(this, message));
823 }
824 } else {
825
826 handler.messageReceived(this, message);
827 }
828 }
829 } else {
830 chain[readChainPosition].messageReceived(this, message, this);
831 }
832
833 readChainPosition--;
834 }
835
836
837
838
839 @Override
840 public String toString() {
841 if (isConnected() || isClosing()) {
842 String remote = null;
843 String local = null;
844
845 try {
846 remote = String.valueOf(getRemoteAddress());
847 } catch (Exception e) {
848 remote = "Cannot get the remote address informations: " + e.getMessage();
849 }
850
851 try {
852 local = String.valueOf(getLocalAddress());
853 } catch (Exception e) {
854 local = "Cannot get the local address informations: " + e.getMessage();
855 }
856
857 if (getService() instanceof IoServer) {
858 return "(" + getIdAsString() + ": " + getServiceName() + ", server, " + remote + " => " + local + ')';
859 }
860
861 return "(" + getIdAsString() + ": " + getServiceName() + ", client, " + local + " => " + remote + ')';
862 }
863
864 return "(" + getIdAsString() + ") Session disconnected ...";
865 }
866
867 private String getServiceName() {
868 return getService().getClass().getCanonicalName();
869 }
870
871
872 private String getIdAsString() {
873 String id = Long.toHexString(getId()).toUpperCase();
874
875
876
877 while (id.length() < 8) {
878 id = '0' + id;
879 }
880 id = "0x" + id;
881
882 return id;
883 }
884 }