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