OSDN Git Service

Merged gcj-eclipse branch to trunk.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / gnu / java / net / PlainSocketImpl.java
1 /* PlainSocketImpl.java -- Default socket implementation
2    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
3    Free Software Foundation, Inc.
4
5 This file is part of GNU Classpath.
6
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11  
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING.  If not, write to the
19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301 USA.
21
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library.  Thus, the terms and
24 conditions of the GNU General Public License cover the whole
25 combination.
26
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
31 terms of your choice, provided that you also meet, for each linked
32 independent module, the terms and conditions of the license of that
33 module.  An independent module is a module which is not derived from
34 or based on this library.  If you modify this library, you may extend
35 this exception to your version of the library, but you are not
36 obligated to do so.  If you do not wish to do so, delete this
37 exception statement from your version. */
38
39
40 package gnu.java.net;
41
42 import gnu.java.nio.SocketChannelImpl;
43 import gnu.java.nio.VMChannel;
44
45 import java.io.InputStream;
46 import java.io.IOException;
47 import java.io.InterruptedIOException;
48 import java.io.OutputStream;
49 import java.net.InetAddress;
50 import java.net.InetSocketAddress;
51 import java.net.SocketAddress;
52 import java.net.SocketException;
53 import java.net.SocketImpl;
54 import java.net.SocketTimeoutException;
55 import java.nio.ByteBuffer;
56
57 /**
58  * Written using on-line Java Platform 1.2 API Specification, as well
59  * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
60  * Status:  Believed complete and correct.
61  */
62
63 /**
64  * Unless the application installs its own SocketImplFactory, this is the
65  * default socket implemetation that will be used.  It simply uses a
66  * combination of Java and native routines to implement standard BSD
67  * style sockets of family AF_INET and types SOCK_STREAM and SOCK_DGRAM
68  *
69  * @author Per Bothner (bothner@cygnus.com)
70  * @author Nic Ferrier (nferrier@tapsellferrier.co.uk)
71  * @author Aaron M. Renn (arenn@urbanophile.com)
72  */
73 public class PlainSocketImpl extends SocketImpl
74 {
75
76   /**
77    * The underlying plain socket VM implementation.
78    */
79   protected VMPlainSocketImpl impl;
80
81   /**
82    * A cached copy of the in stream for reading from the socket.
83    */
84   private InputStream in;
85
86   /**
87    * A cached copy of the out stream for writing to the socket.
88    */
89   private OutputStream out;
90
91   /**
92    * Indicates whether a channel initiated whatever operation
93    * is being invoked on this socket.
94    */
95   private boolean inChannelOperation;
96   
97   /**
98    * The socket channel we use for IO operation. Package-private for
99    * use by inner classes.
100    */
101   SocketChannelImpl channel;
102   
103   /**
104    * Indicates whether we should ignore whether any associated
105    * channel is set to non-blocking mode. Certain operations
106    * throw an <code>IllegalBlockingModeException</code> if the
107    * associated channel is in non-blocking mode, <i>except</i>
108    * if the operation is invoked by the channel itself.
109    */
110   public final boolean isInChannelOperation()
111   {
112     return inChannelOperation;
113   }
114   
115   /**
116    * Sets our indicator of whether an I/O operation is being
117    * initiated by a channel.
118    */
119   public final void setInChannelOperation(boolean b)
120   {
121     inChannelOperation = b;
122   }
123  
124   /**
125    * Default do nothing constructor.
126    */
127   public PlainSocketImpl()
128   {
129     this.impl = new VMPlainSocketImpl();
130   }
131
132   /**
133    * Sets the specified option on a socket to the passed in object.  For
134    * options that take an integer argument, the passed in object is an
135    * Integer.  The option_id parameter is one of the defined constants in
136    * this interface.
137    *
138    * @param optionId The identifier of the option
139    * @param value The value to set the option to
140    *
141    * @throws SocketException if an error occurs
142    */
143   public void setOption(int optionId, Object value) throws SocketException
144   {
145     switch (optionId)
146       {
147         case SO_LINGER:
148         case IP_MULTICAST_LOOP:
149         case SO_BROADCAST:
150         case SO_KEEPALIVE:
151         case SO_OOBINLINE:
152         case TCP_NODELAY:          
153         case IP_TOS:
154         case SO_RCVBUF:
155         case SO_SNDBUF:
156         case SO_TIMEOUT:
157         case SO_REUSEADDR:
158           impl.setOption(optionId, value);
159           return;
160         default:
161           throw new SocketException("Unrecognized TCP option: " + optionId);
162       }
163   }
164
165   /**
166    * Returns the current setting of the specified option.  The Object returned
167    * will be an Integer for options that have integer values.  The option_id
168    * is one of the defined constants in this interface.
169    *
170    * @param optionId the option identifier
171    *
172    * @return the current value of the option
173    *
174    * @throws SocketException if an error occurs
175    */
176   public Object getOption(int optionId) throws SocketException
177   {
178     if (optionId == SO_BINDADDR)
179       {
180         try
181           {
182             return channel.getVMChannel().getLocalAddress().getAddress();
183           }
184         catch (IOException ioe)
185           {
186             SocketException se = new SocketException();
187             se.initCause(ioe);
188             throw se;
189           }
190       }
191     
192     // This filters options which are invalid for TCP.
193     switch (optionId)
194     {
195       case SO_LINGER:
196       case IP_MULTICAST_LOOP:
197       case SO_BROADCAST:
198       case SO_KEEPALIVE:
199       case SO_OOBINLINE:
200       case TCP_NODELAY:          
201       case IP_TOS:
202       case SO_RCVBUF:
203       case SO_SNDBUF:
204       case SO_TIMEOUT:
205       case SO_REUSEADDR:
206         return impl.getOption(optionId);
207       default:
208         throw new SocketException("Unrecognized TCP option: " + optionId);
209     }
210     
211   }
212
213   public void shutdownInput() throws IOException
214   {
215     impl.shutdownInput();
216   }
217
218   public void shutdownOutput() throws IOException
219   {
220     impl.shutdownOutput();
221   }
222
223   /**
224    * Creates a new socket that is not bound to any local address/port and
225    * is not connected to any remote address/port.  The stream parameter will be
226    * ignored since PlainSocketImpl always is a stream socket. Datagram sockets
227    * are handled by PlainDatagramSocketImpl.
228    *
229    * @param stream <code>true</code> for stream sockets, <code>false</code> for
230    *        datagram sockets
231    */
232   protected synchronized void create(boolean stream) throws IOException
233   {
234     channel = new SocketChannelImpl(false);
235     VMChannel vmchannel = channel.getVMChannel();
236     vmchannel.initSocket(stream);
237     channel.configureBlocking(true);
238     impl.getState().setChannelFD(vmchannel.getState());
239   }
240
241   /**
242    * Connects to the remote hostname and port specified as arguments.
243    *
244    * @param hostname the remote hostname to connect to
245    * @param port the remote port to connect to
246    *
247    * @throws IOException If an error occurs
248    */
249   protected synchronized void connect(String hostname, int port)
250     throws IOException
251   {
252     connect(InetAddress.getByName(hostname), port);
253   }
254
255   /**
256    * Connects to the remote address and port specified as arguments.
257    *
258    * @param addr the remote address to connect to
259    * @param port the remote port to connect to
260    *
261    * @throws IOException If an error occurs
262    */
263   protected void connect(InetAddress addr, int port) throws IOException
264   {
265     connect(new InetSocketAddress(addr, port), 0);
266   }
267
268   /**
269    * Connects to the remote socket address with a specified timeout.
270    *
271    * @param address the remote address to connect to
272    * @param timeout the timeout to use for this connect, 0 means infinite.
273    *
274    * @throws IOException If an error occurs
275    */
276   protected synchronized void connect(SocketAddress address, int timeout)
277     throws IOException
278   {
279     if (channel == null)
280       create(true);
281     boolean connected = channel.connect(address, timeout);
282     if (!connected)
283       throw new SocketTimeoutException("connect timed out");
284     
285     // Using the given SocketAddress is important to preserve
286     // hostnames given by the caller.
287     InetSocketAddress addr = (InetSocketAddress) address; 
288     this.address = addr.getAddress();
289     this.port = addr.getPort();
290   }
291
292   /**
293    * Binds to the specified port on the specified addr.  Note that this addr
294    * must represent a local IP address.  **** How bind to INADDR_ANY? ****
295    *
296    * @param addr the address to bind to
297    * @param port the port number to bind to
298    *
299    * @throws IOException if an error occurs
300    */
301   protected synchronized void bind(InetAddress addr, int port)
302     throws IOException
303   {
304     if (channel == null)
305       create(true);
306     impl.bind(new InetSocketAddress(addr, port));
307     localport = channel.getVMChannel().getLocalAddress().getPort();
308   }
309
310   /**
311    * Starts listening for connections on a socket. The queuelen parameter
312    * is how many pending connections will queue up waiting to be serviced
313    * before being accept'ed.  If the queue of pending requests exceeds this
314    * number, additional connections will be refused.
315    *
316    * @param queuelen The length of the pending connection queue
317    * 
318    * @throws IOException If an error occurs
319    */
320   protected synchronized void listen(int queuelen)
321     throws IOException
322   {
323     impl.listen(queuelen);
324   }
325
326   /**
327    * Accepts a new connection on this socket and returns in in the 
328    * passed in SocketImpl.
329    *
330    * @param impl The SocketImpl object to accept this connection.
331    */
332   protected synchronized void accept(SocketImpl impl)
333     throws IOException
334   {
335     if (channel == null)
336         create(true);
337     if (!(impl instanceof PlainSocketImpl))
338       throw new IOException("incompatible SocketImpl: "
339                             + impl.getClass().getName());
340     PlainSocketImpl that = (PlainSocketImpl) impl;
341     VMChannel c = channel.getVMChannel().accept();
342     that.impl.getState().setChannelFD(c.getState());
343     that.channel = new SocketChannelImpl(c);
344     that.setOption(SO_REUSEADDR, Boolean.TRUE);
345     // Reset the inherited timeout.
346     that.setOption(SO_TIMEOUT, Integer.valueOf(0));
347
348   }
349
350   /**
351    * Returns the number of bytes that the caller can read from this socket
352    * without blocking. 
353    *
354    * @return the number of readable bytes before blocking
355    *
356    * @throws IOException if an error occurs
357    */
358   protected int available() throws IOException
359   {
360     if (channel == null)
361       throw new SocketException("not connected");
362     return channel.getVMChannel().available();
363   }
364
365   /**
366    * Closes the socket.  This will cause any InputStream or OutputStream
367    * objects for this Socket to be closed as well.
368    *
369    * <p>
370    * Note that if the SO_LINGER option is set on this socket, then the
371    * operation could block.
372    * </p>
373    *
374    * @throws IOException if an error occurs
375    */
376   protected void close() throws IOException
377   {
378     if (impl.getState().isValid())
379       impl.close();
380     
381     address = null;
382     port = -1;
383   }
384
385   public void sendUrgentData(int data) throws IOException
386   {
387     impl.sendUrgentData(data);
388   }
389
390   /**
391    * Returns an InputStream object for reading from this socket.  This will
392    * be an instance of SocketInputStream.
393    *
394    * @return An input stream attached to the socket.
395    *
396    * @exception IOException If an error occurs
397    */
398   protected synchronized InputStream getInputStream() throws IOException
399   {
400     if (in == null)
401       in = new SocketInputStream();
402
403     return in;
404   }
405
406   /**
407    * Returns an OutputStream object for writing to this socket.  This will
408    * be an instance of SocketOutputStream.
409    *
410    * @return An output stream attached to the socket.
411    *
412    * @exception IOException If an error occurs
413    */
414   protected synchronized OutputStream getOutputStream() throws IOException
415   {
416     if (out == null)
417       out = new SocketOutputStream();
418
419     return out;
420   }
421   
422   public VMChannel getVMChannel()
423   {
424     if (channel == null)
425       return null;
426     return channel.getVMChannel();
427   }
428
429   /* (non-Javadoc)
430    * @see java.net.SocketImpl#getInetAddress()
431    */
432   protected InetAddress getInetAddress()
433   {
434     if (channel == null)
435       return null;
436     
437     try
438       {
439         InetSocketAddress remote = channel.getVMChannel().getPeerAddress();
440         if (remote == null)
441           return null;
442         // To mimic behavior of the RI the InetAddress instance which was
443         // used to establish the connection is returned instead of one that
444         // was created by the native layer (this preserves exact hostnames).
445         if (address != null)
446           return address;
447         
448         return remote.getAddress();
449       }
450     catch (IOException ioe)
451       {
452         return null;
453       }
454   }
455
456   /* (non-Javadoc)
457    * @see java.net.SocketImpl#getLocalPort()
458    */
459   protected int getLocalPort()
460   {
461     if (channel == null)
462       return -1;
463     try
464       {
465         InetSocketAddress local = channel.getVMChannel().getLocalAddress();
466         if (local == null)
467           return -1;
468         return local.getPort();
469       }
470     catch (IOException ioe)
471       {
472         return -1;
473       }
474   }
475   
476   public InetSocketAddress getLocalAddress()
477   {
478     if (channel == null)
479       return null;
480     try
481       {
482         return channel.getVMChannel().getLocalAddress();
483       }
484     catch (IOException ioe)
485       {
486         return null;
487       }
488   }
489
490   /* (non-Javadoc)
491    * @see java.net.SocketImpl#getPort()
492    */
493   protected int getPort()
494   {
495     if (channel == null)
496       return -1;
497     
498     try
499       {
500         InetSocketAddress remote = channel.getVMChannel().getPeerAddress();
501         if (remote == null)
502           return -1;
503         return remote.getPort();
504       }
505     catch (IOException ioe)
506       {
507         return -1;
508       }
509   }
510
511   /**
512    * This class contains an implementation of <code>InputStream</code> for 
513    * sockets.  It in an internal only class used by <code>PlainSocketImpl</code>.
514    *
515    * @author Nic Ferrier <nferrier@tapsellferrier.co.uk>
516    */
517   final class SocketInputStream
518     extends InputStream
519   {
520     /**
521      * Returns the number of bytes available to be read before blocking
522      */
523     public int available() throws IOException
524     {
525       return PlainSocketImpl.this.available();
526     }
527
528     /**
529      * This method not only closes the stream, it closes the underlying socket
530      * (and thus any connection) and invalidates any other Input/Output streams
531      * for the underlying impl object
532      */
533     public void close() throws IOException
534     {
535       PlainSocketImpl.this.close();
536     }
537
538     /**
539      * Reads the next byte of data and returns it as an int.  
540      *
541      * @return The byte read (as an int) or -1 if end of stream);
542      *
543      * @exception IOException If an error occurs.
544      */
545     public int read() throws IOException
546     {
547       if (channel == null)
548         throw new SocketException("not connected");
549       while (true)
550         {
551           try
552             {
553               return channel.getVMChannel().read();
554             }
555           catch (SocketTimeoutException ste)
556             {
557               throw ste;
558             }
559           catch (InterruptedIOException iioe)
560             {
561               // Ignore; NIO may throw this; net io shouldn't
562             }
563         }
564     }
565
566     /**
567      * Reads up to len bytes of data into the caller supplied buffer starting
568      * at offset bytes from the start of the buffer
569      *
570      * @param buf The buffer
571      * @param offset Offset into the buffer to start reading from
572      * @param len The number of bytes to read
573      *
574      * @return The number of bytes actually read or -1 if end of stream
575      *
576      * @exception IOException If an error occurs.
577      */
578     public int read (byte[] buf, int offset, int len) throws IOException
579     {
580       if (channel == null)
581         throw new SocketException("not connected");
582       ByteBuffer b = ByteBuffer.wrap(buf, offset, len);
583       while (true)
584         {
585           try
586             {
587               return channel.read(b);
588             }
589           catch (SocketTimeoutException ste)
590             {
591               throw ste;
592             }
593           catch (InterruptedIOException iioe)
594             {
595               // Ignored; NIO may throw this; net IO not.
596             }
597         }
598     }
599   }
600
601   /**
602    * This class is used internally by <code>PlainSocketImpl</code> to be the 
603    * <code>OutputStream</code> subclass returned by its 
604    * <code>getOutputStream method</code>.  It expects only to  be used in that
605    * context.
606    *
607    * @author Nic Ferrier  <nferrier@tapsellferrier.co.uk>
608    */
609   final class SocketOutputStream
610     extends OutputStream
611   {
612     /**
613      * This method closes the stream and the underlying socket connection. This
614      * action also effectively closes any other InputStream or OutputStream
615      * object associated with the connection.
616      *
617      * @exception IOException If an error occurs
618      */
619     public void close() throws IOException
620     {
621       PlainSocketImpl.this.close();
622     }
623
624     /**
625      * Writes a byte (passed in as an int) to the given output stream
626      * 
627      * @param b The byte to write
628      *
629      * @exception IOException If an error occurs
630      */
631     public void write(int b) throws IOException
632     {
633       if (channel == null)
634         throw new SocketException("not connected");
635       while (true)
636         {
637           try
638             {
639               channel.getVMChannel().write(b);
640               return;
641             }
642           catch (InterruptedIOException iioe)
643             {
644               // Ignored.
645             }
646         }
647     }
648
649     /**
650      * Writes len number of bytes from the array buf to the stream starting
651      * at offset bytes into the buffer.
652      *
653      * @param buf The buffer
654      * @param offset Offset into the buffer to start writing from
655      * @param len The number of bytes to write
656      *
657      * @exception IOException If an error occurs.
658      */
659     public void write (byte[] buf, int offset, int len) throws IOException
660     {
661       if (channel == null)
662         throw new SocketException("not connected");
663       ByteBuffer b = ByteBuffer.wrap(buf, offset, len);
664       while (b.hasRemaining())
665         {
666           try
667             {
668               if (channel.write(b) == -1)
669                 throw new IOException("channel has been closed");
670             }
671           catch (InterruptedIOException iioe)
672             {
673               // Ignored.
674             }
675         }
676     }
677   }
678 }