Coverage Report - com.sun.grizzly.suspendable.SuspendableFilter
 
Classes in this File Line Coverage Branch Coverage Complexity
SuspendableFilter
82 %
84/103
62 %
25/40
0
SuspendableFilter$1
0 %
0/4
N/A
0
SuspendableFilter$KeyHandler
100 %
17/17
N/A
0
SuspendableFilter$Suspend
100 %
1/1
N/A
0
SuspendableFilter$SuspendableHandlerWrapper
63 %
19/30
N/A
0
 
 1  
 /*
 2  
  * 
 3  
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 4  
  * 
 5  
  * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved.
 6  
  * 
 7  
  * The contents of this file are subject to the terms of either the GNU
 8  
  * General Public License Version 2 only ("GPL") or the Common Development
 9  
  * and Distribution License("CDDL") (collectively, the "License").  You
 10  
  * may not use this file except in compliance with the License. You can obtain
 11  
  * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 12  
  * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 13  
  * language governing permissions and limitations under the License.
 14  
  * 
 15  
  * When distributing the software, include this License Header Notice in each
 16  
  * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 17  
  * Sun designates this particular file as subject to the "Classpath" exception
 18  
  * as provided by Sun in the GPL Version 2 section of the License file that
 19  
  * accompanied this code.  If applicable, add the following below the License
 20  
  * Header, with the fields enclosed by brackets [] replaced by your own
 21  
  * identifying information: "Portions Copyrighted [year]
 22  
  * [name of copyright owner]"
 23  
  * 
 24  
  * Contributor(s):
 25  
  * 
 26  
  * If you wish your version of this file to be governed by only the CDDL or
 27  
  * only the GPL Version 2, indicate your decision by adding "[Contributor]
 28  
  * elects to include this software in this distribution under the [CDDL or GPL
 29  
  * Version 2] license."  If you don't indicate a single choice of license, a
 30  
  * recipient has the option to distribute your version of this file under
 31  
  * either the CDDL, the GPL Version 2 or to extend the choice of license to
 32  
  * its licensees as provided above.  However, if you add GPL Version 2 code
 33  
  * and therefore, elected the GPL Version 2 license, then the option applies
 34  
  * only if the new code is made subject to such option by the copyright
 35  
  * holder.
 36  
  *
 37  
  */
 38  
 
 39  
 package com.sun.grizzly.suspendable;
 40  
 
 41  
 import com.sun.grizzly.Context;
 42  
 import com.sun.grizzly.PipelineFullException;
 43  
 import com.sun.grizzly.ProtocolFilter;
 44  
 import com.sun.grizzly.Context.KeyRegistrationState;
 45  
 import com.sun.grizzly.Controller;
 46  
 import com.sun.grizzly.DefaultProtocolChain;
 47  
 import com.sun.grizzly.SelectorHandler;
 48  
 import com.sun.grizzly.util.ThreadAttachment;
 49  
 import com.sun.grizzly.util.ThreadAttachment.Mode;
 50  
 import com.sun.grizzly.util.Utils;
 51  
 import com.sun.grizzly.util.WorkerThread;
 52  
 import java.io.IOException;
 53  
 import java.nio.ByteBuffer;
 54  
 import java.nio.channels.SelectionKey;
 55  
 import java.util.Iterator;
 56  
 import java.util.concurrent.ConcurrentHashMap;
 57  
 import java.util.logging.Level;
 58  
 import java.util.logging.Logger;
 59  
 
 60  
 /**
 61  
  * Suspend the processing of the request and associated {@link ProtocolChain}
 62  
  * 
 63  
  * <p>
 64  
  * When a connection is suspended, the framework will not continue the execution
 65  
  * of the ProtocolChain and its associated {@link ProtocolFilter}. Instead the 
 66  
  * framework will wait until either {@link Suspendable#resume} is called, 
 67  
  * {@link Suspendable#cancel} is called or the passed timeout expires.  
 68  
  * 
 69  
  * If {@link Suspendable#resume} is called or the timeout expires
 70  
  * then the ProtocolChain will be resumed from where it has been suspended.
 71  
  * 
 72  
  * If {@link Suspendable#cancel} is called, the ProtocolChain execution will
 73  
  * not be interrupted and the connection automatically closed.
 74  
  * </p>
 75  
  * 
 76  
  * <p> 
 77  
  * A connection can be suspended  before ({@link #Suspend.BEFORE}) the 
 78  
  * ProtocolChain invoke the next ProtocolFilter, or after ({@link #Suspend.AFTER})
 79  
  * all ProtocolFilter has been called (e.g. when {@link #postExecute} is invoked).
 80  
  * </p>
 81  
  * 
 82  
  * <p> This ProtocolFilter <strong>must always</strong> be invoked after some
 83  
  * read operation has occurred.</p>
 84  
  * 
 85  
  * <p> A common usage of SuspendableFilter could be: </p>
 86  
  * <p><pre><code>
 87  
         final ProtocolFilter readFilter = new ReadFilter();
 88  
         final SuspendableFilter suspendFilter = new SuspendableFilter();
 89  
         final ProtocolFilter yourProtocolFilter = new YourProtocolFilter();
 90  
  * 
 91  
         Suspendable suspendable = 
 92  
  *          suspendFilter.suspend("YOUR TOKEN", timeout, attachment, new SuspendableHandler() {
 93  
 
 94  
             public void interupted(A attachment) {
 95  
                 // Add if you need write something before the connection get closed
 96  
             }
 97  
 
 98  
             public void resumed(A attachment) {
 99  
                 // When the connection is resumed
 100  
             }
 101  
 
 102  
             public void expired(A attachment) {
 103  
                 // When the connection is expired.
 104  
         }, {@link #Suspend); 
 105  
  * 
 106  
  * As an example, all client that send the bytes 'grizzly is cool' will be
 107  
  * suspended for 5 seconds before the {@link EchoFilter} is called.
 108  
  *  
 109  
         final ProtocolFilter readFilter = new ReadFilter();
 110  
         final SuspendableFilter suspendFilter = new SuspendableFilter();
 111  
         final ProtocolFilter echoFilter = new EchoFilter();
 112  
  * 
 113  
         suspendable 
 114  
                 = suspendFilter.suspend("grizzly is cool", 5000, null, new SuspendableHandler() {
 115  
 
 116  
             public void interupted(Object attachment) {
 117  
                 System.out.println("interrupted");
 118  
             }
 119  
 
 120  
             public void resumed(Object attachment) {
 121  
                 System.out.println("resumed");
 122  
             }
 123  
 
 124  
             public void expired(Object attachment) {
 125  
                 System.out.println("expired");
 126  
             }
 127  
         }, Suspend.BEFORE);
 128  
  * </code></pre></p>
 129  
  * 
 130  
  * TODO: Add a better patern matching algorithm
 131  
  *       Allow OP_WRITE operation to be suspended as well.
 132  
  * 
 133  
  * @author Jeanfrancois Arcand
 134  
  * @since 1.7.3
 135  
  */
 136  
 public class SuspendableFilter<T> implements ProtocolFilter {
 137  
 
 138  
     /**
 139  
      * Suspend the connection BEFORE of AFTER
 140  
      */
 141  3
     public enum Suspend {BEFORE,AFTER}
 142  
     
 143  
     
 144  
     /**
 145  
      * Cache the pattern and its associated {@link SuspendableHandler} wrapper.
 146  
      */
 147  6
     private ConcurrentHashMap<byte[],SuspendableHandlerWrapper<? extends T>> suspendCandidates
 148  
             = new ConcurrentHashMap<byte[],SuspendableHandlerWrapper<? extends T>>();
 149  
     
 150  
     
 151  
     /**
 152  
      * The current list of suspended {@link SelectionKey}.
 153  
      */
 154  6
     protected ConcurrentHashMap<SelectionKey,KeyHandler> suspendedKeys
 155  
             = new ConcurrentHashMap<SelectionKey,KeyHandler>();
 156  
         
 157  
     
 158  
     /**
 159  
      * Monitor suspended {@link SelectionKey}
 160  
      */
 161  1
     private static SuspendableMonitor suspendableMonitor = new SuspendableMonitor();
 162  
      
 163  
     
 164  
     /**
 165  
      * Logger
 166  
      */
 167  1
     private static Logger logger = Controller.logger();
 168  
      
 169  
     /**
 170  
      * Controller
 171  
      */
 172  
     private Controller controller;
 173  
     
 174  
     /**
 175  
      * {@link DefaultProtocolChain} used to execute resumed connection.
 176  
      */
 177  
     private DefaultProtocolChain protocolChain;
 178  
     
 179  
     
 180  
     /**
 181  
      * The next {@link ProtocolFilter} to invoke when resuming a connection.
 182  
      */
 183  6
     private int nextFilterPosition = 2;
 184  
     
 185  
     
 186  
     // -------------------------------------------------- Constructor ------- //
 187  
     
 188  
     
 189  6
     public SuspendableFilter(){
 190  6
     }
 191  
     
 192  
     
 193  
     /**
 194  
      * Create a new SuspendableFilter, and resume its associated {@link DefaultProtocolChain}
 195  
      * from position {@link #nextFilterPosition}
 196  
      * @param nextFilterPosition {@link #nextFilterPosition}
 197  
      */
 198  0
     public SuspendableFilter(int nextFilterPosition){
 199  0
         this.nextFilterPosition = nextFilterPosition;
 200  0
     }
 201  
 
 202  
     
 203  
     // -------------------------------------------------- Suspend API ------- //
 204  
     
 205  
     /**
 206  
      * Suspend a connection based on a String. Evevry bytes read from the connection
 207  
      * will be inspected and if the String match, the connection will be suspended. 
 208  
      * @param match The String used to decide if a connection and its associated
 209  
      * bytes need to be suspended. By default, the connection will be resumed
 210  
      * after 60 seconds.
 211  
      * @return A {@link Suspendable}
 212  
      */
 213  
     public Suspendable suspend(String match){           
 214  0
         return suspend(match, 60000, null, null);
 215  
     }
 216  
 
 217  
     
 218  
     /**
 219  
      * Suspend a connection based on a String. Evevry bytes read from the connection
 220  
      * will be inspected and if the String match, the connection will be suspended. 
 221  
      * @param match The String used to decide if a connection and its associated
 222  
      * bytes need to be suspended.
 223  
      * @param expireTime the time in milliseconds before a connection is resumed.
 224  
      * @param attachment The object that will be returned when the {@link SuspendableHandler}
 225  
      * methods are invoked.
 226  
      * @param handler A {@link SuspendableHandler} used to get notification about the suspended
 227  
      * connection state.
 228  
      * @return A {@link Suspendable}
 229  
      */    
 230  
     public Suspendable suspend(String match,long expireTime,
 231  
             T attachement,SuspendableHandler<? extends T> sh){   
 232  0
         return suspend(match,expireTime,attachement,sh,Suspend.AFTER);
 233  
     }
 234  
     
 235  
     
 236  
     /**
 237  
      * Suspend a connection based on a String. Evevry bytes read from the connection
 238  
      * will be inspected and if the String match, the connection will be suspended. 
 239  
      * @param match The String used to decide if a connection and its associated
 240  
      * bytes need to be suspended.
 241  
      * @param expireTime the time in milliseconds before a connection is resumed.
 242  
      * @param attachment The object that will be returned when the {@link SuspendableHandler}
 243  
      * methods are invoked.
 244  
      * @param handler A {@link SuspendableHandler} used to get notification about the suspended
 245  
      * connection state.
 246  
      * @param suspendWhen Suspend before or after the next ProtocolChain execution,
 247  
      * @return A {@link Suspendable}
 248  
      */    
 249  
     public Suspendable suspend(String match,long expireTime,
 250  
             T attachement,SuspendableHandler<? extends T> sh, Suspend suspendWhen){           
 251  6
         Suspendable s = new Suspendable(this);
 252  6
         suspendCandidates.put(match.getBytes(),  new SuspendableHandlerWrapper(
 253  
                 sh,attachement, expireTime,s,suspendWhen));
 254  6
         return s;
 255  
     }
 256  
      
 257  
     
 258  
     // ----------------------------------------------ProtocolFilter API ------- //
 259  
     
 260  
     
 261  
     /**
 262  
      * Excute the pattern matching algorithm to determine if a the current
 263  
      * connection must be suspended or not, and when.
 264  
      * 
 265  
      * @param ctx The current {@link Context}
 266  
      * @return true if the ProtocolChain should continue its execution, 
 267  
      * false if the connection has been suspended.
 268  
      * @throws java.io.IOException
 269  
      */
 270  
     public boolean execute(Context ctx) throws IOException { 
 271  9
         WorkerThread wt = (WorkerThread)Thread.currentThread();
 272  9
         ByteBuffer bb = wt.getByteBuffer();
 273  9
         controller = ctx.getController();
 274  
                 
 275  9
         if (ctx.getProtocol() == Controller.Protocol.TCP){
 276  9
             ctx.getSelectionKey().attach(null);
 277  
         } else {
 278  0
             wt.getAttachment().setTimeout(null);        
 279  
         }
 280  
                 
 281  9
         if (protocolChain == null){
 282  6
             if (ctx.getProtocolChain() instanceof DefaultProtocolChain){
 283  6
                 protocolChain = (DefaultProtocolChain)ctx.getProtocolChain();
 284  
             } else {
 285  0
                 throw new IllegalStateException("SuspendableFilter cannot be " +
 286  
                         "used without the DefaultProtocolChain");
 287  
             }
 288  
         }
 289  
         
 290  9
         log("Trying to match " + ctx.getSelectionKey());
 291  
         
 292  
         // This will be quite slow if a lot of registration. 
 293  
         // TODO: Need a better algorithm.
 294  9
         SuspendableHandlerWrapper<? extends T> sh = null;
 295  9
         Iterator<byte[]> iterator = suspendCandidates.keySet().iterator();
 296  9
         byte[] matchBytes = null;
 297  9
         while(iterator.hasNext()){
 298  9
             matchBytes = iterator.next();
 299  9
             if (Utils.findBytes(bb,matchBytes) > -1){
 300  9
                 log("Find match: " + (new String(matchBytes)) 
 301  
                             + " Suspending: " + ctx.getSelectionKey());
 302  9
                 sh = suspendCandidates.get(matchBytes);
 303  9
                 break;
 304  
             }
 305  
         }
 306  
 
 307  9
         if (sh != null){
 308  9
             KeyHandler kh = new KeyHandler();
 309  9
             kh.setSuspendableHandler(sh);
 310  9
             suspendedKeys.put(ctx.getSelectionKey(),kh);
 311  9
             if (sh.getSuspendWhen() == Suspend.BEFORE){
 312  4
                 suspend(ctx,true);
 313  4
                 log("-----> " + ctx.getKeyRegistrationState());
 314  4
                 return false;
 315  
             }
 316  
         }
 317  
         
 318  5
         return true;
 319  
     }
 320  
 
 321  
     
 322  
     /**
 323  
      * Excute the pattern matching algorithm to determine if a the current
 324  
      * connection must be suspended or not, and when.
 325  
      * 
 326  
      * @param ctx The current {@link Context}
 327  
      * @return true if the ProtocolChain should continue its execution, 
 328  
      * false if the connection has been suspended.
 329  
      * @throws java.io.IOException
 330  
      */
 331  
     public boolean postExecute(Context ctx) throws IOException {                
 332  12
         log("<----- " + ctx.getKeyRegistrationState());     
 333  
         
 334  12
         if (!suspendedKeys.isEmpty() 
 335  
                 && ctx.getKeyRegistrationState() == KeyRegistrationState.REGISTER){
 336  5
             suspend(ctx,false);
 337  5
             return false;
 338  
         }
 339  7
         return true;
 340  
     }
 341  
 
 342  
     
 343  
     // -------------------------------------------------- Implementation ------- //
 344  
     
 345  
     /**
 346  
      * Suspend the request by creating the appropriate structure so the state
 347  
      * of the current connection is not lost.
 348  
      * @param ctx The current {@link Context}
 349  
      * @param incomingRequest suspend now of after.
 350  
      */
 351  
     private void suspend(Context ctx, boolean incomingRequest){
 352  
         try {
 353  9
             SelectionKey key = ctx.getSelectionKey();
 354  9
             KeyHandler kh = suspendedKeys.get(key);
 355  
             
 356  9
             SuspendableHandlerWrapper<? extends T> sh = null;
 357  9
             if (kh != null){
 358  9
                 sh = kh.getSuspendableHandler();
 359  
             } else {
 360  0
                 kh = new KeyHandler(); 
 361  
             }
 362  
             
 363  9
             if (kh != null && !incomingRequest){
 364  5
                 if (sh.getSuspendWhen() == Suspend.BEFORE){
 365  0
                     return;
 366  
                 }
 367  
             }
 368  
             
 369  
             // If the users didn't want to be notified.
 370  9
             if (sh == null) {
 371  
                 // TODO: Configurable.
 372  0
                 sh = new SuspendableHandlerWrapper(new SuspendableHandler() {
 373  
 
 374  
                     public void interupted(Object attachment) {
 375  0
                     }
 376  
 
 377  
                     public void expired(Object attachment) {
 378  0
                     }
 379  
 
 380  
                     public void resumed(Object attachment) {
 381  0
                     }
 382  
                 }, null, 30000, new Suspendable(this), Suspend.AFTER);
 383  
             }
 384  9
             sh.setSuspendableFilter(this);
 385  9
             sh.suspendable.setKey(key);
 386  9
             sh.setSelectorHandler(ctx.getSelectorHandler());
 387  
             
 388  9
             kh.setSuspendableHandler(sh);
 389  9
             kh.setKey(key);
 390  9
             WorkerThread workerThread = (WorkerThread) Thread.currentThread();
 391  9
             ThreadAttachment attachment = workerThread.getAttachment();
 392  9
             attachment.setMode(Mode.STORE_ALL);
 393  9
             kh.setThreadAttachment(workerThread.detach());
 394  9
             ctx.setKeyRegistrationState(KeyRegistrationState.NONE);
 395  
            
 396  9
             suspendableMonitor.suspend(kh);
 397  0
         } catch (Throwable ex) {
 398  0
             if (logger.isLoggable(Level.FINE)){
 399  0
                 logger.log(Level.FINE,"suspend",ex);
 400  
             }
 401  9
         }
 402  9
     }
 403  
  
 404  
     
 405  
     /**
 406  
      * Resume the connection by register back the SelectionKey for OP event.
 407  
      * @param key
 408  
      * @return true if the connection was resumed.
 409  
      */
 410  
     protected boolean resume(SelectionKey key){ 
 411  5
         KeyHandler kh = suspendedKeys.remove(key); 
 412  5
         if (kh.getSuspendableHandler() == null){
 413  0
             return false;
 414  
         } 
 415  5
         log("Resuming: " + kh.getSuspendableHandler());
 416  
 
 417  5
         kh.getSuspendableHandler().getSuspendableHandler().
 418  
                 resumed(kh.getSuspendableHandler().getAttachment());
 419  
         
 420  5
         if (kh.getSuspendableHandler().getSuspendWhen() == Suspend.AFTER){
 421  2
             kh.getSuspendableHandler().getSelectorHandler()
 422  
                     .register(key.channel(), SelectionKey.OP_READ);
 423  
         } else {
 424  
             try {
 425  3
                 Context ctx = controller.pollContext(key);
 426  3
                 controller.configureContext(ctx,
 427  
                         kh.getSuspendableHandler().getSelectorHandler());
 428  3
                 ctx.execute(SuspendableContextTask.poll(
 429  
                         protocolChain,kh.getThreadAttachment(),nextFilterPosition));
 430  0
             } catch (PipelineFullException ex) {
 431  0
                 logger.log(Level.SEVERE,"resume exception", ex);
 432  3
             }
 433  
         }
 434  
              
 435  5
         return true;
 436  
     }
 437  
     
 438  
     
 439  
     /**
 440  
      * Cancel the connection by internalling cancelling the associated
 441  
      * {@link ReadableChannel} and it associated {@link SelectionKey}
 442  
      * @param key
 443  
      */
 444  
     protected void cancel(SelectionKey key){
 445  2
         log("Cancelling: " + key);
 446  2
         KeyHandler kh = suspendedKeys.remove(key); 
 447  2
         if (kh == null){
 448  0
             return;
 449  
         } 
 450  
 
 451  2
         if (kh.getSuspendableHandler() == null){
 452  0
             return;
 453  
         } 
 454  2
         kh.getSuspendableHandler().getSuspendableHandler()
 455  
                 .interupted(kh.getSuspendableHandler().getAttachment());
 456  2
         kh.getSuspendableHandler().getSelectorHandler()
 457  
                 .getSelectionKeyHandler().cancel(key);
 458  2
         kh.setThreadAttachment(null);
 459  2
     }
 460  
  
 461  
         
 462  
     
 463  
     /**
 464  
      * Wrapper class around a {@link SuspendableHandler} with add some connection
 465  
      * state.
 466  
      */
 467  9
     protected class SuspendableHandlerWrapper<A>{        
 468  
         public SuspendableHandlerWrapper(SuspendableHandler<A> sh, A attachment, 
 469  6
                 long expireTime, Suspendable suspendable, Suspend suspendWhen){
 470  6
             this.suspendableHandler = sh;
 471  6
             this.attachment = attachment; 
 472  6
             this.expireTime = expireTime;
 473  6
             this.suspendable = suspendable;
 474  6
             this.suspendWhen = suspendWhen;
 475  6
         }
 476  
         
 477  
         private SuspendableHandler<A> suspendableHandler;
 478  
         private A attachment;
 479  6
         private long expireTime = 0L;    
 480  
         private SuspendableFilter suspendableFilter;  
 481  
         private Suspendable suspendable;
 482  
         private SelectorHandler selectorHandler;
 483  
         private Suspend suspendWhen;
 484  
 
 485  
         protected
 486  
 
 487  
         SuspendableHandler<A> getSuspendableHandler() {
 488  10
             return suspendableHandler;
 489  
         }
 490  
 
 491  
         protected void setSuspendableHandler(SuspendableHandler<A> suspendableHandler) {
 492  0
             this.suspendableHandler = suspendableHandler;
 493  0
         }
 494  
 
 495  
         protected A getAttachment() {
 496  10
             return attachment;
 497  
         }
 498  
 
 499  
         protected void setAttachment(A attachment) {
 500  0
             this.attachment = attachment;
 501  0
         }
 502  
 
 503  
         protected long getExpireTime() {
 504  349137
             return expireTime;
 505  
         }
 506  
 
 507  
         protected void setExpireTime(long expireTime) {
 508  0
             this.expireTime = expireTime;
 509  0
         }
 510  
 
 511  
         protected SuspendableFilter getSuspendableFilter() {
 512  3
             return suspendableFilter;
 513  
         }
 514  
 
 515  
         protected void setSuspendableFilter(SuspendableFilter suspendableFilter) {
 516  9
             this.suspendableFilter = suspendableFilter;
 517  9
         }
 518  
 
 519  
         protected Suspendable getSuspendable() {
 520  0
             return suspendable;
 521  
         }
 522  
 
 523  
         protected void setSuspendable(Suspendable suspendable) {
 524  0
             this.suspendable = suspendable;
 525  0
         }
 526  
 
 527  
         protected SelectorHandler getSelectorHandler() {
 528  7
             return selectorHandler;
 529  
         }
 530  
 
 531  
         protected void setSelectorHandler(SelectorHandler selectorHandler) {
 532  9
             this.selectorHandler = selectorHandler;
 533  9
         }
 534  
 
 535  
         protected Suspend getSuspendWhen() {
 536  19
             return suspendWhen;
 537  
         }
 538  
 
 539  
         protected void setSuspendWhen(Suspend suspendWhen) {
 540  0
             this.suspendWhen = suspendWhen;
 541  0
         }
 542  
     }
 543  
     
 544  
     
 545  
     /**
 546  
      * Struc to keep state of the current suspended Connection.
 547  
      */
 548  9
     protected class KeyHandler{        
 549  
         private SelectionKey key;
 550  
         private SelectionKey foreignKey;
 551  
         private SuspendableHandlerWrapper handler;
 552  
         private ThreadAttachment threadAttachment;        
 553  9
         private long registrationTime = 0L;
 554  
 
 555  
         protected
 556  
 
 557  
         SelectionKey getKey() {
 558  21
             return key;
 559  
         }
 560  
 
 561  
         protected void setKey(SelectionKey key) {
 562  9
             this.key = key;
 563  9
         }
 564  
 
 565  
         protected SelectionKey getForeignKey() {
 566  9
             return foreignKey;
 567  
         }
 568  
 
 569  
         protected void setForeignKey(SelectionKey foreignKey) {
 570  9
             this.foreignKey = foreignKey;
 571  9
         }
 572  
 
 573  
         protected SuspendableHandlerWrapper getSuspendableHandler() {
 574  349193
             return handler;
 575  
         }
 576  
 
 577  
         protected void setSuspendableHandler(SuspendableHandlerWrapper sh) {
 578  18
             this.handler = sh;
 579  18
         }
 580  
 
 581  
         protected ThreadAttachment getThreadAttachment() {
 582  3
             return threadAttachment;
 583  
         }
 584  
 
 585  
         protected void setThreadAttachment(ThreadAttachment threadAttachment) {
 586  11
             this.threadAttachment = threadAttachment;
 587  11
         }
 588  
 
 589  
         protected long getRegistrationTime() {
 590  349160
             return registrationTime;
 591  
         }
 592  
 
 593  
         protected void setRegistrationTime(long registrationTime) {
 594  12
             this.registrationTime = registrationTime;
 595  12
         }        
 596  
     }
 597  
     
 598  
    
 599  
     private void log(String message){
 600  41
         if (logger.isLoggable(Level.FINE)) {
 601  0
             logger.log(Level.FINE, message);
 602  
         }         
 603  41
     }
 604  
 }