View Javadoc

1   // ========================================================================
2   // Copyright (c) 2003-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // ========================================================================
13  
14  package org.eclipse.jetty.server.nio;
15  
16  import java.io.IOException;
17  import java.net.InetSocketAddress;
18  import java.net.Socket;
19  import java.nio.channels.SelectionKey;
20  import java.nio.channels.ServerSocketChannel;
21  import java.nio.channels.SocketChannel;
22  
23  import org.eclipse.jetty.io.Connection;
24  import org.eclipse.jetty.io.EndPoint;
25  import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
26  import org.eclipse.jetty.io.nio.SelectorManager;
27  import org.eclipse.jetty.io.nio.SelectorManager.SelectSet;
28  import org.eclipse.jetty.server.HttpConnection;
29  import org.eclipse.jetty.server.Request;
30  import org.eclipse.jetty.util.log.Log;
31  import org.eclipse.jetty.util.thread.Timeout.Task;
32  
33  /* ------------------------------------------------------------------------------- */
34  /**
35   * Selecting NIO connector.
36   * <p>
37   * This connector uses efficient NIO buffers with a non blocking threading model. Direct NIO buffers
38   * are used and threads are only allocated to connections with requests. Synchronization is used to
39   * simulate blocking for the servlet API, and any unflushed content at the end of request handling
40   * is written asynchronously.
41   * </p>
42   * <p>
43   * This connector is best used when there are a many connections that have idle periods.
44   * </p>
45   * <p>
46   * When used with {@link org.eclipse.jetty.continuation.Continuation}, threadless waits are supported. When
47   * a filter or servlet calls getEvent on a Continuation, a {@link org.eclipse.jetty.server.RetryRequest}
48   * runtime exception is thrown to allow the thread to exit the current request handling. Jetty will
49   * catch this exception and will not send a response to the client. Instead the thread is released
50   * and the Continuation is placed on the timer queue. If the Continuation timeout expires, or it's
51   * resume method is called, then the request is again allocated a thread and the request is retried.
52   * The limitation of this approach is that request content is not available on the retried request,
53   * thus if possible it should be read after the continuation or saved as a request attribute or as the
54   * associated object of the Continuation instance.
55   * </p>
56   * 
57   * @org.apache.xbean.XBean element="nioConnector" description="Creates an NIO based socket connector"
58   * 
59   * 
60   *
61   */
62  public class SelectChannelConnector extends AbstractNIOConnector 
63  {
64      protected ServerSocketChannel _acceptChannel;
65      private long _lowResourcesConnections;
66      private long _lowResourcesMaxIdleTime;
67  
68      private final SelectorManager _manager = new SelectorManager()
69      {
70          protected SocketChannel acceptChannel(SelectionKey key) throws IOException
71          {
72              // TODO handle max connections
73              SocketChannel channel = ((ServerSocketChannel)key.channel()).accept();
74              if (channel==null)
75                  return null;
76              channel.configureBlocking(false);
77              Socket socket = channel.socket();
78              configure(socket);
79              return channel;
80          }
81  
82          public boolean dispatch(Runnable task)
83          {
84              return getThreadPool().dispatch(task);
85          }
86  
87          protected void endPointClosed(final SelectChannelEndPoint endpoint)
88          {
89              connectionClosed((HttpConnection)endpoint.getConnection());
90          }
91  
92          protected void endPointOpened(SelectChannelEndPoint endpoint)
93          {
94              // TODO handle max connections and low resources
95              connectionOpened((HttpConnection)endpoint.getConnection());
96          }
97  
98          protected Connection newConnection(SocketChannel channel,SelectChannelEndPoint endpoint)
99          {
100             return SelectChannelConnector.this.newConnection(channel,endpoint);
101         }
102 
103         protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey sKey) throws IOException
104         {
105             return SelectChannelConnector.this.newEndPoint(channel,selectSet,sKey);
106         }
107     };
108     
109     /* ------------------------------------------------------------------------------- */
110     /**
111      * Constructor.
112      * 
113      */
114     public SelectChannelConnector()
115     {
116     }
117     
118     /* ------------------------------------------------------------ */
119     public void accept(int acceptorID) throws IOException
120     {
121         _manager.doSelect(acceptorID);
122     }
123     
124     /* ------------------------------------------------------------ */
125     public void close() throws IOException
126     {
127         synchronized(this)
128         {
129             if(_manager.isRunning())
130             {
131                 try
132                 {
133                     _manager.stop();
134                 }
135                 catch (Exception e)
136                 {
137                     Log.warn(e);
138                 }
139             }
140             if (_acceptChannel != null)
141                 _acceptChannel.close();
142             _acceptChannel = null;
143         }
144     }
145     
146     /* ------------------------------------------------------------------------------- */
147     @Override
148     public void customize(EndPoint endpoint, Request request) throws IOException
149     {
150         SelectChannelEndPoint cep = ((SelectChannelEndPoint)endpoint);
151         cep.cancelIdle();
152         request.setTimeStamp(cep.getSelectSet().getNow());
153         super.customize(endpoint, request);
154     }
155     
156     /* ------------------------------------------------------------------------------- */
157     @Override
158     public void persist(EndPoint endpoint) throws IOException
159     {
160         ((SelectChannelEndPoint)endpoint).scheduleIdle();
161         super.persist(endpoint);
162     }
163 
164     /* ------------------------------------------------------------ */
165     public Object getConnection()
166     {
167         return _acceptChannel;
168     }
169 
170     /* ------------------------------------------------------------------------------- */
171     public int getLocalPort()
172     {
173         synchronized(this)
174         {
175             if (_acceptChannel==null || !_acceptChannel.isOpen())
176                 return -1;
177             return _acceptChannel.socket().getLocalPort();
178         }
179     }
180 
181     /* ------------------------------------------------------------ */
182     public void open() throws IOException
183     {
184         synchronized(this)
185         {
186             if (_acceptChannel == null)
187             {
188                 // Create a new server socket
189                 _acceptChannel = ServerSocketChannel.open();
190 
191                 // Bind the server socket to the local host and port
192                 _acceptChannel.socket().setReuseAddress(getReuseAddress());
193                 InetSocketAddress addr = getHost()==null?new InetSocketAddress(getPort()):new InetSocketAddress(getHost(),getPort());
194                 _acceptChannel.socket().bind(addr,getAcceptQueueSize());
195 
196                 // Set to non blocking mode
197                 _acceptChannel.configureBlocking(false);
198                 
199             }
200         }
201     }
202 
203     /* ------------------------------------------------------------ */
204     public void setMaxIdleTime(int maxIdleTime)
205     {
206         _manager.setMaxIdleTime(maxIdleTime);
207         super.setMaxIdleTime(maxIdleTime);
208     }
209 
210     /* ------------------------------------------------------------ */
211     /**
212      * @return the lowResourcesConnections
213      */
214     public long getLowResourcesConnections()
215     {
216         return _lowResourcesConnections;
217     }
218 
219     /* ------------------------------------------------------------ */
220     /**
221      * Set the number of connections, which if exceeded places this manager in low resources state.
222      * This is not an exact measure as the connection count is averaged over the select sets.
223      * @param lowResourcesConnections the number of connections
224      * @see {@link #setLowResourcesMaxIdleTime(long)}
225      */
226     public void setLowResourcesConnections(long lowResourcesConnections)
227     {
228         _lowResourcesConnections=lowResourcesConnections;
229     }
230 
231     /* ------------------------------------------------------------ */
232     /**
233      * @return the lowResourcesMaxIdleTime
234      */
235     public long getLowResourcesMaxIdleTime()
236     {
237         return _lowResourcesMaxIdleTime;
238     }
239 
240     /* ------------------------------------------------------------ */
241     /**
242      * Set the period in ms that a connection is allowed to be idle when this there are more
243      * than {@link #getLowResourcesConnections()} connections.  This allows the server to rapidly close idle connections
244      * in order to gracefully handle high load situations.
245      * @param lowResourcesMaxIdleTime the period in ms that a connection is allowed to be idle when resources are low.
246      * @see {@link #setMaxIdleTime(long)}
247      * @deprecated use {@link #setLowResourceMaxIdleTime(int)}
248      */
249     public void setLowResourcesMaxIdleTime(long lowResourcesMaxIdleTime)
250     {
251         _lowResourcesMaxIdleTime=lowResourcesMaxIdleTime;
252         super.setLowResourceMaxIdleTime((int)lowResourcesMaxIdleTime); // TODO fix the name duplications
253     }
254 
255     /* ------------------------------------------------------------ */
256     /**
257      * Set the period in ms that a connection is allowed to be idle when this there are more
258      * than {@link #getLowResourcesConnections()} connections.  This allows the server to rapidly close idle connections
259      * in order to gracefully handle high load situations.
260      * @param lowResourcesMaxIdleTime the period in ms that a connection is allowed to be idle when resources are low.
261      * @see {@link #setMaxIdleTime(long)}
262      */
263     public void setLowResourceMaxIdleTime(int lowResourcesMaxIdleTime)
264     {
265         _lowResourcesMaxIdleTime=lowResourcesMaxIdleTime;
266         super.setLowResourceMaxIdleTime(lowResourcesMaxIdleTime); 
267     }
268 
269     
270     /* ------------------------------------------------------------ */
271     /*
272      * @see org.eclipse.jetty.server.server.AbstractConnector#doStart()
273      */
274     protected void doStart() throws Exception
275     {
276         _manager.setSelectSets(getAcceptors());
277         _manager.setMaxIdleTime(getMaxIdleTime());
278         _manager.setLowResourcesConnections(getLowResourcesConnections());
279         _manager.setLowResourcesMaxIdleTime(getLowResourcesMaxIdleTime());
280         _manager.start();
281         open();
282         _manager.register(_acceptChannel);
283         super.doStart();
284     }
285 
286     /* ------------------------------------------------------------ */
287     /*
288      * @see org.eclipse.jetty.server.server.AbstractConnector#doStop()
289      */
290     protected void doStop() throws Exception
291     {        
292         super.doStop();
293     }
294 
295     /* ------------------------------------------------------------ */
296     protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key) throws IOException
297     {
298         return new SelectChannelEndPoint(channel,selectSet,key);
299     }
300 
301     /* ------------------------------------------------------------------------------- */
302     protected Connection newConnection(SocketChannel channel,final SelectChannelEndPoint endpoint)
303     {
304         return new HttpConnection(SelectChannelConnector.this,endpoint,getServer())
305         {
306             /* ------------------------------------------------------------ */
307             public void cancelTimeout(Task task)
308             {
309                 endpoint.getSelectSet().cancelTimeout(task);
310             }
311 
312             /* ------------------------------------------------------------ */
313             public void scheduleTimeout(Task task, long timeoutMs)
314             {
315                 endpoint.getSelectSet().scheduleTimeout(task,timeoutMs);
316             }
317         };
318     }
319 
320     /* ------------------------------------------------------------------------------- */
321     public void dump()
322     {
323         Log.info("channel "+_acceptChannel+(_acceptChannel.isOpen()?" is open":" is closed"));
324         _manager.dump();
325     }
326 }