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