View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.common;
21  
22  import java.util.AbstractSet;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Set;
26  import java.util.concurrent.atomic.AtomicLong;
27  
28  
29  /**
30   * Base implementation of {@link IoService}s.
31   *
32   * @author The Apache MINA Project (dev@mina.apache.org)
33   * @version $Rev: 607163 $, $Date: 2007-12-27 20:20:07 -0700 (Thu, 27 Dec 2007) $
34   */
35  public abstract class AbstractIoService implements IoService {
36      private static final IoServiceListener SERVICE_ACTIVATION_LISTENER =
37          new IoServiceListener() {
38              public void serviceActivated(IoService service) {
39                  // Update lastIoTime.
40                  AbstractIoService s = (AbstractIoService) service;
41                  s.setLastReadTime(s.getActivationTime());
42                  s.setLastWriteTime(s.getActivationTime());
43                  s.lastThroughputCalculationTime = s.getActivationTime();
44                  
45                  // Start idleness notification.
46                  IdleStatusChecker.getInstance().addService(s);
47              }
48  
49              public void serviceDeactivated(IoService service) {
50                  IdleStatusChecker.getInstance().removeService(
51                          (AbstractIoService) service);
52              }
53  
54              public void serviceIdle(IoService service, IdleStatus idleStatus) {}
55              public void sessionCreated(IoSession session) {}
56              public void sessionDestroyed(IoSession session) {}
57      };
58      
59      /**
60       * Current filter chain builder.
61       */
62      private IoFilterChainBuilder filterChainBuilder = new DefaultIoFilterChainBuilder();
63  
64      /**
65       * Current handler.
66       */
67      private IoHandler handler;
68      
69      private IoSessionDataStructureFactory sessionDataStructureFactory =
70          new DefaultIoSessionDataStructureFactory();
71  
72      /**
73       * Maintains the {@link IoServiceListener}s of this service.
74       */
75      private final IoServiceListenerSupport listeners;
76      
77      private final Object disposalLock = new Object();
78      private volatile boolean disposing;
79      private volatile boolean disposed;
80      private IoFuture disposalFuture;
81  
82      private final AtomicLong readBytes = new AtomicLong();
83      private final AtomicLong writtenBytes = new AtomicLong();
84      private final AtomicLong readMessages = new AtomicLong();
85      private final AtomicLong writtenMessages = new AtomicLong();
86      private long lastReadTime;
87      private long lastWriteTime;
88      
89      private final AtomicLong scheduledWriteBytes = new AtomicLong();
90      private final AtomicLong scheduledWriteMessages = new AtomicLong();
91  
92      private final Object throughputCalculationLock = new Object();
93      private int throughputCalculationInterval = 3;
94  
95      private long lastThroughputCalculationTime;
96      private long lastReadBytes;
97      private long lastWrittenBytes;
98      private long lastReadMessages;
99      private long lastWrittenMessages;
100     private double readBytesThroughput;
101     private double writtenBytesThroughput;
102     private double readMessagesThroughput;
103     private double writtenMessagesThroughput;
104     private double largestReadBytesThroughput;
105     private double largestWrittenBytesThroughput;
106     private double largestReadMessagesThroughput;
107     private double largestWrittenMessagesThroughput;
108 
109     private final Object idlenessCheckLock = new Object();
110     private int idleTimeForRead;
111     private int idleTimeForWrite;
112     private int idleTimeForBoth;
113 
114     private int idleCountForBoth;
115     private int idleCountForRead;
116     private int idleCountForWrite;
117 
118     private long lastIdleTimeForBoth;
119     private long lastIdleTimeForRead;
120     private long lastIdleTimeForWrite;
121     
122     /**
123      * The default {@link IoSessionConfig} which will be used to configure new sessions.
124      */
125     private IoSessionConfig sessionConfig;
126 
127     protected AbstractIoService(IoSessionConfig sessionConfig) {
128         if (sessionConfig == null) {
129             throw new NullPointerException("sessionConfig");
130         }
131 
132         if (!getTransportMetadata().getSessionConfigType().isAssignableFrom(
133                 sessionConfig.getClass())) {
134             throw new IllegalArgumentException("sessionConfig type: "
135                     + sessionConfig.getClass() + " (expected: "
136                     + getTransportMetadata().getSessionConfigType() + ")");
137         }
138 
139         this.listeners = new IoServiceListenerSupport(this);
140         this.listeners.add(SERVICE_ACTIVATION_LISTENER);
141         this.sessionConfig = sessionConfig;
142         
143         // Make JVM load the exception monitor before some transports
144         // change the thread context class loader.
145         ExceptionMonitor.getInstance();
146     }
147 
148     public final IoFilterChainBuilder getFilterChainBuilder() {
149         return filterChainBuilder;
150     }
151 
152     public final void setFilterChainBuilder(IoFilterChainBuilder builder) {
153         if (builder == null) {
154             builder = new DefaultIoFilterChainBuilder();
155         }
156         filterChainBuilder = builder;
157     }
158 
159     public final DefaultIoFilterChainBuilder getFilterChain() {
160         if (filterChainBuilder instanceof DefaultIoFilterChainBuilder) {
161             return (DefaultIoFilterChainBuilder) filterChainBuilder;
162         } else {
163             throw new IllegalStateException(
164                     "Current filter chain builder is not a DefaultIoFilterChainBuilder.");
165         }
166     }
167 
168     public final void addListener(IoServiceListener listener) {
169         listeners.add(listener);
170     }
171 
172     public final void removeListener(IoServiceListener listener) {
173         listeners.remove(listener);
174     }
175 
176     public final boolean isActive() {
177         return listeners.isActive();
178     }
179     
180     public final boolean isDisposing() {
181         return disposing;
182     }
183     
184     public final boolean isDisposed() {
185         return disposed;
186     }
187     
188     public final void dispose() {
189         if (disposed) {
190             return;
191         }
192 
193         IoFuture disposalFuture;
194         synchronized (disposalLock) {
195             disposalFuture = this.disposalFuture;
196             if (!disposing) {
197                 disposing = true;
198                 try {
199                     this.disposalFuture = disposalFuture = dispose0();
200                 } catch (Exception e) {
201                     ExceptionMonitor.getInstance().exceptionCaught(e);
202                 } finally {
203                     if (disposalFuture == null) {
204                         disposed = true;
205                     }
206                 }
207             }
208         }
209         
210         if (disposalFuture != null) {
211             disposalFuture.awaitUninterruptibly();
212         }
213 
214         disposed = true;
215     }
216     
217     /**
218      * Implement this method to release any acquired resources.  This method
219      * is invoked only once by {@link #dispose()}.
220      */
221     protected abstract IoFuture dispose0() throws Exception;
222 
223     public final Set<IoSession> getManagedSessions() {
224         return listeners.getManagedSessions();
225     }
226 
227     public final long getCumulativeManagedSessionCount() {
228         return listeners.getCumulativeManagedSessionCount();
229     }
230 
231     public final int getLargestManagedSessionCount() {
232         return listeners.getLargestManagedSessionCount();
233     }
234 
235     public final int getManagedSessionCount() {
236         return listeners.getManagedSessionCount();
237     }
238 
239     public final IoHandler getHandler() {
240         return handler;
241     }
242 
243     public final void setHandler(IoHandler handler) {
244         if (handler == null) {
245             throw new NullPointerException("handler");
246         }
247 
248         if (isActive()) {
249             throw new IllegalStateException("handler cannot be set while the service is active.");
250         }
251 
252         this.handler = handler;
253     }
254 
255     public IoSessionConfig getSessionConfig() {
256         return sessionConfig;
257     }
258 
259     public final IoSessionDataStructureFactory getSessionDataStructureFactory() {
260         return sessionDataStructureFactory;
261     }
262 
263     public final void setSessionDataStructureFactory(IoSessionDataStructureFactory sessionDataStructureFactory) {
264         if (sessionDataStructureFactory == null) {
265             throw new NullPointerException("sessionDataStructureFactory");
266         }
267 
268         if (isActive()) {
269             throw new IllegalStateException(
270                     "sessionDataStructureFactory cannot be set while the service is active.");
271         }
272 
273         this.sessionDataStructureFactory = sessionDataStructureFactory;
274     }
275 
276     public final long getReadBytes() {
277         return readBytes.get();
278     }
279 
280     protected final void increaseReadBytes(long increment, long currentTime) {
281         readBytes.addAndGet(increment);
282         lastReadTime = currentTime;
283         idleCountForBoth = 0;
284         idleCountForRead = 0;
285     }
286 
287     public final long getReadMessages() {
288         return readMessages.get();
289     }
290 
291     protected final void increaseReadMessages(long currentTime) {
292         readMessages.incrementAndGet();
293         lastReadTime = currentTime;
294         idleCountForBoth = 0;
295         idleCountForRead = 0;
296     }
297 
298     public final int getThroughputCalculationInterval() {
299         return throughputCalculationInterval;
300     }
301 
302     public final void setThroughputCalculationInterval(int throughputCalculationInterval) {
303         if (throughputCalculationInterval < 0) {
304             throw new IllegalArgumentException(
305                     "throughputCalculationInterval: " + throughputCalculationInterval);
306         }
307 
308         this.throughputCalculationInterval = throughputCalculationInterval;
309     }
310     
311     public final long getThroughputCalculationIntervalInMillis() {
312         return throughputCalculationInterval * 1000L;
313     }
314     
315     public final double getReadBytesThroughput() {
316         resetThroughput();
317         return readBytesThroughput;
318     }
319 
320     public final double getWrittenBytesThroughput() {
321         resetThroughput();
322         return writtenBytesThroughput;
323     }
324 
325     public final double getReadMessagesThroughput() {
326         resetThroughput();
327         return readMessagesThroughput;
328     }
329 
330     public final double getWrittenMessagesThroughput() {
331         resetThroughput();
332         return writtenMessagesThroughput;
333     }
334     
335     public final double getLargestReadBytesThroughput() {
336         return largestReadBytesThroughput;
337     }
338     
339     public final double getLargestWrittenBytesThroughput() {
340         return largestWrittenBytesThroughput;
341     }
342     
343     public final double getLargestReadMessagesThroughput() {
344         return largestReadMessagesThroughput;
345     }
346     
347     public final double getLargestWrittenMessagesThroughput() {
348         return largestWrittenMessagesThroughput;
349     }
350     
351     private void resetThroughput() {
352         if (getManagedSessionCount() == 0) {
353             readBytesThroughput = 0;
354             writtenBytesThroughput = 0;
355             readMessagesThroughput = 0;
356             writtenMessagesThroughput = 0;
357         }
358     }
359 
360     private void updateThroughput(long currentTime) {
361         synchronized (throughputCalculationLock) {
362             int interval = (int) (currentTime - lastThroughputCalculationTime);
363             long minInterval = getThroughputCalculationIntervalInMillis();
364             if (minInterval == 0 || interval < minInterval) {
365                 return;
366             }
367             
368             long readBytes = this.readBytes.get();
369             long writtenBytes = this.writtenBytes.get();
370             long readMessages = this.readMessages.get();
371             long writtenMessages = this.writtenMessages.get();
372             
373             readBytesThroughput = (readBytes - lastReadBytes) * 1000.0 / interval;
374             writtenBytesThroughput = (writtenBytes - lastWrittenBytes) * 1000.0 / interval;
375             readMessagesThroughput = (readMessages - lastReadMessages) * 1000.0 / interval;
376             writtenMessagesThroughput = (writtenMessages - lastWrittenMessages) * 1000.0 / interval;
377             
378             if (readBytesThroughput > largestReadBytesThroughput) {
379                 largestReadBytesThroughput = readBytesThroughput;
380             }
381             if (writtenBytesThroughput > largestWrittenBytesThroughput) {
382                 largestWrittenBytesThroughput = writtenBytesThroughput;
383             }
384             if (readMessagesThroughput > largestReadMessagesThroughput) {
385                 largestReadMessagesThroughput = readMessagesThroughput;
386             }
387             if (writtenMessagesThroughput > largestWrittenMessagesThroughput) {
388                 largestWrittenMessagesThroughput = writtenMessagesThroughput;
389             }
390            
391             lastReadBytes = readBytes;
392             lastWrittenBytes = writtenBytes;
393             lastReadMessages = readMessages;
394             lastWrittenMessages = writtenMessages;
395             
396             lastThroughputCalculationTime = currentTime;
397         }
398     }
399     
400     public final long getScheduledWriteBytes() {
401         return scheduledWriteBytes.get();
402     }
403 
404     protected final void increaseScheduledWriteBytes(long increment) {
405         scheduledWriteBytes.addAndGet(increment);
406     }
407 
408     public final long getScheduledWriteMessages() {
409         return scheduledWriteMessages.get();
410     }
411 
412     protected final void increaseScheduledWriteMessages() {
413         scheduledWriteMessages.incrementAndGet();
414     }
415 
416     protected final void decreaseScheduledWriteMessages() {
417         scheduledWriteMessages.decrementAndGet();
418     }
419 
420     public final long getActivationTime() {
421         return listeners.getActivationTime();
422     }
423 
424     public final long getLastIoTime() {
425         return Math.max(lastReadTime, lastWriteTime);
426     }
427 
428     public final long getLastReadTime() {
429         return lastReadTime;
430     }
431 
432     protected final void setLastReadTime(long lastReadTime) {
433         this.lastReadTime = lastReadTime;
434     }
435 
436     public final long getLastWriteTime() {
437         return lastWriteTime;
438     }
439     
440     protected final void setLastWriteTime(long lastWriteTime) {
441         this.lastWriteTime = lastWriteTime;
442     }
443 
444     public final long getWrittenBytes() {
445         return writtenBytes.get();
446     }
447 
448     protected final void increaseWrittenBytes(long increment, long currentTime) {
449         writtenBytes.addAndGet(increment);
450         lastWriteTime = currentTime;
451         idleCountForBoth = 0;
452         idleCountForWrite = 0;
453     }
454 
455     public final long getWrittenMessages() {
456         return writtenMessages.get();
457     }
458 
459     protected final void increaseWrittenMessages(long currentTime) {
460         writtenMessages.incrementAndGet();
461         lastWriteTime = currentTime;
462         idleCountForBoth = 0;
463         idleCountForWrite = 0;
464     }
465 
466     public final int getIdleTime(IdleStatus status) {
467         if (status == IdleStatus.BOTH_IDLE) {
468             return idleTimeForBoth;
469         }
470 
471         if (status == IdleStatus.READER_IDLE) {
472             return idleTimeForRead;
473         }
474 
475         if (status == IdleStatus.WRITER_IDLE) {
476             return idleTimeForWrite;
477         }
478 
479         throw new IllegalArgumentException("Unknown idle status: " + status);
480     }
481 
482     public final long getIdleTimeInMillis(IdleStatus status) {
483         return getIdleTime(status) * 1000L;
484     }
485 
486     public final void setIdleTime(IdleStatus status, int idleTime) {
487         if (idleTime < 0) {
488             throw new IllegalArgumentException("Illegal idle time: " + idleTime);
489         }
490         
491         if (status == IdleStatus.BOTH_IDLE) {
492             idleTimeForBoth = idleTime;
493         } else if (status == IdleStatus.READER_IDLE) {
494             idleTimeForRead = idleTime;
495         } else if (status == IdleStatus.WRITER_IDLE) {
496             idleTimeForWrite = idleTime;
497         } else {
498             throw new IllegalArgumentException("Unknown idle status: " + status);
499         }
500         
501         if (idleTime == 0) {
502             if (status == IdleStatus.BOTH_IDLE) {
503                 idleCountForBoth = 0;
504             } else if (status == IdleStatus.READER_IDLE) {
505                 idleCountForRead = 0;
506             } else if (status == IdleStatus.WRITER_IDLE) {
507                 idleCountForWrite = 0;
508             }
509         }
510     }
511 
512     public final boolean isIdle(IdleStatus status) {
513         if (status == IdleStatus.BOTH_IDLE) {
514             return idleCountForBoth > 0;
515         }
516 
517         if (status == IdleStatus.READER_IDLE) {
518             return idleCountForRead > 0;
519         }
520 
521         if (status == IdleStatus.WRITER_IDLE) {
522             return idleCountForWrite > 0;
523         }
524 
525         throw new IllegalArgumentException("Unknown idle status: " + status);
526     }
527 
528     public final int getIdleCount(IdleStatus status) {
529         if (status == IdleStatus.BOTH_IDLE) {
530             return idleCountForBoth;
531         }
532 
533         if (status == IdleStatus.READER_IDLE) {
534             return idleCountForRead;
535         }
536 
537         if (status == IdleStatus.WRITER_IDLE) {
538             return idleCountForWrite;
539         }
540 
541         throw new IllegalArgumentException("Unknown idle status: " + status);
542     }
543 
544     public final long getLastIdleTime(IdleStatus status) {
545         if (status == IdleStatus.BOTH_IDLE) {
546             return lastIdleTimeForBoth;
547         }
548 
549         if (status == IdleStatus.READER_IDLE) {
550             return lastIdleTimeForRead;
551         }
552 
553         if (status == IdleStatus.WRITER_IDLE) {
554             return lastIdleTimeForWrite;
555         }
556 
557         throw new IllegalArgumentException("Unknown idle status: " + status);
558     }
559 
560     private void increaseIdleCount(IdleStatus status, long currentTime) {
561         if (status == IdleStatus.BOTH_IDLE) {
562             idleCountForBoth++;
563             lastIdleTimeForBoth = currentTime;
564         } else if (status == IdleStatus.READER_IDLE) {
565             idleCountForRead++;
566             lastIdleTimeForRead = currentTime;
567         } else if (status == IdleStatus.WRITER_IDLE) {
568             idleCountForWrite++;
569             lastIdleTimeForWrite = currentTime;
570         } else {
571             throw new IllegalArgumentException("Unknown idle status: " + status);
572         }
573     }
574     
575     protected final void notifyIdleness(long currentTime) {
576         updateThroughput(currentTime);
577         
578         synchronized (idlenessCheckLock) {
579             notifyIdleness(
580                     currentTime,
581                     getIdleTimeInMillis(IdleStatus.BOTH_IDLE),
582                     IdleStatus.BOTH_IDLE, Math.max(
583                             getLastIoTime(),
584                             getLastIdleTime(IdleStatus.BOTH_IDLE)));
585             
586             notifyIdleness(
587                     currentTime,
588                     getIdleTimeInMillis(IdleStatus.READER_IDLE),
589                     IdleStatus.READER_IDLE, Math.max(
590                             getLastReadTime(),
591                             getLastIdleTime(IdleStatus.READER_IDLE)));
592             
593             notifyIdleness(
594                     currentTime,
595                     getIdleTimeInMillis(IdleStatus.WRITER_IDLE),
596                     IdleStatus.WRITER_IDLE, Math.max(
597                             getLastWriteTime(),
598                             getLastIdleTime(IdleStatus.WRITER_IDLE)));
599         }
600     }
601     
602     private void notifyIdleness(
603             long currentTime, long idleTime, IdleStatus status, long lastIoTime) {
604         if (idleTime > 0 && lastIoTime != 0
605                 && currentTime - lastIoTime >= idleTime) {
606             increaseIdleCount(status, currentTime);
607             listeners.fireServiceIdle(status);
608         }
609     }
610 
611     public final int getBothIdleCount() {
612         return getIdleCount(IdleStatus.BOTH_IDLE);
613     }
614 
615     public final long getLastBothIdleTime() {
616         return getLastIdleTime(IdleStatus.BOTH_IDLE);
617     }
618 
619     public final long getLastReaderIdleTime() {
620         return getLastIdleTime(IdleStatus.READER_IDLE);
621     }
622 
623     public final long getLastWriterIdleTime() {
624         return getLastIdleTime(IdleStatus.WRITER_IDLE);
625     }
626 
627     public final int getReaderIdleCount() {
628         return getIdleCount(IdleStatus.READER_IDLE);
629     }
630 
631     public final int getWriterIdleCount() {
632         return getIdleCount(IdleStatus.WRITER_IDLE);
633     }
634     
635     public final int getBothIdleTime() {
636         return getIdleTime(IdleStatus.BOTH_IDLE);
637     }
638 
639     public final long getBothIdleTimeInMillis() {
640         return getIdleTimeInMillis(IdleStatus.BOTH_IDLE);
641     }
642 
643     public final int getReaderIdleTime() {
644         return getIdleTime(IdleStatus.READER_IDLE);
645     }
646 
647     public final long getReaderIdleTimeInMillis() {
648         return getIdleTimeInMillis(IdleStatus.READER_IDLE);
649     }
650 
651     public final int getWriterIdleTime() {
652         return getIdleTime(IdleStatus.WRITER_IDLE);
653     }
654 
655     public final long getWriterIdleTimeInMillis() {
656         return getIdleTimeInMillis(IdleStatus.WRITER_IDLE);
657     }
658 
659     public final boolean isBothIdle() {
660         return isIdle(IdleStatus.BOTH_IDLE);
661     }
662 
663     public final boolean isReaderIdle() {
664         return isIdle(IdleStatus.READER_IDLE);
665     }
666 
667     public final boolean isWriterIdle() {
668         return isIdle(IdleStatus.WRITER_IDLE);
669     }
670 
671     public final void setBothIdleTime(int idleTime) {
672         setIdleTime(IdleStatus.BOTH_IDLE, idleTime);
673     }
674 
675     public final void setReaderIdleTime(int idleTime) {
676         setIdleTime(IdleStatus.READER_IDLE, idleTime);
677     }
678 
679     public final void setWriterIdleTime(int idleTime) {
680         setIdleTime(IdleStatus.WRITER_IDLE, idleTime);
681     }
682 
683     public final Set<WriteFuture> broadcast(Object message) {
684         // Convert to Set.  We do not return a List here because only the 
685         // direct caller of MessageBroadcaster knows the order of write
686         // operations.
687         final List<WriteFuture> futures = IoUtil.broadcast(
688                 message, getManagedSessions());
689         return new AbstractSet<WriteFuture>() {
690             @Override
691             public Iterator<WriteFuture> iterator() {
692                 return futures.iterator();
693             }
694 
695             @Override
696             public int size() {
697                 return futures.size();
698             }
699         };
700     }
701     
702     protected final IoServiceListenerSupport getListeners() {
703         return listeners;
704     }
705     
706     // TODO Figure out make it work without causing a compiler error / warning.
707     @SuppressWarnings("unchecked")
708     protected final void finishSessionInitialization(
709             IoSession session, IoFuture future, IoSessionInitializer sessionInitializer) {
710         // Update lastIoTime if needed.
711         if (getLastReadTime() == 0) {
712             setLastReadTime(getActivationTime());
713         }
714         if (getLastWriteTime() == 0) {
715             setLastWriteTime(getActivationTime());
716         }
717 
718         // Every property but attributeMap should be set now.
719         // Now initialize the attributeMap.  The reason why we initialize
720         // the attributeMap at last is to make sure all session properties
721         // such as remoteAddress are provided to IoSessionDataStructureFactory.
722         try {
723             ((AbstractIoSession) session).setAttributeMap(
724                     session.getService().getSessionDataStructureFactory().getAttributeMap(session));
725         } catch (IoSessionInitializationException e) {
726             throw e;
727         } catch (Exception e) {
728             throw new IoSessionInitializationException(
729                     "Failed to initialize an attributeMap.", e);
730         }
731 
732         try {
733             ((AbstractIoSession) session).setWriteRequestQueue(
734                     session.getService().getSessionDataStructureFactory().getWriteRequestQueue(session));
735         } catch (IoSessionInitializationException e) {
736             throw e;
737         } catch (Exception e) {
738             throw new IoSessionInitializationException(
739                     "Failed to initialize a writeRequestQueue.", e);
740         }
741 
742         if (future != null && future instanceof ConnectFuture) {
743             // DefaultIoFilterChain will notify the future. (We support ConnectFuture only for now).
744             session.setAttribute(DefaultIoFilterChain.SESSION_OPENED_FUTURE, future);
745         }
746         
747         if (sessionInitializer != null) {
748             sessionInitializer.initializeSession(session, future);
749         }
750         
751         finishSessionInitialization0(session, future);
752     }
753     
754     /**
755      * Implement this method to perform additional tasks required for session
756      * initialization. Do not call this method directly;
757      * {@link #finishSessionInitialization(IoSession, IoFuture, IoSessionInitializer)} will call
758      * this method instead.
759      */
760     @SuppressWarnings("unused")
761     protected void finishSessionInitialization0(IoSession session, IoFuture future) {}
762 
763     protected static class ServiceOperationFuture extends DefaultIoFuture {
764         public ServiceOperationFuture() {
765             super(null);
766         }
767 
768         public final boolean isDone() {
769             return getValue() == Boolean.TRUE;
770         }
771 
772         public final void setDone() {
773             setValue(Boolean.TRUE);
774         }
775 
776         public final Exception getException() {
777             if (getValue() instanceof Exception) {
778                 return (Exception) getValue();
779             } else {
780                 return null;
781             }
782         }
783 
784         public final void setException(Exception exception) {
785             if (exception == null) {
786                 throw new NullPointerException("exception");
787             }
788             setValue(exception);
789         }
790     }
791 }