1 /*
   2  * Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.nio.ch;
  27 
  28 import java.io.*;
  29 import java.lang.ref.*;
  30 import java.net.*;
  31 import java.nio.*;
  32 import java.nio.channels.*;
  33 import java.security.AccessController;
  34 import java.security.PrivilegedExceptionAction;
  35 import java.util.*;
  36 
  37 
  38 // Make a socket channel look like a socket.
  39 //
  40 // The only aspects of java.net.Socket-hood that we don't attempt to emulate
  41 // here are the interrupted-I/O exceptions (which our Solaris implementations
  42 // attempt to support) and the sending of urgent data.  Otherwise an adapted
  43 // socket should look enough like a real java.net.Socket to fool most of the
  44 // developers most of the time, right down to the exception message strings.
  45 //
  46 // The methods in this class are defined in exactly the same order as in
  47 // java.net.Socket so as to simplify tracking future changes to that class.
  48 //
  49 
  50 public class SocketAdaptor
  51     extends Socket
  52 {
  53 
  54     // The channel being adapted
  55     private final SocketChannelImpl sc;
  56 
  57     // Timeout "option" value for reads
  58     private volatile int timeout = 0;
  59 
  60     // ## super will create a useless impl
  61     private SocketAdaptor(SocketChannelImpl sc) {
  62         this.sc = sc;
  63     }
  64 
  65     public static Socket create(SocketChannelImpl sc) {
  66         return new SocketAdaptor(sc);
  67     }
  68 
  69     public SocketChannel getChannel() {
  70         return sc;
  71     }
  72 
  73     // Override this method just to protect against changes in the superclass
  74     //
  75     public void connect(SocketAddress remote) throws IOException {
  76         connect(remote, 0);
  77     }
  78 
  79     public void connect(SocketAddress remote, int timeout) throws IOException {
  80         if (remote == null)
  81             throw new IllegalArgumentException("connect: The address can't be null");
  82         if (timeout < 0)
  83             throw new IllegalArgumentException("connect: timeout can't be negative");
  84 
  85         synchronized (sc.blockingLock()) {
  86             if (!sc.isBlocking())
  87                 throw new IllegalBlockingModeException();
  88 
  89             try {
  90 
  91                 if (timeout == 0) {
  92                     sc.connect(remote);
  93                     return;
  94                 }
  95 
  96                 // Implement timeout with a selector
  97                 SelectionKey sk = null;
  98                 Selector sel = null;
  99                 sc.configureBlocking(false);
 100                 try {
 101                     if (sc.connect(remote))
 102                         return;
 103                     sel = Util.getTemporarySelector(sc);
 104                     sk = sc.register(sel, SelectionKey.OP_CONNECT);
 105                     long to = timeout;
 106                     for (;;) {
 107                         if (!sc.isOpen())
 108                             throw new ClosedChannelException();
 109                         long st = System.currentTimeMillis();
 110                         int ns = sel.select(to);
 111                         if (ns > 0 &&
 112                             sk.isConnectable() && sc.finishConnect())
 113                             break;
 114                         sel.selectedKeys().remove(sk);
 115                         to -= System.currentTimeMillis() - st;
 116                         if (to <= 0) {
 117                             try {
 118                                 sc.close();
 119                             } catch (IOException x) { }
 120                             throw new SocketTimeoutException();
 121                         }
 122                     }
 123                 } finally {
 124                     if (sk != null)
 125                         sk.cancel();
 126                     if (sc.isOpen())
 127                         sc.configureBlocking(true);
 128                     if (sel != null)
 129                         Util.releaseTemporarySelector(sel);
 130                 }
 131 
 132             } catch (Exception x) {
 133                 Net.translateException(x, true);
 134             }
 135         }
 136 
 137     }
 138 
 139     public void bind(SocketAddress local) throws IOException {
 140         try {
 141             sc.bind(local);
 142         } catch (Exception x) {
 143             Net.translateException(x);
 144         }
 145     }
 146 
 147     public InetAddress getInetAddress() {
 148         SocketAddress remote = sc.remoteAddress();
 149         if (remote == null) {
 150             return null;
 151         } else {
 152             return ((InetSocketAddress)remote).getAddress();
 153         }
 154     }
 155 
 156     public InetAddress getLocalAddress() {
 157         if (sc.isOpen()) {
 158             SocketAddress local = sc.localAddress();
 159             if (local != null)
 160                 return ((InetSocketAddress)local).getAddress();
 161         }
 162         return new InetSocketAddress(0).getAddress();
 163     }
 164 
 165     public int getPort() {
 166         SocketAddress remote = sc.remoteAddress();
 167         if (remote == null) {
 168             return 0;
 169         } else {
 170             return ((InetSocketAddress)remote).getPort();
 171         }
 172     }
 173 
 174     public int getLocalPort() {
 175         SocketAddress local = sc.localAddress();
 176         if (local == null) {
 177             return -1;
 178         } else {
 179             return ((InetSocketAddress)local).getPort();
 180         }
 181     }
 182 
 183     private class SocketInputStream
 184         extends ChannelInputStream
 185     {
 186         private SocketInputStream() {
 187             super(sc);
 188         }
 189 
 190         protected int read(ByteBuffer bb)
 191             throws IOException
 192         {
 193             synchronized (sc.blockingLock()) {
 194                 if (!sc.isBlocking())
 195                     throw new IllegalBlockingModeException();
 196                 if (timeout == 0)
 197                     return sc.read(bb);
 198 
 199                 // Implement timeout with a selector
 200                 SelectionKey sk = null;
 201                 Selector sel = null;
 202                 sc.configureBlocking(false);
 203                 try {
 204                     int n;
 205                     if ((n = sc.read(bb)) != 0)
 206                         return n;
 207                     sel = Util.getTemporarySelector(sc);
 208                     sk = sc.register(sel, SelectionKey.OP_READ);
 209                     long to = timeout;
 210                     for (;;) {
 211                         if (!sc.isOpen())
 212                             throw new ClosedChannelException();
 213                         long st = System.currentTimeMillis();
 214                         int ns = sel.select(to);
 215                         if (ns > 0 && sk.isReadable()) {
 216                             if ((n = sc.read(bb)) != 0)
 217                                 return n;
 218                         }
 219                         sel.selectedKeys().remove(sk);
 220                         to -= System.currentTimeMillis() - st;
 221                         if (to <= 0)
 222                             throw new SocketTimeoutException();
 223                     }
 224                 } finally {
 225                     if (sk != null)
 226                         sk.cancel();
 227                     if (sc.isOpen())
 228                         sc.configureBlocking(true);
 229                     if (sel != null)
 230                         Util.releaseTemporarySelector(sel);
 231                 }
 232 
 233             }
 234         }
 235     }
 236 
 237     private InputStream socketInputStream = null;
 238 
 239     public InputStream getInputStream() throws IOException {
 240         if (!sc.isOpen())
 241             throw new SocketException("Socket is closed");
 242         if (!sc.isConnected())
 243             throw new SocketException("Socket is not connected");
 244         if (!sc.isInputOpen())
 245             throw new SocketException("Socket input is shutdown");
 246         if (socketInputStream == null) {
 247             try {
 248                 socketInputStream = AccessController.doPrivileged(
 249                     new PrivilegedExceptionAction<InputStream>() {
 250                         public InputStream run() throws IOException {
 251                             return new SocketInputStream();
 252                         }
 253                     });
 254             } catch (java.security.PrivilegedActionException e) {
 255                 throw (IOException)e.getException();
 256             }
 257         }
 258         return socketInputStream;
 259     }
 260 
 261     public OutputStream getOutputStream() throws IOException {
 262         if (!sc.isOpen())
 263             throw new SocketException("Socket is closed");
 264         if (!sc.isConnected())
 265             throw new SocketException("Socket is not connected");
 266         if (!sc.isOutputOpen())
 267             throw new SocketException("Socket output is shutdown");
 268         OutputStream os = null;
 269         try {
 270             os = AccessController.doPrivileged(
 271                 new PrivilegedExceptionAction<OutputStream>() {
 272                     public OutputStream run() throws IOException {
 273                         return Channels.newOutputStream(sc);
 274                     }
 275                 });
 276         } catch (java.security.PrivilegedActionException e) {
 277             throw (IOException)e.getException();
 278         }
 279         return os;
 280     }
 281 
 282     private void setBooleanOption(SocketOption<Boolean> name, boolean value)
 283         throws SocketException
 284     {
 285         try {
 286             sc.setOption(name, value);
 287         } catch (IOException x) {
 288             Net.translateToSocketException(x);
 289         }
 290     }
 291 
 292     private void setIntOption(SocketOption<Integer> name, int value)
 293         throws SocketException
 294     {
 295         try {
 296             sc.setOption(name, value);
 297         } catch (IOException x) {
 298             Net.translateToSocketException(x);
 299         }
 300     }
 301 
 302     private boolean getBooleanOption(SocketOption<Boolean> name) throws SocketException {
 303         try {
 304             return sc.getOption(name).booleanValue();
 305         } catch (IOException x) {
 306             Net.translateToSocketException(x);
 307             return false;       // keep compiler happy
 308         }
 309     }
 310 
 311     private int getIntOption(SocketOption<Integer> name) throws SocketException {
 312         try {
 313             return sc.getOption(name).intValue();
 314         } catch (IOException x) {
 315             Net.translateToSocketException(x);
 316             return -1;          // keep compiler happy
 317         }
 318     }
 319 
 320     public void setTcpNoDelay(boolean on) throws SocketException {
 321         setBooleanOption(StandardSocketOption.TCP_NODELAY, on);
 322     }
 323 
 324     public boolean getTcpNoDelay() throws SocketException {
 325         return getBooleanOption(StandardSocketOption.TCP_NODELAY);
 326     }
 327 
 328     public void setSoLinger(boolean on, int linger) throws SocketException {
 329         if (!on)
 330             linger = -1;
 331         setIntOption(StandardSocketOption.SO_LINGER, linger);
 332     }
 333 
 334     public int getSoLinger() throws SocketException {
 335         return getIntOption(StandardSocketOption.SO_LINGER);
 336     }
 337 
 338     public void sendUrgentData(int data) throws IOException {
 339         synchronized (sc.blockingLock()) {
 340             if (!sc.isBlocking())
 341                 throw new IllegalBlockingModeException();
 342             int n = sc.sendOutOfBandData((byte)data);
 343             assert n == 1;
 344         }
 345     }
 346 
 347     public void setOOBInline(boolean on) throws SocketException {
 348         setBooleanOption(ExtendedSocketOption.SO_OOBINLINE, on);
 349     }
 350 
 351     public boolean getOOBInline() throws SocketException {
 352         return getBooleanOption(ExtendedSocketOption.SO_OOBINLINE);
 353     }
 354 
 355     public void setSoTimeout(int timeout) throws SocketException {
 356         if (timeout < 0)
 357             throw new IllegalArgumentException("timeout can't be negative");
 358         this.timeout = timeout;
 359     }
 360 
 361     public int getSoTimeout() throws SocketException {
 362         return timeout;
 363     }
 364 
 365     public void setSendBufferSize(int size) throws SocketException {
 366         // size 0 valid for SocketChannel, invalid for Socket
 367         if (size <= 0)
 368             throw new IllegalArgumentException("Invalid send size");
 369         setIntOption(StandardSocketOption.SO_SNDBUF, size);
 370     }
 371 
 372     public int getSendBufferSize() throws SocketException {
 373         return getIntOption(StandardSocketOption.SO_SNDBUF);
 374     }
 375 
 376     public void setReceiveBufferSize(int size) throws SocketException {
 377         // size 0 valid for SocketChannel, invalid for Socket
 378         if (size <= 0)
 379             throw new IllegalArgumentException("Invalid receive size");
 380         setIntOption(StandardSocketOption.SO_RCVBUF, size);
 381     }
 382 
 383     public int getReceiveBufferSize() throws SocketException {
 384         return getIntOption(StandardSocketOption.SO_RCVBUF);
 385     }
 386 
 387     public void setKeepAlive(boolean on) throws SocketException {
 388         setBooleanOption(StandardSocketOption.SO_KEEPALIVE, on);
 389     }
 390 
 391     public boolean getKeepAlive() throws SocketException {
 392         return getBooleanOption(StandardSocketOption.SO_KEEPALIVE);
 393     }
 394 
 395     public void setTrafficClass(int tc) throws SocketException {
 396         setIntOption(StandardSocketOption.IP_TOS, tc);
 397     }
 398 
 399     public int getTrafficClass() throws SocketException {
 400         return getIntOption(StandardSocketOption.IP_TOS);
 401     }
 402 
 403     public void setReuseAddress(boolean on) throws SocketException {
 404         setBooleanOption(StandardSocketOption.SO_REUSEADDR, on);
 405     }
 406 
 407     public boolean getReuseAddress() throws SocketException {
 408         return getBooleanOption(StandardSocketOption.SO_REUSEADDR);
 409     }
 410 
 411     public void close() throws IOException {
 412         sc.close();
 413     }
 414 
 415     public void shutdownInput() throws IOException {
 416         try {
 417             sc.shutdownInput();
 418         } catch (Exception x) {
 419             Net.translateException(x);
 420         }
 421     }
 422 
 423     public void shutdownOutput() throws IOException {
 424         try {
 425             sc.shutdownOutput();
 426         } catch (Exception x) {
 427             Net.translateException(x);
 428         }
 429     }
 430 
 431     public String toString() {
 432         if (sc.isConnected())
 433             return "Socket[addr=" + getInetAddress() +
 434                 ",port=" + getPort() +
 435                 ",localport=" + getLocalPort() + "]";
 436         return "Socket[unconnected]";
 437     }
 438 
 439     public boolean isConnected() {
 440         return sc.isConnected();
 441     }
 442 
 443     public boolean isBound() {
 444         return sc.localAddress() != null;
 445     }
 446 
 447     public boolean isClosed() {
 448         return !sc.isOpen();
 449     }
 450 
 451     public boolean isInputShutdown() {
 452         return !sc.isInputOpen();
 453     }
 454 
 455     public boolean isOutputShutdown() {
 456         return !sc.isOutputOpen();
 457     }
 458 
 459 }