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 }