Coverage Report - com.sun.grizzly.filter.SSLReadFilter
 
Classes in this File Line Coverage Branch Coverage Complexity
SSLReadFilter
56 %
107/192
46 %
34/74
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.filter;
 40  
 
 41  
 import com.sun.grizzly.Context;
 42  
 import com.sun.grizzly.Controller;
 43  
 import com.sun.grizzly.ProtocolFilter;
 44  
 import com.sun.grizzly.SSLConfig;
 45  
 import com.sun.grizzly.util.InputReader;
 46  
 import com.sun.grizzly.util.SSLUtils;
 47  
 import com.sun.grizzly.util.ThreadAttachment;
 48  
 import com.sun.grizzly.util.ThreadAttachment.Mode;
 49  
 import com.sun.grizzly.util.WorkerThread;
 50  
 import java.io.EOFException;
 51  
 import java.io.IOException;
 52  
 import java.nio.ByteBuffer;
 53  
 import java.nio.channels.SelectionKey;
 54  
 import java.nio.channels.SocketChannel;
 55  
 import java.util.ArrayList;
 56  
 import java.util.logging.Level;
 57  
 import javax.net.ssl.SSLContext;
 58  
 import javax.net.ssl.SSLEngine;
 59  
 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
 60  
 import javax.net.ssl.SSLException;
 61  
 
 62  
 /**
 63  
  * Simple ProtocolFilter implementation which execute an SSL handshake and
 64  
  * decrypt the bytes, the pass the control to the next filter.
 65  
  *
 66  
  * @author Jeanfrancois Arcand
 67  
  */
 68  
 public class SSLReadFilter implements ProtocolFilter{
 69  
     /**
 70  
      * The {@link SSLContext} associated with the SSL implementation
 71  
      * we are running on.
 72  
      */
 73  
     protected SSLContext sslContext;
 74  
     
 75  
     
 76  
     /**
 77  
      * The list of cipher suite
 78  
      */
 79  5
     private String[] enabledCipherSuites = null;
 80  
     
 81  
     
 82  
     /**
 83  
      * the list of protocols
 84  
      */
 85  5
     private String[] enabledProtocols = null;
 86  
     
 87  
     
 88  
     /**
 89  
      * Client mode when handshaking.
 90  
      */
 91  5
     private boolean clientMode = false;
 92  
     
 93  
     
 94  
     /**
 95  
      * Require client Authentication.
 96  
      */
 97  5
     private boolean needClientAuth = false;
 98  
     
 99  
     
 100  
     /**
 101  
      * True when requesting authentication.
 102  
      */
 103  5
     private boolean wantClientAuth = false;
 104  
     
 105  
     
 106  
     /**
 107  
      * Has the enabled protocol configured.
 108  
      */
 109  5
     private boolean isProtocolConfigured = false;
 110  
     
 111  
     
 112  
     /**
 113  
      * Has the enabled Cipher configured.
 114  
      */
 115  5
     private boolean isCipherConfigured = false;
 116  
     
 117  
     
 118  
     /**
 119  
      * Encrypted ByteBuffer default size.
 120  
      */
 121  5
     protected int inputBBSize = 5 * 4096;
 122  
     
 123  
     
 124  5
     public SSLReadFilter() {
 125  5
     }
 126  
 
 127  
     
 128  
     public boolean execute(Context ctx) throws IOException {
 129  53201
         boolean result = true;
 130  53201
         int count = 0;
 131  53201
         Throwable exception = null;
 132  53201
         SelectionKey key = ctx.getSelectionKey();
 133  
         WorkerThread workerThread;
 134  
         try{
 135  53201
             workerThread = (WorkerThread)Thread.currentThread();   
 136  0
         } catch (ClassCastException ex){
 137  0
             throw new IllegalStateException(ex.getMessage());
 138  53201
         }
 139  
 
 140  53201
         SSLEngine sslEngine = workerThread.getSSLEngine();
 141  53201
         if (sslEngine == null) {
 142  123
             sslEngine = newSSLEngine(key);
 143  123
             workerThread.setSSLEngine(sslEngine);
 144  123
             ThreadAttachment attachment = workerThread.updateAttachment(Mode.SSL_ENGINE);
 145  123
             key.attach(attachment);
 146  
         }
 147  
 
 148  53201
         boolean hasHandshake = sslEngine.getSession().isValid();
 149  
         try {
 150  53201
             SSLUtils.allocateThreadBuffers(inputBBSize);
 151  
             
 152  53201
             if (hasHandshake) {
 153  53078
                 count = doRead(key);
 154  123
             } else if (doHandshake(key, SSLUtils.getReadTimeout())) {
 155  122
                 hasHandshake = true;
 156  
                 // set "no available data" for secured output buffer
 157  122
                 ByteBuffer outputBB = workerThread.getOutputBB();
 158  122
                 outputBB.limit(outputBB.position());
 159  122
             } else {
 160  1
                 count = -1;
 161  
             }
 162  0
         } catch (IOException ex) {
 163  0
             exception = ex;
 164  0
             log("SSLReadFilter.execute",ex);
 165  0
         } catch (Throwable ex) {
 166  0
             exception = ex;    
 167  0
             log("SSLReadFilter.execute",ex);
 168  
         } finally {
 169  53201
             if (exception != null || count == -1){
 170  118
                 ctx.setAttribute(Context.THROWABLE,exception);
 171  118
                 ctx.setKeyRegistrationState(
 172  
                         Context.KeyRegistrationState.CANCEL);
 173  118
                 result = false;
 174  
             }
 175  
         }     
 176  53201
         return result;
 177  
     }
 178  
 
 179  
     
 180  
     /**
 181  
      * If no bytes were available, close the connection by cancelling the
 182  
      * SelectionKey. If bytes were available, register the SelectionKey
 183  
      * for new bytes.
 184  
      *
 185  
      * @return <tt>true</tt> if the previous ProtocolFilter postExecute method
 186  
      *         needs to be invoked.
 187  
      */
 188  
     public boolean postExecute(Context ctx) throws IOException {
 189  53201
         if (ctx.getKeyRegistrationState()
 190  
                 == Context.KeyRegistrationState.CANCEL){
 191  118
             ctx.getSelectorHandler().getSelectionKeyHandler().
 192  
                     cancel(ctx.getSelectionKey());
 193  53083
         } else if (ctx.getKeyRegistrationState()
 194  
                 == Context.KeyRegistrationState.REGISTER){            
 195  53083
             saveSecuredBufferRemainders(ctx.getSelectionKey());
 196  53083
             ctx.getSelectorHandler().register(ctx.getSelectionKey(),
 197  
                     SelectionKey.OP_READ);
 198  53083
             ctx.setKeyRegistrationState(Context.KeyRegistrationState.NONE);
 199  
         }
 200  53201
         return true;
 201  
     }
 202  
     
 203  
     
 204  
     /**
 205  
      * Execute a non blocking SSL handshake.
 206  
      * @param key {@link SelectionKey}
 207  
      * @param timeout 
 208  
      * @return 
 209  
      * @throws java.io.IOException 
 210  
      */    
 211  
     private static boolean doHandshake(SelectionKey key,int timeout) throws IOException{
 212  123
         final WorkerThread workerThread = 
 213  
                 (WorkerThread)Thread.currentThread();
 214  123
         ByteBuffer byteBuffer = workerThread.getByteBuffer();
 215  123
         ByteBuffer outputBB = workerThread.getOutputBB();
 216  123
         ByteBuffer inputBB = workerThread.getInputBB();
 217  123
         SSLEngine sslEngine = workerThread.getSSLEngine();
 218  
         
 219  123
         HandshakeStatus handshakeStatus = HandshakeStatus.NEED_UNWRAP;
 220  
         
 221  123
         boolean OK = true;    
 222  
         try{ 
 223  123
             byteBuffer = SSLUtils.doHandshake
 224  
                          ((SocketChannel) key.channel(), byteBuffer, inputBB,
 225  
                     outputBB, sslEngine, handshakeStatus, timeout);
 226  122
             if (doRead(key) == -1){
 227  0
                 throw new EOFException();
 228  
             }
 229  1
         } catch (IOException ex) {
 230  1
             log("doHandshake", ex);
 231  1
             OK = false;
 232  122
         }
 233  123
         return OK;
 234  
     }    
 235  
     
 236  
     
 237  
     private static int doRead(SelectionKey key) {
 238  53200
         final WorkerThread workerThread =
 239  
                 (WorkerThread) Thread.currentThread();
 240  53200
         ByteBuffer byteBuffer = workerThread.getByteBuffer();
 241  53200
         ByteBuffer outputBB = workerThread.getOutputBB();
 242  53200
         ByteBuffer inputBB = workerThread.getInputBB();
 243  53200
         SSLEngine sslEngine = workerThread.getSSLEngine();
 244  
 
 245  53200
         int count = -1;
 246  
         try {
 247  
             // Read first bytes to avoid continuing if the client
 248  
             // closed the connection.
 249  53200
             int initialBufferPosition = byteBuffer.position();
 250  
 
 251  
             try {
 252  53200
                 count = ((SocketChannel) key.channel()).read(inputBB);
 253  0
             } catch(IOException e) {
 254  0
                 log("Exception during SSL read.", e);
 255  0
                 count = -1;
 256  53200
             }
 257  
             
 258  53200
             if (count > -1 || inputBB.position() > 0) {
 259  
                 // Decrypt the bytes we just read.
 260  53083
                 if (Controller.logger().isLoggable(Level.FINE)) {
 261  0
                     Controller.logger().log(Level.FINE,
 262  
                             "SSLReadFilter. Read: " + count +
 263  
                             " Calling unwrapAll. InputBB: " +
 264  
                             inputBB + " byteBuffer: " + byteBuffer);
 265  
                 }
 266  
 
 267  53083
                 int initialInputBBPosition = inputBB.position();
 268  53083
                 byteBuffer =
 269  
                         SSLUtils.unwrapAll(byteBuffer, inputBB, sslEngine);
 270  53083
                 workerThread.setInputBB(inputBB);
 271  53083
                 workerThread.setOutputBB(outputBB);
 272  53083
                 workerThread.setByteBuffer(byteBuffer);
 273  
                 
 274  53083
                 if (count == -1 && byteBuffer.position() != initialBufferPosition) {
 275  0
                     return initialInputBBPosition;
 276  
                 }
 277  
             }
 278  53200
             return count;
 279  0
         } catch (IOException ex) {
 280  0
             log("Exception during SSL read.", ex);
 281  0
             return -1;
 282  
         } finally {
 283  53200
             if (count == -1) {
 284  
                 try {
 285  117
                     sslEngine.closeInbound();
 286  0
                 } catch (SSLException ex) {
 287  53317
                 }
 288  
             }
 289  
         }
 290  
     }
 291  
     
 292  
     
 293  
     /**
 294  
      * Get the peer certificate list by initiating a new handshake.
 295  
      * @param key {@link SelectionKey}
 296  
      * @param needClientAuth 
 297  
      * @return Object[] An array of X509Certificate.
 298  
      * @throws java.io.IOException 
 299  
      */
 300  
     public static Object[] doPeerCertificateChain(SelectionKey key,
 301  
             boolean needClientAuth) throws IOException {
 302  
         
 303  0
         final WorkerThread workerThread = 
 304  
                 (WorkerThread)Thread.currentThread();
 305  0
         ByteBuffer byteBuffer = workerThread.getByteBuffer();
 306  0
         ByteBuffer inputBB = workerThread.getInputBB();
 307  0
         ByteBuffer outputBB = workerThread.getOutputBB();
 308  0
         SSLEngine sslEngine = workerThread.getSSLEngine();
 309  
         
 310  0
         return SSLUtils.doPeerCertificateChain((SocketChannel) key.channel(), 
 311  
                 byteBuffer, inputBB, outputBB, sslEngine, needClientAuth, 
 312  
                 InputReader.getDefaultReadTimeout());
 313  
     }
 314  
     
 315  
     
 316  
     /**
 317  
      * Return a new configured{@link SSLEngine}
 318  
      * @return a new configured{@link SSLEngine}
 319  
      */
 320  
     protected SSLEngine newSSLEngine(){
 321  123
         SSLEngine sslEngine = sslContext.createSSLEngine();
 322  123
         if (enabledCipherSuites != null){            
 323  0
             if (!isCipherConfigured){
 324  0
                 enabledCipherSuites = configureEnabledCiphers(sslEngine,
 325  
                                                         enabledCipherSuites);
 326  0
                 isCipherConfigured = true;
 327  
             }
 328  0
             sslEngine.setEnabledCipherSuites(enabledCipherSuites);
 329  
         }
 330  
         
 331  123
         if (enabledProtocols != null){
 332  0
             if (!isProtocolConfigured) {
 333  0
                 enabledProtocols = configureEnabledProtocols(sslEngine,
 334  
                                                     enabledProtocols);
 335  0
                 isProtocolConfigured = true;
 336  
             }
 337  0
             sslEngine.setEnabledProtocols(enabledProtocols);
 338  
         }
 339  123
         sslEngine.setUseClientMode(clientMode);
 340  123
         return sslEngine;
 341  
     }
 342  
     
 343  
     
 344  
     /**
 345  
      * Configure and return an instance of SSLEngine
 346  
      * @param key  a {@link SelectionKey}
 347  
      * @return  a configured instance of{@link SSLEngine}
 348  
      */
 349  
     protected SSLEngine newSSLEngine(SelectionKey key){
 350  123
         SSLEngine sslEngine = null;
 351  123
         if (key.attachment() instanceof ThreadAttachment) {
 352  1
             sslEngine = ((WorkerThread) Thread.currentThread()).getSSLEngine();
 353  
         }
 354  
         
 355  123
         if (sslEngine == null) {
 356  123
            sslEngine = newSSLEngine();
 357  
         }
 358  
         
 359  123
         sslEngine.setWantClientAuth(wantClientAuth);
 360  123
         sslEngine.setNeedClientAuth(needClientAuth);
 361  123
         return sslEngine;
 362  
     }
 363  
            
 364  
     /**
 365  
      * Configures SSL settings. <code>SSLConfig</code> contains all the parameters
 366  
      * required to build{@link SSLEngine}. There will be no need to call
 367  
      * four methods: setSSLContext, setClientMode, setWantClientAuth, 
 368  
      * setNeedClientAuth.
 369  
      * @param sslConfig <code>SSLConfig</code> configuration
 370  
      */
 371  
     public void configure(SSLConfig sslConfig) {
 372  0
         sslContext = sslConfig.createSSLContext();
 373  0
         wantClientAuth = sslConfig.isWantClientAuth();
 374  0
         needClientAuth = sslConfig.isNeedClientAuth();
 375  0
         clientMode = sslConfig.isClientMode();
 376  0
     }
 377  
     
 378  
     /**
 379  
      * Set the SSLContext required to support SSL over NIO.
 380  
      * @param sslContext {@link SSLContext}
 381  
      */
 382  
     public void setSSLContext(SSLContext sslContext){
 383  5
         this.sslContext = sslContext;
 384  5
     }
 385  
     
 386  
     
 387  
     /**
 388  
      * Return the SSLContext required to support SSL over NIO.
 389  
      * @return {@link SSLContext}
 390  
      */    
 391  
     public SSLContext getSSLContext(){
 392  0
         return sslContext;
 393  
     }
 394  
     
 395  
     
 396  
     /**
 397  
      * Returns the list of cipher suites to be enabled when {@link SSLEngine}
 398  
      * is initialized.
 399  
      *
 400  
      * @return <tt>null</tt> means 'use {@link SSLEngine}'s default.'
 401  
      */
 402  
     public String[] getEnabledCipherSuites() {
 403  0
         return enabledCipherSuites;
 404  
     }
 405  
     
 406  
     
 407  
     /**
 408  
      * Sets the list of cipher suites to be enabled when {@link SSLEngine}
 409  
      * is initialized.
 410  
      * @param enabledCipherSuites 
 411  
      */
 412  
     public void setEnabledCipherSuites(String[] enabledCipherSuites) {
 413  0
         this.enabledCipherSuites = enabledCipherSuites;
 414  0
     }
 415  
     
 416  
     
 417  
     /**
 418  
      * Returns the list of protocols to be enabled when {@link SSLEngine}
 419  
      * is initialized.
 420  
      *
 421  
      * @return <tt>null</tt> means 'use {@link SSLEngine}'s default.'
 422  
      */
 423  
     public String[] getEnabledProtocols() {
 424  0
         return enabledProtocols;
 425  
     }
 426  
     
 427  
     
 428  
     /**
 429  
      * Sets the list of protocols to be enabled when {@link SSLEngine}
 430  
      * is initialized.
 431  
      *
 432  
      * @param enabledProtocols <tt>null</tt> means 'use {@link SSLEngine}'s default.'
 433  
      */
 434  
     public void setEnabledProtocols(String[] enabledProtocols) {
 435  0
         this.enabledProtocols = enabledProtocols;
 436  0
     }
 437  
     
 438  
     
 439  
     /**
 440  
      * Returns <tt>true</tt> if the SSlEngine is set to use client mode
 441  
      * when handshaking.
 442  
      * @return true / false
 443  
      */
 444  
     public boolean isClientMode() {
 445  0
         return clientMode;
 446  
     }
 447  
     
 448  
     
 449  
     /**
 450  
      * Configures the engine to use client (or server) mode when handshaking.
 451  
      * @param clientMode 
 452  
      */    
 453  
     public void setClientMode(boolean clientMode) {
 454  0
         this.clientMode = clientMode;
 455  0
     }
 456  
     
 457  
     
 458  
     /**
 459  
      * Returns <tt>true</tt> if the SSLEngine will <em>require</em>
 460  
      * client authentication.
 461  
      * @return 
 462  
      */   
 463  
     public boolean isNeedClientAuth() {
 464  0
         return needClientAuth;
 465  
     }
 466  
     
 467  
     
 468  
     /**
 469  
      * Configures the engine to <em>require</em> client authentication.
 470  
      * @param needClientAuth 
 471  
      */    
 472  
     public void setNeedClientAuth(boolean needClientAuth) {
 473  0
         this.needClientAuth = needClientAuth;
 474  0
     }
 475  
     
 476  
     
 477  
     /**
 478  
      * Returns <tt>true</tt> if the engine will <em>request</em> client
 479  
      * authentication.
 480  
      * @return 
 481  
      */   
 482  
     public boolean isWantClientAuth() {
 483  0
         return wantClientAuth;
 484  
     }
 485  
     
 486  
     
 487  
     /**
 488  
      * Configures the engine to <em>request</em> client authentication.
 489  
      * @param wantClientAuth 
 490  
      */    
 491  
     public void setWantClientAuth(boolean wantClientAuth) {
 492  0
         this.wantClientAuth = wantClientAuth;
 493  0
     }
 494  
     
 495  
     
 496  
     /**
 497  
      * Return the list of allowed protocol.
 498  
      * @return String[] an array of supported protocols.
 499  
      */
 500  
     private final static String[] configureEnabledProtocols(
 501  
             SSLEngine sslEngine, String[] requestedProtocols){
 502  
         
 503  0
         String[] supportedProtocols = sslEngine.getSupportedProtocols();
 504  0
         String[] protocols = null;
 505  0
         ArrayList<String> list = null;
 506  0
         for(String supportedProtocol: supportedProtocols){        
 507  
             /*
 508  
              * Check to see if the requested protocol is among the
 509  
              * supported protocols, i.e., may be enabled
 510  
              */
 511  0
             for(String protocol: requestedProtocols) {
 512  0
                 protocol = protocol.trim();
 513  0
                 if (supportedProtocol.equals(protocol)) {
 514  0
                     if (list == null) {
 515  0
                         list = new ArrayList<String>();
 516  
                     }
 517  0
                     list.add(protocol);
 518  0
                     break;
 519  
                 }
 520  
             }
 521  
         } 
 522  
 
 523  0
         if (list != null) {
 524  0
             protocols = list.toArray(new String[list.size()]);                
 525  
         }
 526  
  
 527  0
         return protocols;
 528  
     }
 529  
     
 530  
     
 531  
     /**
 532  
      * Determines the SSL cipher suites to be enabled.
 533  
      *
 534  
      * @return Array of SSL cipher suites to be enabled, or null if none of the
 535  
      * requested ciphers are supported
 536  
      */
 537  
     private final static String[] configureEnabledCiphers(SSLEngine sslEngine,
 538  
             String[] requestedCiphers) {
 539  
 
 540  0
         String[] supportedCiphers = sslEngine.getSupportedCipherSuites();
 541  0
         String[] ciphers = null;
 542  0
         ArrayList<String> list = null;
 543  0
         for(String supportedCipher: supportedCiphers){        
 544  
             /*
 545  
              * Check to see if the requested protocol is among the
 546  
              * supported protocols, i.e., may be enabled
 547  
              */
 548  0
             for(String cipher: requestedCiphers) {
 549  0
                 cipher = cipher.trim();
 550  0
                 if (supportedCipher.equals(cipher)) {
 551  0
                     if (list == null) {
 552  0
                         list = new ArrayList<String>();
 553  
                     }
 554  0
                     list.add(cipher);
 555  0
                     break;
 556  
                 }
 557  
             }
 558  
         } 
 559  
 
 560  0
         if (list != null) {
 561  0
             ciphers = list.toArray(new String[list.size()]);                
 562  
         }
 563  
  
 564  0
         return ciphers;
 565  
     }
 566  
 
 567  
     private void saveSecuredBufferRemainders(SelectionKey selectionKey) {
 568  53083
         ThreadAttachment attachment = 
 569  
                 (ThreadAttachment) selectionKey.attachment();
 570  
         
 571  53083
         WorkerThread workerThread = (WorkerThread) Thread.currentThread();   
 572  
 
 573  53083
         if (attachment == null || workerThread.getAttachment() != attachment) {
 574  0
             Controller.logger().log(Level.FINE, 
 575  
                     "SelectionKey ThreadAttachment is NULL or doesn't " +
 576  
                     "correspond to the current thread, when saving buffers");
 577  0
             return;
 578  
         }
 579  
         
 580  53083
         ByteBuffer inputBB = workerThread.getInputBB();
 581  53083
         if (inputBB != null && inputBB.hasRemaining()) {
 582  53083
             workerThread.updateAttachment(attachment.getMode() | Mode.INPUT_BB);
 583  
         } else {
 584  0
             workerThread.updateAttachment(attachment.getMode() & 
 585  
                     (Integer.MAX_VALUE ^ Mode.INPUT_BB));
 586  
         }
 587  
 
 588  53083
         ByteBuffer outputBB = workerThread.getOutputBB();
 589  53083
         if (outputBB != null && outputBB.hasRemaining()) {
 590  101
             workerThread.updateAttachment(attachment.getMode() | Mode.OUTPUT_BB);
 591  
         } else {
 592  52982
             workerThread.updateAttachment(attachment.getMode() & 
 593  
                     (Integer.MAX_VALUE ^ Mode.OUTPUT_BB));
 594  
         }
 595  53083
     }
 596  
     
 597  
     /**
 598  
      * Log a message/exception.
 599  
      * @param msg <code>String</code>
 600  
      * @param t <code>Throwable</code>
 601  
      */
 602  
     protected static void log(String msg, Throwable t) {
 603  1
         if (Controller.logger().isLoggable(Level.FINE)) {
 604  0
             Controller.logger().log(Level.FINE, msg, t);
 605  
         }
 606  1
     }   
 607  
 }