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