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