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