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

org.mortbay.jetty.nio.SelectChannelConnector Maven / Gradle / Ivy

// ========================================================================
// Copyright 2003-2005 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// Licensed 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.mortbay.jetty.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

import org.mortbay.io.Connection;
import org.mortbay.io.nio.SelectChannelEndPoint;
import org.mortbay.io.nio.SelectorManager;
import org.mortbay.io.nio.SelectorManager.SelectSet;
import org.mortbay.jetty.HttpConnection;
import org.mortbay.jetty.Request;
import org.mortbay.jetty.RetryRequest;
import org.mortbay.log.Log;
import org.mortbay.thread.Timeout;
import org.mortbay.util.ajax.Continuation;

/* ------------------------------------------------------------------------------- */
/**
 * Selecting NIO connector.
 * 

* This connector uses efficient NIO buffers with a non blocking threading model. Direct NIO buffers * are used and threads are only allocated to connections with requests. Synchronization is used to * simulate blocking for the servlet API, and any unflushed content at the end of request handling * is written asynchronously. *

*

* This connector is best used when there are a many connections that have idle periods. *

*

* When used with {@link org.mortbay.util.ajax.Continuation}, threadless waits are supported. When * a filter or servlet calls getEvent on a Continuation, a {@link org.mortbay.jetty.RetryRequest} * runtime exception is thrown to allow the thread to exit the current request handling. Jetty will * catch this exception and will not send a response to the client. Instead the thread is released * and the Continuation is placed on the timer queue. If the Continuation timeout expires, or it's * resume method is called, then the request is again allocated a thread and the request is retried. * The limitation of this approach is that request content is not available on the retried request, * thus if possible it should be read after the continuation or saved as a request attribute or as the * associated object of the Continuation instance. *

* * @org.apache.xbean.XBean element="nioConnector" description="Creates an NIO based socket connector" * * @author gregw * */ public class SelectChannelConnector extends AbstractNIOConnector { protected transient ServerSocketChannel _acceptChannel; private long _lowResourcesConnections; private long _lowResourcesMaxIdleTime; private SelectorManager _manager = new SelectorManager() { protected SocketChannel acceptChannel(SelectionKey key) throws IOException { // TODO handle max connections SocketChannel channel = ((ServerSocketChannel)key.channel()).accept(); if (channel==null) return null; channel.configureBlocking(false); Socket socket = channel.socket(); configure(socket); return channel; } public boolean dispatch(Runnable task) throws IOException { return getThreadPool().dispatch(task); } protected void endPointClosed(SelectChannelEndPoint endpoint) { // TODO handle max connections and low resources connectionClosed((HttpConnection)endpoint.getConnection()); } protected void endPointOpened(SelectChannelEndPoint endpoint) { // TODO handle max connections and low resources connectionOpened((HttpConnection)endpoint.getConnection()); } protected Connection newConnection(SocketChannel channel,SelectChannelEndPoint endpoint) { return SelectChannelConnector.this.newConnection(channel,endpoint); } protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey sKey) throws IOException { return SelectChannelConnector.this.newEndPoint(channel,selectSet,sKey); } }; /* ------------------------------------------------------------------------------- */ /** * Constructor. * */ public SelectChannelConnector() { } /* ------------------------------------------------------------ */ public void accept(int acceptorID) throws IOException { _manager.doSelect(acceptorID); } /* ------------------------------------------------------------ */ public void close() throws IOException { synchronized(this) { if(_manager.isRunning()) { try { _manager.stop(); } catch (Exception e) { Log.warn(e); } } if (_acceptChannel != null) _acceptChannel.close(); _acceptChannel = null; } } /* ------------------------------------------------------------------------------- */ public void customize(org.mortbay.io.EndPoint endpoint, Request request) throws IOException { ConnectorEndPoint cep = ((ConnectorEndPoint)endpoint); cep.cancelIdle(); request.setTimeStamp(cep.getSelectSet().getNow()); super.customize(endpoint, request); } /* ------------------------------------------------------------------------------- */ public void persist(org.mortbay.io.EndPoint endpoint) throws IOException { ((ConnectorEndPoint)endpoint).scheduleIdle(); super.persist(endpoint); } /* ------------------------------------------------------------ */ public Object getConnection() { return _acceptChannel; } /* ------------------------------------------------------------ */ /** Get delay select key update * If true, the select set is not updated when a endpoint is dispatched for * reading. The assumption is that the task will be short and thus will probably * be complete before the select is tried again. * @return Returns the assumeShortDispatch. */ public boolean getDelaySelectKeyUpdate() { return _manager.isDelaySelectKeyUpdate(); } /* ------------------------------------------------------------------------------- */ public int getLocalPort() { synchronized(this) { if (_acceptChannel==null || !_acceptChannel.isOpen()) return -1; return _acceptChannel.socket().getLocalPort(); } } /* ------------------------------------------------------------ */ /* * @see org.mortbay.jetty.Connector#newContinuation() */ public Continuation newContinuation() { return new RetryContinuation(); } /* ------------------------------------------------------------ */ public void open() throws IOException { synchronized(this) { if (_acceptChannel == null) { // Create a new server socket _acceptChannel = ServerSocketChannel.open(); // Bind the server socket to the local host and port _acceptChannel.socket().setReuseAddress(getReuseAddress()); InetSocketAddress addr = getHost()==null?new InetSocketAddress(getPort()):new InetSocketAddress(getHost(),getPort()); _acceptChannel.socket().bind(addr,getAcceptQueueSize()); // Set to non blocking mode _acceptChannel.configureBlocking(false); } } } /* ------------------------------------------------------------ */ /** * @param delay If true, updating a {@link SelectionKey} is delayed until a redundant event is * schedules. This is an optimization that assumes event handling can be completed before the next select * completes. */ public void setDelaySelectKeyUpdate(boolean delay) { _manager.setDelaySelectKeyUpdate(delay); } /* ------------------------------------------------------------ */ public void setMaxIdleTime(int maxIdleTime) { _manager.setMaxIdleTime(maxIdleTime); super.setMaxIdleTime(maxIdleTime); } /* ------------------------------------------------------------ */ /** * @return the lowResourcesConnections */ public long getLowResourcesConnections() { return _lowResourcesConnections; } /* ------------------------------------------------------------ */ /** * Set the number of connections, which if exceeded places this manager in low resources state. * This is not an exact measure as the connection count is averaged over the select sets. * @param lowResourcesConnections the number of connections * @see {@link #setLowResourcesMaxIdleTime(long)} */ public void setLowResourcesConnections(long lowResourcesConnections) { _lowResourcesConnections=lowResourcesConnections; } /* ------------------------------------------------------------ */ /** * @return the lowResourcesMaxIdleTime */ public long getLowResourcesMaxIdleTime() { return _lowResourcesMaxIdleTime; } /* ------------------------------------------------------------ */ /** * Set the period in ms that a connection is allowed to be idle when this there are more * than {@link #getLowResourcesConnections()} connections. This allows the server to rapidly close idle connections * in order to gracefully handle high load situations. * @param lowResourcesMaxIdleTime the period in ms that a connection is allowed to be idle when resources are low. * @see {@link #setMaxIdleTime(long)} * @deprecated use {@link #setLowResourceMaxIdleTime(int)} */ public void setLowResourcesMaxIdleTime(long lowResourcesMaxIdleTime) { _lowResourcesMaxIdleTime=lowResourcesMaxIdleTime; super.setLowResourceMaxIdleTime((int)lowResourcesMaxIdleTime); // TODO fix the name duplications } /* ------------------------------------------------------------ */ /** * Set the period in ms that a connection is allowed to be idle when this there are more * than {@link #getLowResourcesConnections()} connections. This allows the server to rapidly close idle connections * in order to gracefully handle high load situations. * @param lowResourcesMaxIdleTime the period in ms that a connection is allowed to be idle when resources are low. * @see {@link #setMaxIdleTime(long)} */ public void setLowResourceMaxIdleTime(int lowResourcesMaxIdleTime) { _lowResourcesMaxIdleTime=lowResourcesMaxIdleTime; super.setLowResourceMaxIdleTime(lowResourcesMaxIdleTime); } /* ------------------------------------------------------------ */ /* * @see org.mortbay.jetty.AbstractConnector#doStart() */ protected void doStart() throws Exception { _manager.setSelectSets(getAcceptors()); _manager.setMaxIdleTime(getMaxIdleTime()); _manager.setLowResourcesConnections(getLowResourcesConnections()); _manager.setLowResourcesMaxIdleTime(getLowResourcesMaxIdleTime()); _manager.start(); open(); _manager.register(_acceptChannel); super.doStart(); } /* ------------------------------------------------------------ */ /* * @see org.mortbay.jetty.AbstractConnector#doStop() */ protected void doStop() throws Exception { super.doStop(); } /* ------------------------------------------------------------ */ protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key) throws IOException { return new ConnectorEndPoint(channel,selectSet,key); } /* ------------------------------------------------------------------------------- */ protected Connection newConnection(SocketChannel channel,SelectChannelEndPoint endpoint) { return new HttpConnection(SelectChannelConnector.this,endpoint,getServer()); } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ public static class ConnectorEndPoint extends SelectChannelEndPoint { public ConnectorEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key) { super(channel,selectSet,key); scheduleIdle(); } public void close() throws IOException { Connection con=getConnection(); if (con instanceof HttpConnection) { RetryContinuation continuation = (RetryContinuation) ((HttpConnection)getConnection()).getRequest().getContinuation(); if (continuation != null && continuation.isPending()) continuation.reset(); } super.close(); } /* ------------------------------------------------------------ */ public void undispatch() { Connection con=getConnection(); if (con instanceof HttpConnection) { RetryContinuation continuation = (RetryContinuation) ((HttpConnection)getConnection()).getRequest().getContinuation(); if (continuation != null) { // We have a continuation Log.debug("continuation {}", continuation); if (continuation.undispatch()) super.undispatch(); } else { super.undispatch(); } } else super.undispatch(); } } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ public static class RetryContinuation extends Timeout.Task implements Continuation, Runnable { SelectChannelEndPoint _endPoint=(SelectChannelEndPoint)HttpConnection.getCurrentConnection().getEndPoint(); boolean _new = true; Object _object; boolean _pending = false; // waiting for resume or timeout boolean _resumed = false; // resume called. boolean _parked =false; // end point dispatched, but undispatch called. RetryRequest _retry; long _timeout; public Object getObject() { return _object; } public long getTimeout() { return _timeout; } public boolean isNew() { return _new; } public boolean isPending() { return _pending; } public boolean isResumed() { return _resumed; } public void reset() { synchronized (this) { _resumed = false; _pending = false; _parked = false; } synchronized (_endPoint.getSelectSet()) { this.cancel(); } } public boolean suspend(long timeout) { boolean resumed=false; synchronized (this) { resumed=_resumed; _resumed=false; _new = false; if (!_pending && !resumed && timeout >= 0) { _pending=true; _parked = false; _timeout = timeout; if (_retry==null) _retry = new RetryRequest(); throw _retry; } // here only if suspend called on pending continuation. // acts like a reset _resumed = false; _pending = false; _parked =false; } synchronized (_endPoint.getSelectSet()) { this.cancel(); } return resumed; } public void resume() { boolean redispatch=false; synchronized (this) { if (_pending && !isExpired()) { _resumed = true; redispatch=_parked; _parked=false; } } if (redispatch) { SelectSet selectSet = _endPoint.getSelectSet(); synchronized (selectSet) { this.cancel(); } _endPoint.scheduleIdle(); // TODO maybe not needed? selectSet.addChange(this); selectSet.wakeup(); } } public void expire() { boolean redispatch=false; synchronized (this) { redispatch=_parked && _pending && !_resumed; _parked=false; } if (redispatch) { _endPoint.scheduleIdle(); // TODO maybe not needed? _endPoint.getSelectSet().addChange(this); _endPoint.getSelectSet().wakeup(); } } public void run() { _endPoint.run(); } /* undispatch continuation. * Called when an endppoint is undispatched. * Either sets timeout or dispatches if already resumed or expired */ public boolean undispatch() { boolean redispatch=false; synchronized (this) { if (!_pending) return true; redispatch=isExpired() || _resumed; _parked=!redispatch; } if (redispatch) { _endPoint.scheduleIdle(); _endPoint.getSelectSet().addChange(this); } else if (_timeout>0) _endPoint.getSelectSet().scheduleTimeout(this,_timeout); _endPoint.getSelectSet().wakeup(); return false; } public void setObject(Object object) { _object = object; } public String toString() { synchronized (this) { return "RetryContinuation@"+hashCode()+ (_new?",new":"")+ (_pending?",pending":"")+ (_resumed?",resumed":"")+ (isExpired()?",expired":"")+ (_parked?",parked":""); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy