1 /*
   2  * Copyright (c) 2000, 2011, 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     private SocketAdaptor(SocketChannelImpl sc) throws SocketException {
  61         super((SocketImpl) null);
  62         this.sc = sc;
  63     }
  64 
  65     public static Socket create(SocketChannelImpl sc) {
  66         try {
  67             return new SocketAdaptor(sc);
  68         } catch (SocketException e) {
  69             throw new InternalError("Should not reach here");
  70         }
  71     }
  72 
  73     public SocketChannel getChannel() {
  74         return sc;
  75     }
  76 
  77     // Override this method just to protect against changes in the superclass
  78     //
  79     public void connect(SocketAddress remote) throws IOException {
  80         connect(remote, 0);
  81     }
  82 
  83     public void connect(SocketAddress remote, int timeout) throws IOException {
  84         if (remote == null)
  85             throw new IllegalArgumentException("connect: The address can't be null");
  86         if (timeout < 0)
  87             throw new IllegalArgumentException("connect: timeout can't be negative");
  88 
  89         synchronized (sc.blockingLock()) {
  90             if (!sc.isBlocking())
  91                 throw new IllegalBlockingModeException();
  92 
  93             try {
  94 
  95                 if (timeout == 0) {
  96                     sc.connect(remote);
  97                     return;
  98                 }
  99 
 100                 // Implement timeout with a selector
 101                 SelectionKey sk = null;
 102                 Selector sel = null;
 103                 sc.configureBlocking(false);
 104                 try {
 105                     if (sc.connect(remote))
 106                         return;
 107                     sel = Util.getTemporarySelector(sc);
 108                     sk = sc.register(sel, SelectionKey.OP_CONNECT);
 109                     long to = timeout;
 110                     for (;;) {
 111                         if (!sc.isOpen())
 112                             throw new ClosedChannelException();
 113                         long st = System.currentTimeMillis();
 114                         int ns = sel.select(to);
 115                         if (ns > 0 &&
 116                             sk.isConnectable() && sc.finishConnect())
 117                             break;
 118                         sel.selectedKeys().remove(sk);
 119                         to -= System.currentTimeMillis() - st;
 120                         if (to <= 0) {
 121                             try {
 122                                 sc.close();
 123                             } catch (IOException x) { }
 124                             throw new SocketTimeoutException();
 125                         }
 126                     }
 127                 } finally {
 128                     if (sk != null)
 129                         sk.cancel();
 130                     if (sc.isOpen())
 131                         sc.configureBlocking(true);
 132                     if (sel != null)
 133                         Util.releaseTemporarySelector(sel);
 134                 }
 135 
 136             } catch (Exception x) {
 137                 Net.translateException(x, true);
 138             }
 139         }
 140 
 141     }
 142 
 143     public void bind(SocketAddress local) throws IOException {
 144         try {
 145             sc.bind(local);
 146         } catch (Exception x) {
 147             Net.translateException(x);
 148         }
 149     }
 150 
 151     public InetAddress getInetAddress() {
 152         SocketAddress remote = sc.remoteAddress();
 153         if (remote == null) {
 154             return null;
 155         } else {
 156             return ((InetSocketAddress)remote).getAddress();
 157         }
 158     }
 159 
 160     public InetAddress getLocalAddress() {
 161         if (sc.isOpen()) {
 162             SocketAddress local = sc.localAddress();
 163             if (local != null)
 164                 return ((InetSocketAddress)local).getAddress();
 165         }
 166         return new InetSocketAddress(0).getAddress();
 167     }
 168 
 169     public int getPort() {
 170         SocketAddress remote = sc.remoteAddress();
 171         if (remote == null) {
 172             return 0;
 173         } else {
 174             return ((InetSocketAddress)remote).getPort();
 175         }
 176     }
 177 
 178     public int getLocalPort() {
 179         SocketAddress local = sc.localAddress();
 180         if (local == null) {
 181             return -1;
 182         } else {
 183             return ((InetSocketAddress)local).getPort();
 184         }
 185     }
 186 
 187     private class SocketInputStream
 188         extends ChannelInputStream
 189     {
 190         private SocketInputStream() {
 191             super(sc);
 192         }
 193 
 194         protected int read(ByteBuffer bb)
 195             throws IOException
 196         {
 197             synchronized (sc.blockingLock()) {
 198                 if (!sc.isBlocking())
 199                     throw new IllegalBlockingModeException();
 200                 if (timeout == 0)
 201                     return sc.read(bb);
 202 
 203                 // Implement timeout with a selector
 204                 SelectionKey sk = null;
 205                 Selector sel = null;
 206                 sc.configureBlocking(false);
 207                 try {
 208                     int n;
 209                     if ((n = sc.read(bb)) != 0)
 210                         return n;
 211                     sel = Util.getTemporarySelector(sc);
 212                     sk = sc.register(sel, SelectionKey.OP_READ);
 213                     long to = timeout;
 214                     for (;;) {
 215                         if (!sc.isOpen())
 216                             throw new ClosedChannelException();
 217                         long st = System.currentTimeMillis();
 218                         int ns = sel.select(to);
 219                         if (ns > 0 && sk.isReadable()) {
 220                             if ((n = sc.read(bb)) != 0)
 221                                 return n;
 222                         }
 223                         sel.selectedKeys().remove(sk);
 224                         to -= System.currentTimeMillis() - st;
 225                         if (to <= 0)
 226                             throw new SocketTimeoutException();
 227                     }
 228                 } finally {
 229                     if (sk != null)
 230                         sk.cancel();
 231                     if (sc.isOpen())
 232                         sc.configureBlocking(true);
 233                     if (sel != null)
 234                         Util.releaseTemporarySelector(sel);
 235                 }
 236 
 237             }
 238         }
 239     }
 240 
 241     private InputStream socketInputStream = null;
 242 
 243     public InputStream getInputStream() throws IOException {
 244         if (!sc.isOpen())
 245             throw new SocketException("Socket is closed");
 246         if (!sc.isConnected())
 247             throw new SocketException("Socket is not connected");
 248         if (!sc.isInputOpen())
 249             throw new SocketException("Socket input is shutdown");
 250         if (socketInputStream == null) {
 251             try {
 252                 socketInputStream = AccessController.doPrivileged(
 253                     new PrivilegedExceptionAction<InputStream>() {
 254                         public InputStream run() throws IOException {
 255                             return new SocketInputStream();
 256                         }
 257                     });
 258             } catch (java.security.PrivilegedActionException e) {
 259                 throw (IOException)e.getException();
 260             }
 261         }
 262         return socketInputStream;
 263     }
 264 
 265     public OutputStream getOutputStream() throws IOException {
 266         if (!sc.isOpen())
 267             throw new SocketException("Socket is closed");
 268         if (!sc.isConnected())
 269             throw new SocketException("Socket is not connected");
 270         if (!sc.isOutputOpen())
 271             throw new SocketException("Socket output is shutdown");
 272         OutputStream os = null;
 273         try {
 274             os = AccessController.doPrivileged(
 275                 new PrivilegedExceptionAction<OutputStream>() {
 276                     public OutputStream run() throws IOException {
 277                         return Channels.newOutputStream(sc);
 278                     }
 279                 });
 280         } catch (java.security.PrivilegedActionException e) {
 281             throw (IOException)e.getException();
 282         }
 283         return os;
 284     }
 285 
 286     private void setBooleanOption(SocketOption<Boolean> name, boolean value)
 287         throws SocketException
 288     {
 289         try {
 290             sc.setOption(name, value);
 291         } catch (IOException x) {
 292             Net.translateToSocketException(x);
 293         }
 294     }
 295 
 296     private void setIntOption(SocketOption<Integer> name, int value)
 297         throws SocketException
 298     {
 299         try {
 300             sc.setOption(name, value);
 301         } catch (IOException x) {
 302             Net.translateToSocketException(x);
 303         }
 304     }
 305 
 306     private boolean getBooleanOption(SocketOption<Boolean> name) throws SocketException {
 307         try {
 308             return sc.getOption(name).booleanValue();
 309         } catch (IOException x) {
 310             Net.translateToSocketException(x);
 311             return false;       // keep compiler happy
 312         }
 313     }
 314 
 315     private int getIntOption(SocketOption<Integer> name) throws SocketException {
 316         try {
 317             return sc.getOption(name).intValue();
 318         } catch (IOException x) {
 319             Net.translateToSocketException(x);
 320             return -1;          // keep compiler happy
 321         }
 322     }
 323 
 324     public void setTcpNoDelay(boolean on) throws SocketException {
 325         setBooleanOption(StandardSocketOptions.TCP_NODELAY, on);
 326     }
 327 
 328     public boolean getTcpNoDelay() throws SocketException {
 329         return getBooleanOption(StandardSocketOptions.TCP_NODELAY);
 330     }
 331 
 332     public void setSoLinger(boolean on, int linger) throws SocketException {
 333         if (!on)
 334             linger = -1;
 335         setIntOption(StandardSocketOptions.SO_LINGER, linger);
 336     }
 337 
 338     public int getSoLinger() throws SocketException {
 339         return getIntOption(StandardSocketOptions.SO_LINGER);
 340     }
 341 
 342     public void sendUrgentData(int data) throws IOException {
 343         synchronized (sc.blockingLock()) {
 344             if (!sc.isBlocking())
 345                 throw new IllegalBlockingModeException();
 346             int n = sc.sendOutOfBandData((byte)data);
 347             assert n == 1;
 348         }
 349     }
 350 
 351     public void setOOBInline(boolean on) throws SocketException {
 352         setBooleanOption(ExtendedSocketOption.SO_OOBINLINE, on);
 353     }
 354 
 355     public boolean getOOBInline() throws SocketException {
 356         return getBooleanOption(ExtendedSocketOption.SO_OOBINLINE);
 357     }
 358 
 359     public void setSoTimeout(int timeout) throws SocketException {
 360         if (timeout < 0)
 361             throw new IllegalArgumentException("timeout can't be negative");
 362         this.timeout = timeout;
 363     }
 364 
 365     public int getSoTimeout() throws SocketException {
 366         return timeout;
 367     }
 368 
 369     public void setSendBufferSize(int size) throws SocketException {
 370         // size 0 valid for SocketChannel, invalid for Socket
 371         if (size <= 0)
 372             throw new IllegalArgumentException("Invalid send size");
 373         setIntOption(StandardSocketOptions.SO_SNDBUF, size);
 374     }
 375 
 376     public int getSendBufferSize() throws SocketException {
 377         return getIntOption(StandardSocketOptions.SO_SNDBUF);
 378     }
 379 
 380     public void setReceiveBufferSize(int size) throws SocketException {
 381         // size 0 valid for SocketChannel, invalid for Socket
 382         if (size <= 0)
 383             throw new IllegalArgumentException("Invalid receive size");
 384         setIntOption(StandardSocketOptions.SO_RCVBUF, size);
 385     }
 386 
 387     public int getReceiveBufferSize() throws SocketException {
 388         return getIntOption(StandardSocketOptions.SO_RCVBUF);
 389     }
 390 
 391     public void setKeepAlive(boolean on) throws SocketException {
 392         setBooleanOption(StandardSocketOptions.SO_KEEPALIVE, on);
 393     }
 394 
 395     public boolean getKeepAlive() throws SocketException {
 396         return getBooleanOption(StandardSocketOptions.SO_KEEPALIVE);
 397     }
 398 
 399     public void setTrafficClass(int tc) throws SocketException {
 400         setIntOption(StandardSocketOptions.IP_TOS, tc);
 401     }
 402 
 403     public int getTrafficClass() throws SocketException {
 404         return getIntOption(StandardSocketOptions.IP_TOS);
 405     }
 406 
 407     public void setReuseAddress(boolean on) throws SocketException {
 408         setBooleanOption(StandardSocketOptions.SO_REUSEADDR, on);
 409     }
 410 
 411     public boolean getReuseAddress() throws SocketException {
 412         return getBooleanOption(StandardSocketOptions.SO_REUSEADDR);
 413     }
 414 
 415     public void close() throws IOException {
 416         sc.close();
 417     }
 418 
 419     public void shutdownInput() throws IOException {
 420         try {
 421             sc.shutdownInput();
 422         } catch (Exception x) {
 423             Net.translateException(x);
 424         }
 425     }
 426 
 427     public void shutdownOutput() throws IOException {
 428         try {
 429             sc.shutdownOutput();
 430         } catch (Exception x) {
 431             Net.translateException(x);
 432         }
 433     }
 434 
 435     public String toString() {
 436         if (sc.isConnected())
 437             return "Socket[addr=" + getInetAddress() +
 438                 ",port=" + getPort() +
 439                 ",localport=" + getLocalPort() + "]";
 440         return "Socket[unconnected]";
 441     }
 442 
 443     public boolean isConnected() {
 444         return sc.isConnected();
 445     }
 446 
 447     public boolean isBound() {
 448         return sc.localAddress() != null;
 449     }
 450 
 451     public boolean isClosed() {
 452         return !sc.isOpen();
 453     }
 454 
 455     public boolean isInputShutdown() {
 456         return !sc.isInputOpen();
 457     }
 458 
 459     public boolean isOutputShutdown() {
 460         return !sc.isOutputOpen();
 461     }
 462 
 463 }