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