All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.tomcat.util.net.NioEndpoint Maven / Gradle / Ivy

/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package org.apache.tomcat.util.net;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.nio.channels.CancelledKeyException;
import java.nio.channels.FileChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.WritableByteChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.X509KeyManager;

import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.IntrospectionUtils;
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
import org.apache.tomcat.util.net.SecureNioChannel.ApplicationBufferHandler;
import org.apache.tomcat.util.net.jsse.NioX509KeyManager;

/**
 * NIO tailored thread pool, providing the following services:
 * 
    *
  • Socket acceptor thread
  • *
  • Socket poller thread
  • *
  • Worker threads pool
  • *
* * When switching to Java 5, there's an opportunity to use the virtual * machine's thread pool. * * @author Mladen Turk * @author Remy Maucherat * @author Filip Hanik */ public class NioEndpoint extends AbstractEndpoint { // -------------------------------------------------------------- Constants private static final Log log = LogFactory.getLog(NioEndpoint.class); public static final int OP_REGISTER = 0x100; //register interest op public static final int OP_CALLBACK = 0x200; //callback interest op // ----------------------------------------------------------------- Fields protected NioSelectorPool selectorPool = new NioSelectorPool(); /** * Server socket "pointer". */ protected ServerSocketChannel serverSock = null; /** * use send file */ protected boolean useSendfile = true; /** * The size of the OOM parachute. */ protected int oomParachute = 1024*1024; /** * The oom parachute, when an OOM error happens, * will release the data, giving the JVM instantly * a chunk of data to be able to recover with. */ protected byte[] oomParachuteData = null; /** * Make sure this string has already been allocated */ protected static final String oomParachuteMsg = "SEVERE:Memory usage is low, parachute is non existent, your system may start failing."; /** * Keep track of OOM warning messages. */ long lastParachuteCheck = System.currentTimeMillis(); /** * */ protected volatile CountDownLatch stopLatch = null; /** * Cache for SocketProcessor objects */ protected ConcurrentLinkedQueue processorCache = new ConcurrentLinkedQueue() { private static final long serialVersionUID = 1L; protected AtomicInteger size = new AtomicInteger(0); @Override public boolean offer(SocketProcessor sc) { sc.reset(null,null); boolean offer = socketProperties.getProcessorCache()==-1?true:size.get() keyCache = new ConcurrentLinkedQueue() { private static final long serialVersionUID = 1L; protected AtomicInteger size = new AtomicInteger(0); @Override public boolean offer(KeyAttachment ka) { ka.reset(); boolean offer = socketProperties.getKeyCache()==-1?true:size.get() eventCache = new ConcurrentLinkedQueue() { private static final long serialVersionUID = 1L; protected AtomicInteger size = new AtomicInteger(0); @Override public boolean offer(PollerEvent pe) { pe.reset(); boolean offer = socketProperties.getEventCache()==-1?true:size.get() nioChannels = new ConcurrentLinkedQueue() { private static final long serialVersionUID = 1L; protected AtomicInteger size = new AtomicInteger(0); protected AtomicInteger bytes = new AtomicInteger(0); @Override public boolean offer(NioChannel socket) { boolean offer = socketProperties.getBufferPool()==-1?true:size.get()10000) { try { log.fatal(oomParachuteMsg); }catch (Throwable t) { ExceptionUtils.handleThrowable(t); System.err.println(oomParachuteMsg); } lastParachuteCheck = System.currentTimeMillis(); } } protected boolean reclaimParachute(boolean force) { if ( oomParachuteData != null ) return true; if ( oomParachute > 0 && ( force || (Runtime.getRuntime().freeMemory() > (oomParachute*2))) ) oomParachuteData = new byte[oomParachute]; return oomParachuteData != null; } protected void releaseCaches() { this.keyCache.clear(); this.nioChannels.clear(); this.processorCache.clear(); if ( handler != null ) handler.recycle(); } // --------------------------------------------------------- Public Methods /** * Number of keepalive sockets. */ public int getKeepAliveCount() { if (pollers == null) { return 0; } else { int sum = 0; for (int i=0; i0) reclaimParachute(true); selectorPool.open(); } public KeyManager[] wrap(KeyManager[] managers) { if (managers==null) return null; KeyManager[] result = new KeyManager[managers.length]; for (int i=0; i 0 ) engine.setEnabledCipherSuites(getCiphersArray()); if ( getSslEnabledProtocolsArray().length > 0 ) engine.setEnabledProtocols(getSslEnabledProtocolsArray()); return engine; } /** * Returns true if a worker thread is available for processing. * @return boolean */ protected boolean isWorkerAvailable() { return true; } public boolean processSocket(NioChannel socket, SocketStatus status, boolean dispatch) { try { KeyAttachment attachment = (KeyAttachment)socket.getAttachment(false); attachment.setCometNotify(false); //will get reset upon next reg SocketProcessor sc = processorCache.poll(); if ( sc == null ) sc = new SocketProcessor(socket,status); else sc.reset(socket,status); if ( dispatch && getExecutor()!=null ) getExecutor().execute(sc); else sc.run(); } catch (RejectedExecutionException rx) { log.warn("Socket processing request was rejected for:"+socket,rx); return false; } catch (Throwable t) { ExceptionUtils.handleThrowable(t); // This means we got an OOM or similar creating a thread, or that // the pool and its queue are full log.error(sm.getString("endpoint.process.fail"), t); return false; } return true; } @Override protected Log getLog() { return log; } // --------------------------------------------------- Acceptor Inner Class /** * Server socket acceptor thread. */ protected class Acceptor extends AbstractEndpoint.Acceptor { /** * The background thread that listens for incoming TCP/IP connections and * hands them off to an appropriate processor. */ @Override public void run() { int errorDelay = 0; // Loop until we receive a shutdown command while (running) { // Loop if endpoint is paused while (paused && running) { state = AcceptorState.PAUSED; try { Thread.sleep(50); } catch (InterruptedException e) { // Ignore } } if (!running) { break; } state = AcceptorState.RUNNING; try { //if we have reached max connections, wait countUpOrAwaitConnection(); SocketChannel socket = null; try { // Accept the next incoming connection from the server // socket socket = serverSock.accept(); } catch (IOException ioe) { // Introduce delay if necessary errorDelay = handleExceptionWithDelay(errorDelay); // re-throw throw ioe; } // Successful accept, reset the error delay errorDelay = 0; // Hand this socket off to an appropriate processor //TODO FIXME - this is currently a blocking call, meaning we will be blocking //further accepts until there is a thread available. if ( running && (!paused) && socket != null ) { // setSocketOptions() will add channel to the poller // if successful if (!setSocketOptions(socket)) { try { socket.socket().close(); socket.close(); } catch (IOException ix) { if (log.isDebugEnabled()) log.debug("", ix); } } } } catch (SocketTimeoutException sx) { //normal condition } catch (IOException x) { if (running) { log.error(sm.getString("endpoint.accept.fail"), x); } } catch (OutOfMemoryError oom) { try { oomParachuteData = null; releaseCaches(); log.error("", oom); }catch ( Throwable oomt ) { try { try { System.err.println(oomParachuteMsg); oomt.printStackTrace(); }catch (Throwable letsHopeWeDontGetHere){ ExceptionUtils.handleThrowable(letsHopeWeDontGetHere); } }catch (Throwable letsHopeWeDontGetHere){ ExceptionUtils.handleThrowable(letsHopeWeDontGetHere); } } } catch (Throwable t) { ExceptionUtils.handleThrowable(t); log.error(sm.getString("endpoint.accept.fail"), t); } }//while state = AcceptorState.ENDED; }//run } // ----------------------------------------------------- Poller Inner Classes /** * * PollerEvent, cacheable object for poller events to avoid GC */ public static class PollerEvent implements Runnable { protected NioChannel socket; protected int interestOps; protected KeyAttachment key; public PollerEvent(NioChannel ch, KeyAttachment k, int intOps) { reset(ch, k, intOps); } public void reset(NioChannel ch, KeyAttachment k, int intOps) { socket = ch; interestOps = intOps; key = k; } public void reset() { reset(null, null, 0); } @Override public void run() { if ( interestOps == OP_REGISTER ) { try { socket.getIOChannel().register(socket.getPoller().getSelector(), SelectionKey.OP_READ, key); } catch (Exception x) { log.error("", x); } } else { final SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector()); try { boolean cancel = false; if (key != null) { final KeyAttachment att = (KeyAttachment) key.attachment(); if ( att!=null ) { //handle callback flag if (att.getComet() && (interestOps & OP_CALLBACK) == OP_CALLBACK ) { att.setCometNotify(true); } else { att.setCometNotify(false); } interestOps = (interestOps & (~OP_CALLBACK));//remove the callback flag att.access();//to prevent timeout //we are registering the key to start with, reset the fairness counter. int ops = key.interestOps() | interestOps; att.interestOps(ops); key.interestOps(ops); } else { cancel = true; } } else { cancel = true; } if ( cancel ) socket.getPoller().cancelledKey(key,SocketStatus.ERROR,false); }catch (CancelledKeyException ckx) { try { socket.getPoller().cancelledKey(key,SocketStatus.DISCONNECT,true); }catch (Exception ignore) {} } }//end if }//run @Override public String toString() { return super.toString()+"[intOps="+this.interestOps+"]"; } } /** * Poller class. */ public class Poller implements Runnable { protected Selector selector; protected ConcurrentLinkedQueue events = new ConcurrentLinkedQueue(); protected volatile boolean close = false; protected long nextExpiration = 0;//optimize expiration handling protected AtomicLong wakeupCounter = new AtomicLong(0l); protected volatile int keyCount = 0; public Poller() throws IOException { synchronized (Selector.class) { // Selector.open() isn't thread safe // http://bugs.sun.com/view_bug.do?bug_id=6427854 // Affects 1.6.0_29, fixed in 1.7.0_01 this.selector = Selector.open(); } } public int getKeyCount() { return keyCount; } public Selector getSelector() { return selector;} /** * Destroy the poller. */ protected void destroy() { // Wait for polltime before doing anything, so that the poller threads // exit, otherwise parallel closure of sockets which are still // in the poller can cause problems close = true; events.clear(); selector.wakeup(); } public void addEvent(Runnable event) { events.offer(event); if ( wakeupCounter.incrementAndGet() == 0 ) selector.wakeup(); } public void cometInterest(NioChannel socket) { KeyAttachment att = (KeyAttachment)socket.getAttachment(false); add(socket,att.getCometOps()); if ( (att.getCometOps()&OP_CALLBACK) == OP_CALLBACK ) { nextExpiration = 0; //force the check for faster callback selector.wakeup(); } } /** * Add specified socket and associated pool to the poller. The socket will * be added to a temporary array, and polled first after a maximum amount * of time equal to pollTime (in most cases, latency will be much lower, * however). * * @param socket to add to the poller */ public void add(final NioChannel socket) { add(socket,SelectionKey.OP_READ); } public void add(final NioChannel socket, final int interestOps) { PollerEvent r = eventCache.poll(); if ( r==null) r = new PollerEvent(socket,null,interestOps); else r.reset(socket,null,interestOps); addEvent(r); } /** * Processes events in the event queue of the Poller. * * @return true if some events were processed, * false if queue was empty */ public boolean events() { boolean result = false; Runnable r = null; while ( (r = events.poll()) != null ) { result = true; try { r.run(); if ( r instanceof PollerEvent ) { ((PollerEvent)r).reset(); eventCache.offer((PollerEvent)r); } } catch ( Throwable x ) { log.error("",x); } } return result; } public void register(final NioChannel socket) { socket.setPoller(this); KeyAttachment key = keyCache.poll(); final KeyAttachment ka = key!=null?key:new KeyAttachment(socket); ka.reset(this,socket,getSocketProperties().getSoTimeout()); ka.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests()); PollerEvent r = eventCache.poll(); ka.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into. if ( r==null) r = new PollerEvent(socket,ka,OP_REGISTER); else r.reset(socket,ka,OP_REGISTER); addEvent(r); } public void cancelledKey(SelectionKey key, SocketStatus status, boolean dispatch) { try { if ( key == null ) return;//nothing to do KeyAttachment ka = (KeyAttachment) key.attachment(); if (ka != null && ka.getComet() && status != null) { //the comet event takes care of clean up //processSocket(ka.getChannel(), status, dispatch); ka.setComet(false);//to avoid a loop if (status == SocketStatus.TIMEOUT ) { if (processSocket(ka.getChannel(), status, true)) { return; // don't close on comet timeout } } else { processSocket(ka.getChannel(), status, false); //don't dispatch if the lines below are cancelling the key } } key.attach(null); if (ka!=null) handler.release(ka); else handler.release((SocketChannel)key.channel()); if (key.isValid()) key.cancel(); if (key.channel().isOpen()) { try { key.channel().close(); } catch (Exception e) { if (log.isDebugEnabled()) { log.debug(sm.getString( "endpoint.debug.channelCloseFail"), e); } } } try { if (ka!=null) { ka.getSocket().close(true); } } catch (Exception e){ if (log.isDebugEnabled()) { log.debug(sm.getString( "endpoint.debug.socketCloseFail"), e); } } try { if (ka != null && ka.getSendfileData() != null && ka.getSendfileData().fchannel != null && ka.getSendfileData().fchannel.isOpen()) { ka.getSendfileData().fchannel.close(); } } catch (Exception ignore) { } if (ka!=null) { ka.reset(); countDownConnection(); } } catch (Throwable e) { ExceptionUtils.handleThrowable(e); if (log.isDebugEnabled()) log.error("",e); } } /** * The background thread that listens for incoming TCP/IP connections and * hands them off to an appropriate processor. */ @Override public void run() { // Loop until destroy() is called while (true) { try { // Loop if endpoint is paused while (paused && (!close) ) { try { Thread.sleep(100); } catch (InterruptedException e) { // Ignore } } boolean hasEvents = events(); // Time to terminate? if (close) { timeout(0, false); break; } try { if ( !close ) { if (wakeupCounter.getAndSet(-1) > 0) { //if we are here, means we have other stuff to do //do a non blocking select keyCount = selector.selectNow(); } else { keyCount = selector.select(selectorTimeout); } wakeupCounter.set(0); } if (close) { timeout(0, false); selector.close(); break; } } catch ( NullPointerException x ) { //sun bug 5076772 on windows JDK 1.5 if ( log.isDebugEnabled() ) log.debug("Possibly encountered sun bug 5076772 on windows JDK 1.5",x); if ( wakeupCounter == null || selector == null ) throw x; continue; } catch ( CancelledKeyException x ) { //sun bug 5076772 on windows JDK 1.5 if ( log.isDebugEnabled() ) log.debug("Possibly encountered sun bug 5076772 on windows JDK 1.5",x); if ( wakeupCounter == null || selector == null ) throw x; continue; } catch (Throwable x) { ExceptionUtils.handleThrowable(x); log.error("",x); continue; } //either we timed out or we woke up, process events first if ( keyCount == 0 ) hasEvents = (hasEvents | events()); Iterator iterator = keyCount > 0 ? selector.selectedKeys().iterator() : null; // Walk through the collection of ready keys and dispatch // any active event. while (iterator != null && iterator.hasNext()) { SelectionKey sk = iterator.next(); KeyAttachment attachment = (KeyAttachment)sk.attachment(); // Attachment may be null if another thread has called // cancelledKey() if (attachment == null) { iterator.remove(); } else { attachment.access(); iterator.remove(); processKey(sk, attachment); } }//while //process timeouts timeout(keyCount,hasEvents); if ( oomParachute > 0 && oomParachuteData == null ) checkParachute(); } catch (OutOfMemoryError oom) { try { oomParachuteData = null; releaseCaches(); log.error("", oom); }catch ( Throwable oomt ) { try { System.err.println(oomParachuteMsg); oomt.printStackTrace(); }catch (Throwable letsHopeWeDontGetHere){ ExceptionUtils.handleThrowable(letsHopeWeDontGetHere); } } } }//while synchronized (this) { this.notifyAll(); } stopLatch.countDown(); } protected boolean processKey(SelectionKey sk, KeyAttachment attachment) { boolean result = true; try { if ( close ) { cancelledKey(sk, SocketStatus.STOP, attachment.comet); } else if ( sk.isValid() && attachment != null ) { attachment.access();//make sure we don't time out valid sockets sk.attach(attachment);//cant remember why this is here NioChannel channel = attachment.getChannel(); if (sk.isReadable() || sk.isWritable() ) { if ( attachment.getSendfileData() != null ) { processSendfile(sk,attachment,true, false); } else if ( attachment.getComet() ) { //check if thread is available if ( isWorkerAvailable() ) { //set interest ops to 0 so we don't get multiple //Invocations for both read and write on separate threads reg(sk, attachment, 0); //read goes before write if (sk.isReadable()) { //read notification if (!processSocket(channel, SocketStatus.OPEN, true)) processSocket(channel, SocketStatus.DISCONNECT, true); } else { //future placement of a WRITE notif if (!processSocket(channel, SocketStatus.OPEN, true)) processSocket(channel, SocketStatus.DISCONNECT, true); } } else { result = false; } } else { //later on, improve latch behavior if ( isWorkerAvailable() ) { unreg(sk, attachment,sk.readyOps()); boolean close = (!processSocket(channel, null, true)); if (close) { cancelledKey(sk,SocketStatus.DISCONNECT,false); } } else { result = false; } } } } else { //invalid key cancelledKey(sk, SocketStatus.ERROR,false); } } catch ( CancelledKeyException ckx ) { cancelledKey(sk, SocketStatus.ERROR,false); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); log.error("",t); } return result; } public boolean processSendfile(SelectionKey sk, KeyAttachment attachment, boolean reg, boolean event) { NioChannel sc = null; try { //unreg(sk,attachment);//only do this if we do process send file on a separate thread SendfileData sd = attachment.getSendfileData(); if ( sd.fchannel == null ) { File f = new File(sd.fileName); if ( !f.exists() ) { cancelledKey(sk,SocketStatus.ERROR,false); return false; } sd.fchannel = new FileInputStream(f).getChannel(); } sc = attachment.getChannel(); sc.setSendFile(true); WritableByteChannel wc = ((sc instanceof SecureNioChannel)?sc:sc.getIOChannel()); if (sc.getOutboundRemaining()>0) { if (sc.flushOutbound()) { attachment.access(); } } else { long written = sd.fchannel.transferTo(sd.pos,sd.length,wc); if ( written > 0 ) { sd.pos += written; sd.length -= written; attachment.access(); } else { // Unusual not to be able to transfer any bytes // Check the length was set correctly if (sd.fchannel.size() <= sd.pos) { throw new IOException("Sendfile configured to " + "send more data than was available"); } } } if ( sd.length <= 0 && sc.getOutboundRemaining()<=0) { if (log.isDebugEnabled()) { log.debug("Send file complete for:"+sd.fileName); } attachment.setSendfileData(null); try { sd.fchannel.close(); } catch (Exception ignore) { } if ( sd.keepAlive ) { if (reg) { if (log.isDebugEnabled()) { log.debug("Connection is keep alive, registering back for OP_READ"); } if (event) { this.add(attachment.getChannel(),SelectionKey.OP_READ); } else { reg(sk,attachment,SelectionKey.OP_READ); } } } else { if (log.isDebugEnabled()) { log.debug("Send file connection is being closed"); } cancelledKey(sk,SocketStatus.STOP,false); return false; } } else if ( attachment.interestOps() == 0 && reg ) { if (log.isDebugEnabled()) { log.debug("OP_WRITE for sendilfe:"+sd.fileName); } if (event) { add(attachment.getChannel(),SelectionKey.OP_WRITE); } else { reg(sk,attachment,SelectionKey.OP_WRITE); } } }catch ( IOException x ) { if ( log.isDebugEnabled() ) log.debug("Unable to complete sendfile request:", x); cancelledKey(sk,SocketStatus.ERROR,false); return false; }catch ( Throwable t ) { log.error("",t); cancelledKey(sk, SocketStatus.ERROR, false); return false; }finally { if (sc!=null) sc.setSendFile(false); } return true; } protected void unreg(SelectionKey sk, KeyAttachment attachment, int readyOps) { //this is a must, so that we don't have multiple threads messing with the socket reg(sk,attachment,sk.interestOps()& (~readyOps)); } protected void reg(SelectionKey sk, KeyAttachment attachment, int intops) { sk.interestOps(intops); attachment.interestOps(intops); attachment.setCometOps(intops); } protected void timeout(int keyCount, boolean hasEvents) { long now = System.currentTimeMillis(); //don't process timeouts too frequently, but if the selector simply timed out //then we can check timeouts to avoid gaps if ( ((keyCount>0 || hasEvents) ||(now < nextExpiration)) && (!close) ) { return; } long prevExp = nextExpiration; //for logging purposes only nextExpiration = now + socketProperties.getTimeoutInterval(); //timeout Set keys = selector.keys(); int keycount = 0; for (Iterator iter = keys.iterator(); iter.hasNext();) { SelectionKey key = iter.next(); keycount++; try { KeyAttachment ka = (KeyAttachment) key.attachment(); if ( ka == null ) { cancelledKey(key, SocketStatus.ERROR,false); //we don't support any keys without attachments } else if ( ka.getError() ) { cancelledKey(key, SocketStatus.ERROR,true);//TODO this is not yet being used } else if (ka.getComet() && ka.getCometNotify() ) { ka.setCometNotify(false); reg(key,ka,0);//avoid multiple calls, this gets reregistered after invocation //if (!processSocket(ka.getChannel(), SocketStatus.OPEN_CALLBACK)) processSocket(ka.getChannel(), SocketStatus.DISCONNECT); if (!processSocket(ka.getChannel(), SocketStatus.OPEN, true)) processSocket(ka.getChannel(), SocketStatus.DISCONNECT, true); } else if ((ka.interestOps()&SelectionKey.OP_READ) == SelectionKey.OP_READ || (ka.interestOps()&SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) { //only timeout sockets that we are waiting for a read from long delta = now - ka.getLastAccess(); long timeout = ka.getTimeout(); boolean isTimedout = timeout > 0 && delta > timeout; if ( close ) { key.interestOps(0); ka.interestOps(0); //avoid duplicate stop calls processKey(key,ka); } else if (isTimedout) { key.interestOps(0); ka.interestOps(0); //avoid duplicate timeout calls cancelledKey(key, SocketStatus.TIMEOUT,true); } else if (timeout > -1) { long nextTime = now+(timeout-delta); nextExpiration = (nextTime < nextExpiration)?nextTime:nextExpiration; } } else if (ka.isAsync() || ka.getComet()) { // Async requests with a timeout of 0 or less never timeout if (!ka.isAsync() || ka.getTimeout() > 0) { long delta = now - ka.getLastAccess(); long timeout = (ka.getTimeout()==-1)?((long) socketProperties.getSoTimeout()):(ka.getTimeout()); boolean isTimedout = delta > timeout; if (isTimedout) { // Prevent subsequent timeouts if the timeout event takes a while to process ka.access(Long.MAX_VALUE); processSocket(ka.getChannel(), SocketStatus.TIMEOUT, true); } } }//end if }catch ( CancelledKeyException ckx ) { cancelledKey(key, SocketStatus.ERROR,false); } }//for if ( log.isTraceEnabled() ) log.trace("timeout completed: keys processed="+keycount+"; now="+now+"; nextExpiration="+prevExp+"; "+ "keyCount="+keyCount+"; hasEvents="+hasEvents +"; eval="+( (now < prevExp) && (keyCount>0 || hasEvents) && (!close) )); } } // ----------------------------------------------------- Key Attachment Class public static class KeyAttachment extends SocketWrapper { public KeyAttachment(NioChannel channel) { super(channel); } public void reset(Poller poller, NioChannel channel, long soTimeout) { this.socket = channel; this.poller = poller; lastAccess = System.currentTimeMillis(); comet = false; timeout = soTimeout; error = false; lastRegistered = 0; sendfileData = null; if (readLatch != null) { try { for (int i = 0; i < (int) readLatch.getCount(); i++) { readLatch.countDown(); } } catch (Exception ignore) { } } readLatch = null; if (writeLatch != null) { try { for (int i = 0; i < (int) writeLatch.getCount(); i++) { writeLatch.countDown(); } } catch (Exception ignore) { } } writeLatch = null; cometNotify = false; cometOps = SelectionKey.OP_READ; sendfileData = null; keepAliveLeft = 100; async = false; } public void reset() { reset(null,null,-1); } public Poller getPoller() { return poller;} public void setPoller(Poller poller){this.poller = poller;} public void setComet(boolean comet) { this.comet = comet; } public boolean getComet() { return comet; } public void setCometNotify(boolean notify) { this.cometNotify = notify; } public boolean getCometNotify() { return cometNotify; } public void setCometOps(int ops) { this.cometOps = ops; } public int getCometOps() { return cometOps; } public NioChannel getChannel() { return getSocket();} public void setChannel(NioChannel channel) { this.socket = channel;} protected Poller poller = null; protected int interestOps = 0; public int interestOps() { return interestOps;} public int interestOps(int ops) { this.interestOps = ops; return ops; } public CountDownLatch getReadLatch() { return readLatch; } public CountDownLatch getWriteLatch() { return writeLatch; } protected CountDownLatch resetLatch(CountDownLatch latch) { if ( latch==null || latch.getCount() == 0 ) return null; else throw new IllegalStateException("Latch must be at count 0"); } public void resetReadLatch() { readLatch = resetLatch(readLatch); } public void resetWriteLatch() { writeLatch = resetLatch(writeLatch); } protected CountDownLatch startLatch(CountDownLatch latch, int cnt) { if ( latch == null || latch.getCount() == 0 ) { return new CountDownLatch(cnt); } else throw new IllegalStateException("Latch must be at count 0 or null."); } public void startReadLatch(int cnt) { readLatch = startLatch(readLatch,cnt);} public void startWriteLatch(int cnt) { writeLatch = startLatch(writeLatch,cnt);} protected void awaitLatch(CountDownLatch latch, long timeout, TimeUnit unit) throws InterruptedException { if ( latch == null ) throw new IllegalStateException("Latch cannot be null"); latch.await(timeout,unit); } public void awaitReadLatch(long timeout, TimeUnit unit) throws InterruptedException { awaitLatch(readLatch,timeout,unit);} public void awaitWriteLatch(long timeout, TimeUnit unit) throws InterruptedException { awaitLatch(writeLatch,timeout,unit);} public long getLastRegistered() { return lastRegistered; } public void setLastRegistered(long reg) { lastRegistered = reg; } public void setSendfileData(SendfileData sf) { this.sendfileData = sf;} public SendfileData getSendfileData() { return this.sendfileData;} protected boolean comet = false; protected int cometOps = SelectionKey.OP_READ; protected boolean cometNotify = false; protected CountDownLatch readLatch = null; protected CountDownLatch writeLatch = null; protected SendfileData sendfileData = null; } // ------------------------------------------------ Application Buffer Handler public static class NioBufferHandler implements ApplicationBufferHandler { protected ByteBuffer readbuf = null; protected ByteBuffer writebuf = null; public NioBufferHandler(int readsize, int writesize, boolean direct) { if ( direct ) { readbuf = ByteBuffer.allocateDirect(readsize); writebuf = ByteBuffer.allocateDirect(writesize); }else { readbuf = ByteBuffer.allocate(readsize); writebuf = ByteBuffer.allocate(writesize); } } @Override public ByteBuffer expand(ByteBuffer buffer, int remaining) {return buffer;} @Override public ByteBuffer getReadBuffer() {return readbuf;} @Override public ByteBuffer getWriteBuffer() {return writebuf;} } // ------------------------------------------------ Handler Inner Interface /** * Bare bones interface used for socket processing. Per thread data is to be * stored in the ThreadWithAttributes extra folders, or alternately in * thread local fields. */ public interface Handler extends AbstractEndpoint.Handler { public SocketState process(SocketWrapper socket, SocketStatus status); public void release(SocketWrapper socket); public void release(SocketChannel socket); public SSLImplementation getSslImplementation(); } // ---------------------------------------------- SocketProcessor Inner Class /** * This class is the equivalent of the Worker, but will simply use in an * external Executor thread pool. */ protected class SocketProcessor implements Runnable { protected NioChannel socket = null; protected SocketStatus status = null; public SocketProcessor(NioChannel socket, SocketStatus status) { reset(socket,status); } public void reset(NioChannel socket, SocketStatus status) { this.socket = socket; this.status = status; } @Override public void run() { boolean launch = false; synchronized (socket) { SelectionKey key = null; try { key = socket.getIOChannel().keyFor(socket.getPoller().getSelector()); int handshake = -1; try { if (key!=null) handshake = socket.handshake(key.isReadable(), key.isWritable()); }catch ( IOException x ) { handshake = -1; if ( log.isDebugEnabled() ) log.debug("Error during SSL handshake",x); }catch ( CancelledKeyException ckx ) { handshake = -1; } if ( handshake == 0 ) { SocketState state = SocketState.OPEN; // Process the request from this socket if (status == null) { state = handler.process( (KeyAttachment) key.attachment(), SocketStatus.OPEN); } else { state = handler.process( (KeyAttachment) key.attachment(), status); } if (state == SocketState.CLOSED) { // Close socket and pool try { KeyAttachment ka = null; if (key!=null) { ka = (KeyAttachment) key.attachment(); if (ka!=null) ka.setComet(false); socket.getPoller().cancelledKey(key, SocketStatus.ERROR, false); } if (socket!=null) nioChannels.offer(socket); socket = null; if ( ka!=null ) keyCache.offer(ka); ka = null; }catch ( Exception x ) { log.error("",x); } } } else if (handshake == -1 ) { KeyAttachment ka = null; if (key!=null) { ka = (KeyAttachment) key.attachment(); socket.getPoller().cancelledKey(key, SocketStatus.DISCONNECT, false); } if (socket!=null) nioChannels.offer(socket); socket = null; if ( ka!=null ) keyCache.offer(ka); ka = null; } else { final SelectionKey fk = key; final int intops = handshake; final KeyAttachment ka = (KeyAttachment)fk.attachment(); ka.getPoller().add(socket,intops); } }catch(CancelledKeyException cx) { socket.getPoller().cancelledKey(key,null,false); } catch (OutOfMemoryError oom) { try { oomParachuteData = null; socket.getPoller().cancelledKey(key,SocketStatus.ERROR,false); releaseCaches(); log.error("", oom); }catch ( Throwable oomt ) { try { System.err.println(oomParachuteMsg); oomt.printStackTrace(); }catch (Throwable letsHopeWeDontGetHere){ ExceptionUtils.handleThrowable(letsHopeWeDontGetHere); } } }catch ( Throwable t ) { log.error("",t); socket.getPoller().cancelledKey(key,SocketStatus.ERROR,false); } finally { if (launch) { try { getExecutor().execute(new SocketProcessor(socket, SocketStatus.OPEN)); } catch (NullPointerException npe) { if (running) { log.error(sm.getString("endpoint.launch.fail"), npe); } } } socket = null; status = null; //return to cache processorCache.offer(this); } } } } // ----------------------------------------------- SendfileData Inner Class /** * SendfileData class. */ public static class SendfileData { // File public String fileName; public FileChannel fchannel; public long pos; public long length; // KeepAlive flag public boolean keepAlive; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy