View Javadoc

1   // ========================================================================
2   // Copyright (c) 2004-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.io.nio;
15  
16  import java.io.IOException;
17  import java.net.InetSocketAddress;
18  import java.net.Socket;
19  import java.net.SocketException;
20  import java.nio.ByteBuffer;
21  import java.nio.channels.ByteChannel;
22  import java.nio.channels.GatheringByteChannel;
23  import java.nio.channels.SelectableChannel;
24  import java.nio.channels.SocketChannel;
25  
26  import org.eclipse.jetty.io.Buffer;
27  import org.eclipse.jetty.io.EndPoint;
28  import org.eclipse.jetty.util.StringUtil;
29  import org.eclipse.jetty.util.log.Log;
30  import org.eclipse.jetty.util.log.Logger;
31  
32  /**
33   * Channel End Point.
34   * <p>Holds the channel and socket for an NIO endpoint.
35   *
36   */
37  public class ChannelEndPoint implements EndPoint
38  {
39      private static final Logger LOG = Log.getLogger(ChannelEndPoint.class);
40  
41      protected final ByteChannel _channel;
42      protected final ByteBuffer[] _gather2=new ByteBuffer[2];
43      protected final Socket _socket;
44      protected final InetSocketAddress _local;
45      protected final InetSocketAddress _remote;
46      protected volatile int _maxIdleTime;
47      private volatile boolean _ishut;
48      private volatile boolean _oshut;
49  
50      public ChannelEndPoint(ByteChannel channel) throws IOException
51      {
52          super();
53          this._channel = channel;
54          _socket=(channel instanceof SocketChannel)?((SocketChannel)channel).socket():null;
55          if (_socket!=null)
56          {
57              _local=(InetSocketAddress)_socket.getLocalSocketAddress();
58              _remote=(InetSocketAddress)_socket.getRemoteSocketAddress();
59              _maxIdleTime=_socket.getSoTimeout();
60          }
61          else
62          {
63              _local=_remote=null;
64          }
65      }
66  
67      protected ChannelEndPoint(ByteChannel channel, int maxIdleTime) throws IOException
68      {
69          this._channel = channel;
70          _maxIdleTime=maxIdleTime;
71          _socket=(channel instanceof SocketChannel)?((SocketChannel)channel).socket():null;
72          if (_socket!=null)
73          {
74              _local=(InetSocketAddress)_socket.getLocalSocketAddress();
75              _remote=(InetSocketAddress)_socket.getRemoteSocketAddress();
76              _socket.setSoTimeout(_maxIdleTime);
77          }
78          else
79          {
80              _local=_remote=null;
81          }
82      }
83  
84      public boolean isBlocking()
85      {
86          return  !(_channel instanceof SelectableChannel) || ((SelectableChannel)_channel).isBlocking();
87      }
88  
89      public boolean blockReadable(long millisecs) throws IOException
90      {
91          return true;
92      }
93  
94      public boolean blockWritable(long millisecs) throws IOException
95      {
96          return true;
97      }
98  
99      /*
100      * @see org.eclipse.io.EndPoint#isOpen()
101      */
102     public boolean isOpen()
103     {
104         return _channel.isOpen();
105     }
106 
107     /** Shutdown the channel Input.
108      * Cannot be overridden. To override, see {@link #shutdownInput()}
109      * @throws IOException
110      */
111     protected final void shutdownChannelInput() throws IOException
112     {
113         LOG.debug("ishut {}", this);
114         _ishut = true;
115         if (_channel.isOpen())
116         {
117             if (_socket != null)
118             {
119                 try
120                 {
121                     if (!_socket.isInputShutdown())
122                     {
123                         _socket.shutdownInput();
124                     }
125                 }
126                 catch (SocketException e)
127                 {
128                     LOG.debug(e.toString());
129                     LOG.ignore(e);
130                 }
131                 finally
132                 {
133                     if (_oshut)
134                     {
135                         close();
136                     }
137                 }
138             }
139         }
140     }
141 
142     /* (non-Javadoc)
143      * @see org.eclipse.io.EndPoint#close()
144      */
145     public void shutdownInput() throws IOException
146     {
147         shutdownChannelInput();
148     }
149 
150     protected final void shutdownChannelOutput() throws IOException
151     {
152         LOG.debug("oshut {}",this);
153         _oshut = true;
154         if (_channel.isOpen())
155         {
156             if (_socket != null)
157             {
158                 try
159                 {
160                     if (!_socket.isOutputShutdown())
161                     {
162                         _socket.shutdownOutput();
163                     }
164                 }
165                 catch (SocketException e)
166                 {
167                     LOG.debug(e.toString());
168                     LOG.ignore(e);
169                 }
170                 finally
171                 {
172                     if (_ishut)
173                     {
174                         close();
175                     }
176                 }
177             }
178         }
179     }
180 
181     /* (non-Javadoc)
182      * @see org.eclipse.io.EndPoint#close()
183      */
184     public void shutdownOutput() throws IOException
185     {
186         shutdownChannelOutput();
187     }
188 
189     public boolean isOutputShutdown()
190     {
191         return _oshut || !_channel.isOpen() || _socket != null && _socket.isOutputShutdown();
192     }
193 
194     public boolean isInputShutdown()
195     {
196         return _ishut || !_channel.isOpen() || _socket != null && _socket.isInputShutdown();
197     }
198 
199     /* (non-Javadoc)
200      * @see org.eclipse.io.EndPoint#close()
201      */
202     public void close() throws IOException
203     {
204         LOG.debug("close {}",this);
205         _channel.close();
206     }
207 
208     /* (non-Javadoc)
209      * @see org.eclipse.io.EndPoint#fill(org.eclipse.io.Buffer)
210      */
211     public int fill(Buffer buffer) throws IOException
212     {
213         if (_ishut)
214             return -1;
215         Buffer buf = buffer.buffer();
216         int len=0;
217         if (buf instanceof NIOBuffer)
218         {
219             final NIOBuffer nbuf = (NIOBuffer)buf;
220             final ByteBuffer bbuf=nbuf.getByteBuffer();
221 
222             //noinspection SynchronizationOnLocalVariableOrMethodParameter
223             try
224             {
225                 synchronized(bbuf)
226                 {
227                     try
228                     {
229                         bbuf.position(buffer.putIndex());
230                         len=_channel.read(bbuf);
231                     }
232                     finally
233                     {
234                         buffer.setPutIndex(bbuf.position());
235                         bbuf.position(0);
236                     }
237                 }
238 
239                 if (len<0 && isOpen())
240                 {
241                     if (!isInputShutdown())
242                         shutdownInput();
243                     if (isOutputShutdown())
244                         _channel.close();
245                 }
246             }
247             catch (IOException x)
248             {
249                 LOG.debug(x.toString());
250                 LOG.ignore(x);
251                 try
252                 {
253                     if (_channel.isOpen())
254                         _channel.close();
255                 }
256                 catch (Exception xx)
257                 {
258                     LOG.ignore(xx);
259                 }
260 
261                 if (len>0)
262                     throw x;
263                 len=-1;
264             }
265         }
266         else
267         {
268             throw new IOException("Not Implemented");
269         }
270 
271         return len;
272     }
273 
274     /* (non-Javadoc)
275      * @see org.eclipse.io.EndPoint#flush(org.eclipse.io.Buffer)
276      */
277     public int flush(Buffer buffer) throws IOException
278     {
279         Buffer buf = buffer.buffer();
280         int len=0;
281         if (buf instanceof NIOBuffer)
282         {
283             final NIOBuffer nbuf = (NIOBuffer)buf;
284             final ByteBuffer bbuf=nbuf.getByteBuffer();
285 
286             //noinspection SynchronizationOnLocalVariableOrMethodParameter
287             synchronized(bbuf)
288             {
289                 try
290                 {
291                     bbuf.position(buffer.getIndex());
292                     bbuf.limit(buffer.putIndex());
293                     len=_channel.write(bbuf);
294                 }
295                 finally
296                 {
297                     if (len>0)
298                         buffer.skip(len);
299                     bbuf.position(0);
300                     bbuf.limit(bbuf.capacity());
301                 }
302             }
303         }
304         else if (buf instanceof RandomAccessFileBuffer)
305         {
306             len = ((RandomAccessFileBuffer)buf).writeTo(_channel,buffer.getIndex(),buffer.length());
307             if (len>0)
308                 buffer.skip(len);
309         }
310         else if (buffer.array()!=null)
311         {
312             ByteBuffer b = ByteBuffer.wrap(buffer.array(), buffer.getIndex(), buffer.length());
313             len=_channel.write(b);
314             if (len>0)
315                 buffer.skip(len);
316         }
317         else
318         {
319             throw new IOException("Not Implemented");
320         }
321         return len;
322     }
323 
324     /* (non-Javadoc)
325      * @see org.eclipse.io.EndPoint#flush(org.eclipse.io.Buffer, org.eclipse.io.Buffer, org.eclipse.io.Buffer)
326      */
327     public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException
328     {
329         int length=0;
330 
331         Buffer buf0 = header==null?null:header.buffer();
332         Buffer buf1 = buffer==null?null:buffer.buffer();
333 
334         if (_channel instanceof GatheringByteChannel &&
335             header!=null && header.length()!=0 && buf0 instanceof NIOBuffer &&
336             buffer!=null && buffer.length()!=0 && buf1 instanceof NIOBuffer)
337         {
338             length = gatheringFlush(header,((NIOBuffer)buf0).getByteBuffer(),buffer,((NIOBuffer)buf1).getByteBuffer());
339         }
340         else
341         {
342             // flush header
343             if (header!=null && header.length()>0)
344                 length=flush(header);
345 
346             // flush buffer
347             if ((header==null || header.length()==0) &&
348                  buffer!=null && buffer.length()>0)
349                 length+=flush(buffer);
350 
351             // flush trailer
352             if ((header==null || header.length()==0) &&
353                 (buffer==null || buffer.length()==0) &&
354                  trailer!=null && trailer.length()>0)
355                 length+=flush(trailer);
356         }
357 
358         return length;
359     }
360 
361     protected int gatheringFlush(Buffer header, ByteBuffer bbuf0, Buffer buffer, ByteBuffer bbuf1) throws IOException
362     {
363         int length;
364 
365         synchronized(this)
366         {
367             // We must sync because buffers may be shared (eg nbuf1 is likely to be cached content).
368             //noinspection SynchronizationOnLocalVariableOrMethodParameter
369             synchronized(bbuf0)
370             {
371                 //noinspection SynchronizationOnLocalVariableOrMethodParameter
372                 synchronized(bbuf1)
373                 {
374                     try
375                     {
376                         // Adjust position indexs of buf0 and buf1
377                         bbuf0.position(header.getIndex());
378                         bbuf0.limit(header.putIndex());
379                         bbuf1.position(buffer.getIndex());
380                         bbuf1.limit(buffer.putIndex());
381 
382                         _gather2[0]=bbuf0;
383                         _gather2[1]=bbuf1;
384 
385                         // do the gathering write.
386                         length=(int)((GatheringByteChannel)_channel).write(_gather2);
387 
388                         int hl=header.length();
389                         if (length>hl)
390                         {
391                             header.clear();
392                             buffer.skip(length-hl);
393                         }
394                         else if (length>0)
395                         {
396                             header.skip(length);
397                         }
398                     }
399                     finally
400                     {
401                         bbuf0.position(0);
402                         bbuf1.position(0);
403                         bbuf0.limit(bbuf0.capacity());
404                         bbuf1.limit(bbuf1.capacity());
405                     }
406                 }
407             }
408         }
409         return length;
410     }
411 
412     /* ------------------------------------------------------------ */
413     /**
414      * @return Returns the channel.
415      */
416     public ByteChannel getChannel()
417     {
418         return _channel;
419     }
420 
421 
422     /* ------------------------------------------------------------ */
423     /*
424      * @see org.eclipse.io.EndPoint#getLocalAddr()
425      */
426     public String getLocalAddr()
427     {
428         if (_socket==null)
429             return null;
430        if (_local==null || _local.getAddress()==null || _local.getAddress().isAnyLocalAddress())
431            return StringUtil.ALL_INTERFACES;
432         return _local.getAddress().getHostAddress();
433     }
434 
435     /* ------------------------------------------------------------ */
436     /*
437      * @see org.eclipse.io.EndPoint#getLocalHost()
438      */
439     public String getLocalHost()
440     {
441         if (_socket==null)
442             return null;
443        if (_local==null || _local.getAddress()==null || _local.getAddress().isAnyLocalAddress())
444            return StringUtil.ALL_INTERFACES;
445         return _local.getAddress().getCanonicalHostName();
446     }
447 
448     /* ------------------------------------------------------------ */
449     /*
450      * @see org.eclipse.io.EndPoint#getLocalPort()
451      */
452     public int getLocalPort()
453     {
454         if (_socket==null)
455             return 0;
456         if (_local==null)
457             return -1;
458         return _local.getPort();
459     }
460 
461     /* ------------------------------------------------------------ */
462     /*
463      * @see org.eclipse.io.EndPoint#getRemoteAddr()
464      */
465     public String getRemoteAddr()
466     {
467         if (_socket==null)
468             return null;
469         if (_remote==null)
470             return null;
471         return _remote.getAddress().getHostAddress();
472     }
473 
474     /* ------------------------------------------------------------ */
475     /*
476      * @see org.eclipse.io.EndPoint#getRemoteHost()
477      */
478     public String getRemoteHost()
479     {
480         if (_socket==null)
481             return null;
482         if (_remote==null)
483             return null;
484         return _remote.getAddress().getCanonicalHostName();
485     }
486 
487     /* ------------------------------------------------------------ */
488     /*
489      * @see org.eclipse.io.EndPoint#getRemotePort()
490      */
491     public int getRemotePort()
492     {
493         if (_socket==null)
494             return 0;
495         return _remote==null?-1:_remote.getPort();
496     }
497 
498     /* ------------------------------------------------------------ */
499     /*
500      * @see org.eclipse.io.EndPoint#getConnection()
501      */
502     public Object getTransport()
503     {
504         return _channel;
505     }
506 
507     /* ------------------------------------------------------------ */
508     public void flush()
509         throws IOException
510     {
511     }
512 
513     /* ------------------------------------------------------------ */
514     public int getMaxIdleTime()
515     {
516         return _maxIdleTime;
517     }
518 
519     /* ------------------------------------------------------------ */
520     /**
521      * @see org.eclipse.jetty.io.bio.StreamEndPoint#setMaxIdleTime(int)
522      */
523     public void setMaxIdleTime(int timeMs) throws IOException
524     {
525         if (_socket!=null && timeMs!=_maxIdleTime)
526             _socket.setSoTimeout(timeMs>0?timeMs:0);
527         _maxIdleTime=timeMs;
528     }
529 }