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