1 package org.mortbay.io.nio;
2
3 import java.io.IOException;
4 import java.nio.channels.ClosedChannelException;
5 import java.nio.channels.SelectableChannel;
6 import java.nio.channels.SelectionKey;
7 import java.nio.channels.SocketChannel;
8
9 import javax.net.ssl.SSLException;
10
11 import org.mortbay.io.AsyncEndPoint;
12 import org.mortbay.io.Buffer;
13 import org.mortbay.io.Connection;
14 import org.mortbay.io.nio.SelectorManager.SelectSet;
15 import org.mortbay.jetty.EofException;
16 import org.mortbay.jetty.HttpException;
17 import org.mortbay.log.Log;
18 import org.mortbay.thread.Timeout;
19
20
21
22
23
24
25
26
27 public class SelectChannelEndPoint extends ChannelEndPoint implements Runnable, AsyncEndPoint
28 {
29 protected SelectorManager _manager;
30 protected SelectorManager.SelectSet _selectSet;
31 protected boolean _dispatched = false;
32 protected boolean _redispatched = false;
33 protected boolean _writable = true;
34 protected SelectionKey _key;
35 protected int _interestOps;
36 protected boolean _readBlocked;
37 protected boolean _writeBlocked;
38 protected Connection _connection;
39 private boolean _open;
40 private Timeout.Task _idleTask = new IdleTask();
41
42
43 public Connection getConnection()
44 {
45 return _connection;
46 }
47
48
49 public SelectChannelEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key)
50 {
51 super(channel);
52
53 _manager = selectSet.getManager();
54 _selectSet = selectSet;
55 _connection = _manager.newConnection(channel,this);
56 _dispatched = false;
57 _redispatched = false;
58 _open=true;
59 _manager.endPointOpened(this);
60
61 _key = key;
62 scheduleIdle();
63 }
64
65
66
67
68
69 public void schedule() throws IOException
70 {
71
72 synchronized (this)
73 {
74
75 if (_key == null || !_key.isValid())
76 {
77 _readBlocked=false;
78 _writeBlocked=false;
79 this.notifyAll();
80 return;
81 }
82
83
84 if (_readBlocked || _writeBlocked)
85 {
86
87 if (_readBlocked && _key.isReadable())
88 _readBlocked=false;
89 if (_writeBlocked && _key.isWritable())
90 _writeBlocked=false;
91
92
93 this.notifyAll();
94
95
96 _key.interestOps(0);
97 return;
98 }
99
100
101 if (!isReadyForDispatch())
102 {
103
104 _key.interestOps(0);
105 return;
106 }
107
108
109 if ((_key.readyOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE && (_key.interestOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE)
110 {
111
112 _interestOps = _key.interestOps() & ~SelectionKey.OP_WRITE;
113 _key.interestOps(_interestOps);
114 _writable = true;
115 }
116
117 if (!dispatch())
118 updateKey();
119 }
120 }
121
122
123 public boolean dispatch()
124 {
125 synchronized(this)
126 {
127 if (_dispatched)
128 {
129 _redispatched=true;
130 return true;
131 }
132
133 _dispatched = _manager.dispatch((Runnable)this);
134 if(!_dispatched)
135 Log.warn("Dispatched Failed!");
136 return _dispatched;
137 }
138 }
139
140
141
142
143
144
145 protected boolean undispatch()
146 {
147 synchronized (this)
148 {
149 if (_redispatched)
150 {
151 _redispatched=false;
152 return false;
153 }
154 _dispatched = false;
155 updateKey();
156 }
157 return true;
158 }
159
160
161 public void scheduleIdle()
162 {
163 _selectSet.scheduleIdle(_idleTask);
164 }
165
166
167 public void cancelIdle()
168 {
169 _selectSet.cancelIdle(_idleTask);
170 }
171
172
173 protected void idleExpired()
174 {
175 try
176 {
177 close();
178 }
179 catch (IOException e)
180 {
181 Log.ignore(e);
182 }
183 }
184
185
186
187
188 public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException
189 {
190 int l = super.flush(header, buffer, trailer);
191 _writable = l > 0;
192 return l;
193 }
194
195
196
197
198 public int flush(Buffer buffer) throws IOException
199 {
200 int l = super.flush(buffer);
201 _writable = l > 0;
202 return l;
203 }
204
205
206 public boolean isReadyForDispatch()
207 {
208 return !_dispatched;
209 }
210
211
212
213
214
215 public boolean blockReadable(long timeoutMs) throws IOException
216 {
217 synchronized (this)
218 {
219 long start=_selectSet.getNow();
220 try
221 {
222 _readBlocked=true;
223 while (isOpen() && _readBlocked)
224 {
225 try
226 {
227 updateKey();
228 this.wait(timeoutMs);
229
230 if (_readBlocked && timeoutMs<(_selectSet.getNow()-start))
231 return false;
232 }
233 catch (InterruptedException e)
234 {
235 Log.warn(e);
236 }
237 }
238 }
239 finally
240 {
241 _readBlocked=false;
242 }
243 }
244 return true;
245 }
246
247
248
249
250
251 public boolean blockWritable(long timeoutMs) throws IOException
252 {
253 synchronized (this)
254 {
255 long start=_selectSet.getNow();
256 try
257 {
258 _writeBlocked=true;
259 while (isOpen() && _writeBlocked)
260 {
261 try
262 {
263 updateKey();
264 this.wait(timeoutMs);
265
266 if (_writeBlocked && timeoutMs<(_selectSet.getNow()-start))
267 return false;
268 }
269 catch (InterruptedException e)
270 {
271 Log.warn(e);
272 }
273 }
274 }
275 finally
276 {
277 _writeBlocked=false;
278 }
279 }
280 return true;
281 }
282
283
284 public void setWritable(boolean writable)
285 {
286 _writable=writable;
287 }
288
289
290 public void scheduleWrite()
291 {
292 _writable=false;
293 updateKey();
294 }
295
296
297
298
299
300
301
302 private void updateKey()
303 {
304 synchronized (this)
305 {
306
307 int ops=-1;
308 if (getChannel().isOpen())
309 {
310 _interestOps =
311 ((!_dispatched || _readBlocked) ? SelectionKey.OP_READ : 0)
312 | ((!_writable || _writeBlocked) ? SelectionKey.OP_WRITE : 0);
313 try
314 {
315 ops = ((_key!=null && _key.isValid())?_key.interestOps():-1);
316 }
317 catch(Exception e)
318 {
319 _key=null;
320 Log.ignore(e);
321 }
322 }
323 if(_interestOps == ops && getChannel().isOpen())
324 return;
325
326 }
327 _selectSet.addChange(this);
328 _selectSet.wakeup();
329 }
330
331
332
333
334
335 void doUpdateKey()
336 {
337 synchronized (this)
338 {
339 if (getChannel().isOpen())
340 {
341 if (_interestOps>0)
342 {
343 if (_key==null || !_key.isValid())
344 {
345 SelectableChannel sc = (SelectableChannel)getChannel();
346 if (sc.isRegistered())
347 {
348 updateKey();
349 }
350 else
351 {
352 try
353 {
354 _key=((SelectableChannel)getChannel()).register(_selectSet.getSelector(),_interestOps,this);
355 }
356 catch (Exception e)
357 {
358 Log.ignore(e);
359 if (_key!=null && _key.isValid())
360 {
361 _key.cancel();
362 }
363 cancelIdle();
364 if (_open)
365 _manager.endPointClosed(this);
366 _open=false;
367 _key = null;
368 }
369 }
370 }
371 else
372 {
373 _key.interestOps(_interestOps);
374 }
375 }
376 else
377 {
378 if (_key.isValid())
379 _key.interestOps(0);
380 else
381 _key=null;
382 }
383 }
384 else
385 {
386 if (_key!=null && _key.isValid())
387 _key.cancel();
388
389 cancelIdle();
390 if (_open)
391 _manager.endPointClosed(this);
392 _open=false;
393 _key = null;
394 }
395 }
396 }
397
398
399
400
401 public void run()
402 {
403
404 boolean dispatched=true;
405 do
406 {
407 try
408 {
409 _connection.handle();
410 }
411 catch (ClosedChannelException e)
412 {
413 Log.ignore(e);
414 }
415 catch (EofException e)
416 {
417 Log.debug("EOF", e);
418 try{close();}
419 catch(IOException e2){Log.ignore(e2);}
420 }
421 catch (HttpException e)
422 {
423 Log.debug("BAD", e);
424 try{close();}
425 catch(IOException e2){Log.ignore(e2);}
426 }
427 catch (Throwable e)
428 {
429 Log.warn("handle failed", e);
430 try{close();}
431 catch(IOException e2){Log.ignore(e2);}
432 }
433 finally
434 {
435 dispatched=!undispatch();
436 }
437 }
438 while(dispatched);
439 }
440
441
442
443
444
445 public void close() throws IOException
446 {
447 try
448 {
449 super.close();
450 }
451 catch (IOException e)
452 {
453 Log.ignore(e);
454 }
455 finally
456 {
457 updateKey();
458 }
459 }
460
461
462 public String toString()
463 {
464 return "SCEP@" + hashCode() + "[d=" + _dispatched + ",io=" + _interestOps + ",w=" + _writable + ",b=" + _readBlocked + "|" + _writeBlocked + "]";
465 }
466
467
468 public Timeout.Task getTimeoutTask()
469 {
470 return _idleTask;
471 }
472
473
474 public SelectSet getSelectSet()
475 {
476 return _selectSet;
477 }
478
479
480
481
482 public class IdleTask extends Timeout.Task
483 {
484
485
486
487
488 public void expired()
489 {
490 idleExpired();
491 }
492
493 public String toString()
494 {
495 return "TimeoutTask:" + SelectChannelEndPoint.this.toString();
496 }
497
498 }
499
500 }