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 }