1 /* VMChannel.java -- Native interface suppling channel operations. 2 Copyright (C) 2006 Free Software Foundation, Inc. 3 4 This file is part of GNU Classpath. 5 6 GNU Classpath is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 GNU Classpath is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU Classpath; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301 USA. 20 21 Linking this library statically or dynamically with other modules is 22 making a combined work based on this library. Thus, the terms and 23 conditions of the GNU General Public License cover the whole 24 combination. 25 26 As a special exception, the copyright holders of this library give you 27 permission to link this library with independent modules to produce an 28 executable, regardless of the license terms of these independent 29 modules, and to copy and distribute the resulting executable under 30 terms of your choice, provided that you also meet, for each linked 31 independent module, the terms and conditions of the license of that 32 module. An independent module is a module which is not derived from 33 or based on this library. If you modify this library, you may extend 34 this exception to your version of the library, but you are not 35 obligated to do so. If you do not wish to do so, delete this 36 exception statement from your version. */ 37 38 39 package gnu.java.nio; 40 41 import gnu.classpath.Configuration; 42 43 import java.io.IOException; 44 import java.net.Inet4Address; 45 import java.net.Inet6Address; 46 import java.net.InetAddress; 47 import java.net.InetSocketAddress; 48 import java.net.SocketAddress; 49 import java.net.SocketException; 50 import java.nio.ByteBuffer; 51 import java.nio.MappedByteBuffer; 52 53 /** 54 * Native interface to support configuring of channel to run in a non-blocking 55 * manner and support scatter/gather io operations. 56 * 57 * @author Michael Barker <mike@middlesoft.co.uk> 58 * 59 */ 60 public final class VMChannel 61 { 62 /** 63 * Our reference implementation uses an integer to store the native 64 * file descriptor. Implementations without such support 65 */ 66 private final State nfd; 67 68 private Kind kind; 69 VMChannel()70 public VMChannel() 71 { 72 // XXX consider adding security check here, so only Classpath 73 // code may create instances. 74 this.nfd = new State(); 75 kind = Kind.OTHER; 76 } 77 78 /** 79 * This constructor is used by the POSIX reference implementation; 80 * other virtual machines need not support it. 81 * 82 * <strong>Important:</strong> do not call this in library code that is 83 * not specific to Classpath's reference implementation. 84 * 85 * @param native_fd The native file descriptor integer. 86 * @throws IOException 87 */ VMChannel(final int native_fd)88 VMChannel(final int native_fd) throws IOException 89 { 90 this(); 91 this.nfd.setNativeFD(native_fd); 92 } 93 getState()94 public State getState() 95 { 96 return nfd; 97 } 98 99 static 100 { 101 // load the shared library needed for native methods. 102 if (Configuration.INIT_LOAD_LIBRARY) 103 { 104 System.loadLibrary ("javanio"); 105 } initIDs()106 initIDs(); 107 } 108 getStdin()109 public static VMChannel getStdin() throws IOException 110 { 111 return new VMChannel(stdin_fd()); 112 } 113 getStdout()114 public static VMChannel getStdout() throws IOException 115 { 116 return new VMChannel(stdout_fd()); 117 } 118 getStderr()119 public static VMChannel getStderr() throws IOException 120 { 121 return new VMChannel(stderr_fd()); 122 } 123 stdin_fd()124 private static native int stdin_fd(); stdout_fd()125 private static native int stdout_fd(); stderr_fd()126 private static native int stderr_fd(); 127 128 /** 129 * Set the file descriptor to have the required blocking 130 * setting. 131 * 132 * @param blocking The blocking flag to set. 133 */ setBlocking(boolean blocking)134 public void setBlocking(boolean blocking) throws IOException 135 { 136 setBlocking(nfd.getNativeFD(), blocking); 137 } 138 setBlocking(int fd, boolean blocking)139 private static native void setBlocking(int fd, boolean blocking) 140 throws IOException; 141 available()142 public int available() throws IOException 143 { 144 return available(nfd.getNativeFD()); 145 } 146 available(int native_fd)147 private static native int available(int native_fd) throws IOException; 148 149 /** 150 * Reads a byte buffer directly using the supplied file descriptor. 151 * 152 * @param dst Direct Byte Buffer to read to. 153 * @return Number of bytes read. 154 * @throws IOException If an error occurs or dst is not a direct buffers. 155 */ read(ByteBuffer dst)156 public int read(ByteBuffer dst) 157 throws IOException 158 { 159 return read(nfd.getNativeFD(), dst); 160 } 161 read(int fd, ByteBuffer dst)162 private static native int read(int fd, ByteBuffer dst) throws IOException; 163 164 /** 165 * Read a single byte. 166 * 167 * @return The byte read, or -1 on end of file. 168 * @throws IOException 169 */ read()170 public int read() throws IOException 171 { 172 return read(nfd.getNativeFD()); 173 } 174 read(int fd)175 private static native int read(int fd) throws IOException; 176 177 /** 178 * Reads into byte buffers directly using the supplied file descriptor. 179 * Assumes that the buffer list contains DirectBuffers. Will perform a 180 * scattering read. 181 * 182 * @param dsts An array direct byte buffers. 183 * @param offset Index of the first buffer to read to. 184 * @param length The number of buffers to read to. 185 * @return Number of bytes read. 186 * @throws IOException If an error occurs or the dsts are not direct buffers. 187 */ readScattering(ByteBuffer[] dsts, int offset, int length)188 public long readScattering(ByteBuffer[] dsts, int offset, int length) 189 throws IOException 190 { 191 if (offset + length > dsts.length) 192 throw new IndexOutOfBoundsException("offset + length > dsts.length"); 193 194 return readScattering(nfd.getNativeFD(), dsts, offset, length); 195 } 196 readScattering(int fd, ByteBuffer[] dsts, int offset, int length)197 private static native long readScattering(int fd, ByteBuffer[] dsts, 198 int offset, int length) 199 throws IOException; 200 201 /** 202 * Receive a datagram on this channel, returning the host address 203 * that sent the datagram. 204 * 205 * @param dst Where to store the datagram. 206 * @return The host address that sent the datagram. 207 * @throws IOException 208 */ receive(ByteBuffer dst)209 public SocketAddress receive(ByteBuffer dst) throws IOException 210 { 211 if (kind != Kind.SOCK_DGRAM) 212 throw new SocketException("not a datagram socket"); 213 ByteBuffer hostPort = ByteBuffer.allocateDirect(18); 214 int hostlen = receive(nfd.getNativeFD(), dst, hostPort); 215 if (hostlen == 0) 216 return null; 217 if (hostlen == 4) // IPv4 218 { 219 byte[] addr = new byte[4]; 220 hostPort.get(addr); 221 int port = hostPort.getShort() & 0xFFFF; 222 return new InetSocketAddress(Inet4Address.getByAddress(addr), port); 223 } 224 if (hostlen == 16) // IPv6 225 { 226 byte[] addr = new byte[16]; 227 hostPort.get(addr); 228 int port = hostPort.getShort() & 0xFFFF; 229 return new InetSocketAddress(Inet6Address.getByAddress(addr), port); 230 } 231 232 throw new SocketException("host address received with invalid length: " 233 + hostlen); 234 } 235 receive(int fd, ByteBuffer dst, ByteBuffer address)236 private static native int receive (int fd, ByteBuffer dst, ByteBuffer address) 237 throws IOException; 238 239 /** 240 * Writes from a direct byte bufer using the supplied file descriptor. 241 * Assumes the buffer is a DirectBuffer. 242 * 243 * @param src The source buffer. 244 * @return Number of bytes written. 245 * @throws IOException 246 */ write(ByteBuffer src)247 public int write(ByteBuffer src) throws IOException 248 { 249 return write(nfd.getNativeFD(), src); 250 } 251 write(int fd, ByteBuffer src)252 private native int write(int fd, ByteBuffer src) throws IOException; 253 254 /** 255 * Writes from byte buffers directly using the supplied file descriptor. 256 * Assumes the that buffer list constains DirectBuffers. Will perform 257 * as gathering write. 258 * 259 * @param srcs 260 * @param offset 261 * @param length 262 * @return Number of bytes written. 263 * @throws IOException 264 */ writeGathering(ByteBuffer[] srcs, int offset, int length)265 public long writeGathering(ByteBuffer[] srcs, int offset, int length) 266 throws IOException 267 { 268 if (offset + length > srcs.length) 269 throw new IndexOutOfBoundsException("offset + length > srcs.length"); 270 271 // A gathering write is limited to 16 buffers; when writing, ensure 272 // that we have at least one buffer with something in it in the 16 273 // buffer window starting at offset. 274 while (!srcs[offset].hasRemaining() && offset < srcs.length) 275 offset++; 276 277 // There are no buffers with anything to write. 278 if (offset == srcs.length) 279 return 0; 280 281 // If we advanced `offset' so far that we don't have `length' 282 // buffers left, reset length to only the remaining buffers. 283 if (length > srcs.length - offset) 284 length = srcs.length - offset; 285 286 return writeGathering(nfd.getNativeFD(), srcs, offset, length); 287 } 288 writeGathering(int fd, ByteBuffer[] srcs, int offset, int length)289 private native long writeGathering(int fd, ByteBuffer[] srcs, 290 int offset, int length) 291 throws IOException; 292 293 /** 294 * Send a datagram to the given address. 295 * 296 * @param src The source buffer. 297 * @param dst The destination address. 298 * @return The number of bytes written. 299 * @throws IOException 300 */ send(ByteBuffer src, InetSocketAddress dst)301 public int send(ByteBuffer src, InetSocketAddress dst) 302 throws IOException 303 { 304 InetAddress addr = dst.getAddress(); 305 if (addr == null) 306 throw new NullPointerException(); 307 if (addr instanceof Inet4Address) 308 return send(nfd.getNativeFD(), src, addr.getAddress(), dst.getPort()); 309 else if (addr instanceof Inet6Address) 310 return send6(nfd.getNativeFD(), src, addr.getAddress(), dst.getPort()); 311 else 312 throw new SocketException("unrecognized inet address type"); 313 } 314 315 // Send to an IPv4 address. send(int fd, ByteBuffer src, byte[] addr, int port)316 private static native int send(int fd, ByteBuffer src, byte[] addr, int port) 317 throws IOException; 318 319 // Send to an IPv6 address. send6(int fd, ByteBuffer src, byte[] addr, int port)320 private static native int send6(int fd, ByteBuffer src, byte[] addr, int port) 321 throws IOException; 322 323 /** 324 * Write a single byte. 325 * 326 * @param b The byte to write. 327 * @throws IOException 328 */ write(int b)329 public void write(int b) throws IOException 330 { 331 write(nfd.getNativeFD(), b); 332 } 333 write(int fd, int b)334 private static native void write(int fd, int b) throws IOException; 335 initIDs()336 private native static void initIDs(); 337 338 // Network (socket) specific methods. 339 340 /** 341 * Create a new socket. This method will initialize the native file 342 * descriptor state of this instance. 343 * 344 * @param stream Whether or not to create a streaming socket, or a datagram 345 * socket. 346 * @throws IOException If creating a new socket fails, or if this 347 * channel already has its native descriptor initialized. 348 */ initSocket(boolean stream)349 public void initSocket(boolean stream) throws IOException 350 { 351 if (nfd.isValid()) 352 throw new IOException("native FD already initialized"); 353 if (stream) 354 kind = Kind.SOCK_STREAM; 355 else 356 kind = Kind.SOCK_DGRAM; 357 nfd.setNativeFD(socket(stream)); 358 } 359 360 /** 361 * Create a new socket, returning the native file descriptor. 362 * 363 * @param stream Set to true for streaming sockets; false for datagrams. 364 * @return The native file descriptor. 365 * @throws IOException If creating the socket fails. 366 */ socket(boolean stream)367 private static native int socket(boolean stream) throws IOException; 368 369 /** 370 * Connect the underlying socket file descriptor to the remote host. 371 * 372 * @param saddr The address to connect to. 373 * @param timeout The connect timeout to use for blocking connects. 374 * @return True if the connection succeeded; false if the file descriptor 375 * is in non-blocking mode and the connection did not immediately 376 * succeed. 377 * @throws IOException If an error occurs while connecting. 378 */ connect(InetSocketAddress saddr, int timeout)379 public boolean connect(InetSocketAddress saddr, int timeout) 380 throws SocketException 381 { 382 int fd; 383 384 InetAddress addr = saddr.getAddress(); 385 386 // Translates an IOException into a SocketException to conform 387 // to the throws clause. 388 try 389 { 390 fd = nfd.getNativeFD(); 391 } 392 catch (IOException ioe) 393 { 394 throw new SocketException(ioe.getMessage()); 395 } 396 397 if (addr instanceof Inet4Address) 398 return connect(fd, addr.getAddress(), saddr.getPort(), 399 timeout); 400 if (addr instanceof Inet6Address) 401 return connect6(fd, addr.getAddress(), saddr.getPort(), 402 timeout); 403 throw new SocketException("unsupported internet address"); 404 } 405 connect(int fd, byte[] addr, int port, int timeout)406 private static native boolean connect(int fd, byte[] addr, int port, int timeout) 407 throws SocketException; 408 connect6(int fd, byte[] addr, int port, int timeout)409 private static native boolean connect6(int fd, byte[] addr, int port, int timeout) 410 throws SocketException; 411 412 /** 413 * Disconnect this channel, if it is a datagram socket. Disconnecting 414 * a datagram channel will disassociate it from any address, so the 415 * socket will remain open, but can send and receive datagrams from 416 * any address. 417 * 418 * @throws IOException If disconnecting this channel fails, or if this 419 * channel is not a datagram channel. 420 */ disconnect()421 public void disconnect() throws IOException 422 { 423 if (kind != Kind.SOCK_DGRAM) 424 throw new IOException("can only disconnect datagram channels"); 425 disconnect(nfd.getNativeFD()); 426 } 427 disconnect(int fd)428 private static native void disconnect(int fd) throws IOException; 429 getLocalAddress()430 public InetSocketAddress getLocalAddress() throws IOException 431 { 432 if (!nfd.isValid()) 433 return null; 434 ByteBuffer name = ByteBuffer.allocateDirect(18); 435 int namelen = getsockname(nfd.getNativeFD(), name); 436 if (namelen == 0) // not bound 437 return null; // XXX return some wildcard? 438 if (namelen == 4) 439 { 440 byte[] addr = new byte[4]; 441 name.get(addr); 442 int port = name.getShort() & 0xFFFF; 443 return new InetSocketAddress(Inet4Address.getByAddress(addr), port); 444 } 445 if (namelen == 16) 446 { 447 byte[] addr = new byte[16]; 448 name.get(addr); 449 int port = name.getShort() & 0xFFFF; 450 return new InetSocketAddress(Inet6Address.getByAddress(addr), port); 451 } 452 throw new SocketException("invalid address length"); 453 } 454 getsockname(int fd, ByteBuffer name)455 private static native int getsockname(int fd, ByteBuffer name) 456 throws IOException; 457 458 /** 459 * Returns the socket address of the remote peer this channel is connected 460 * to, or null if this channel is not yet connected. 461 * 462 * @return The peer address. 463 * @throws IOException 464 */ getPeerAddress()465 public InetSocketAddress getPeerAddress() throws IOException 466 { 467 if (!nfd.isValid()) 468 return null; 469 ByteBuffer name = ByteBuffer.allocateDirect(18); 470 int namelen = getpeername (nfd.getNativeFD(), name); 471 if (namelen == 0) // not connected yet 472 return null; 473 if (namelen == 4) // IPv4 474 { 475 byte[] addr = new byte[4]; 476 name.get(addr); 477 int port = name.getShort() & 0xFFFF; 478 return new InetSocketAddress(Inet4Address.getByAddress(addr), port); 479 } 480 else if (namelen == 16) // IPv6 481 { 482 byte[] addr = new byte[16]; 483 name.get(addr); 484 int port = name.getShort() & 0xFFFF; 485 return new InetSocketAddress(Inet6Address.getByAddress(addr), port); 486 } 487 throw new SocketException("invalid address length"); 488 } 489 490 /* 491 * The format here is the peer address, followed by the port number. 492 * The returned value is the length of the peer address; thus, there 493 * will be LEN + 2 valid bytes put into NAME. 494 */ getpeername(int fd, ByteBuffer name)495 private static native int getpeername(int fd, ByteBuffer name) 496 throws IOException; 497 498 /** 499 * Accept an incoming connection, returning a new VMChannel, or null 500 * if the channel is nonblocking and no connection is pending. 501 * 502 * @return The accepted connection, or null. 503 * @throws IOException If an IO error occurs. 504 */ accept()505 public VMChannel accept() throws IOException 506 { 507 int new_fd = accept(nfd.getNativeFD()); 508 if (new_fd == -1) // non-blocking accept had no pending connection 509 return null; 510 return new VMChannel(new_fd); 511 } 512 accept(int native_fd)513 private static native int accept(int native_fd) throws IOException; 514 515 // File-specific methods. 516 517 /** 518 * Open a file at PATH, initializing the native state to operate on 519 * that open file. 520 * 521 * @param path The absolute file path. 522 * @throws IOException If the file cannot be opened, or if this 523 * channel was previously initialized. 524 */ openFile(String path, int mode)525 public void openFile(String path, int mode) throws IOException 526 { 527 if (nfd.isValid() || nfd.isClosed()) 528 throw new IOException("can't reinitialize this channel"); 529 int fd = open(path, mode); 530 nfd.setNativeFD(fd); 531 kind = Kind.FILE; 532 } 533 open(String path, int mode)534 private static native int open(String path, int mode) throws IOException; 535 position()536 public long position() throws IOException 537 { 538 if (kind != Kind.FILE) 539 throw new IOException("not a file"); 540 return position(nfd.getNativeFD()); 541 } 542 position(int fd)543 private static native long position(int fd) throws IOException; 544 seek(long pos)545 public void seek(long pos) throws IOException 546 { 547 if (kind != Kind.FILE) 548 throw new IOException("not a file"); 549 seek(nfd.getNativeFD(), pos); 550 } 551 seek(int fd, long pos)552 private static native void seek(int fd, long pos) throws IOException; 553 truncate(long length)554 public void truncate(long length) throws IOException 555 { 556 if (kind != Kind.FILE) 557 throw new IOException("not a file"); 558 truncate(nfd.getNativeFD(), length); 559 } 560 truncate(int fd, long len)561 private static native void truncate(int fd, long len) throws IOException; 562 lock(long pos, long len, boolean shared, boolean wait)563 public boolean lock(long pos, long len, boolean shared, boolean wait) 564 throws IOException 565 { 566 if (kind != Kind.FILE) 567 throw new IOException("not a file"); 568 return lock(nfd.getNativeFD(), pos, len, shared, wait); 569 } 570 lock(int fd, long pos, long len, boolean shared, boolean wait)571 private static native boolean lock(int fd, long pos, long len, 572 boolean shared, boolean wait) 573 throws IOException; 574 unlock(long pos, long len)575 public void unlock(long pos, long len) throws IOException 576 { 577 if (kind != Kind.FILE) 578 throw new IOException("not a file"); 579 unlock(nfd.getNativeFD(), pos, len); 580 } 581 unlock(int fd, long pos, long len)582 private static native void unlock(int fd, long pos, long len) throws IOException; 583 size()584 public long size() throws IOException 585 { 586 if (kind != Kind.FILE) 587 throw new IOException("not a file"); 588 return size(nfd.getNativeFD()); 589 } 590 size(int fd)591 private static native long size(int fd) throws IOException; 592 map(char mode, long position, int size)593 public MappedByteBuffer map(char mode, long position, int size) 594 throws IOException 595 { 596 if (kind != Kind.FILE) 597 throw new IOException("not a file"); 598 return map(nfd.getNativeFD(), mode, position, size); 599 } 600 map(int fd, char mode, long position, int size)601 private static native MappedByteBuffer map(int fd, char mode, 602 long position, int size) 603 throws IOException; 604 flush(boolean metadata)605 public boolean flush(boolean metadata) throws IOException 606 { 607 if (kind != Kind.FILE) 608 throw new IOException("not a file"); 609 return flush(nfd.getNativeFD(), metadata); 610 } 611 flush(int fd, boolean metadata)612 private static native boolean flush(int fd, boolean metadata) throws IOException; 613 614 // Close. 615 616 /** 617 * Close this socket. The socket is also automatically closed when this 618 * object is finalized. 619 * 620 * @throws IOException If closing the socket fails, or if this object has 621 * no open socket. 622 */ close()623 public void close() throws IOException 624 { 625 nfd.close(); 626 } 627 close(int native_fd)628 static native void close(int native_fd) throws IOException; 629 630 /** 631 * <p>Provides a simple mean for the JNI code to find out whether the 632 * current thread was interrupted by a call to Thread.interrupt().</p> 633 * 634 * @return 635 */ isThreadInterrupted()636 static boolean isThreadInterrupted() 637 { 638 return Thread.currentThread().isInterrupted(); 639 } 640 641 // Inner classes. 642 643 /** 644 * A wrapper for a native file descriptor integer. This tracks the state 645 * of an open file descriptor, and ensures that 646 * 647 * This class need not be fully supported by virtual machines; if a 648 * virtual machine does not use integer file descriptors, or does and 649 * wishes to hide that, then the methods of this class may be stubbed out. 650 * 651 * System-specific classes that depend on access to native file descriptor 652 * integers SHOULD declare this fact. 653 */ 654 public final class State 655 { 656 private int native_fd; 657 private boolean valid; 658 private boolean closed; 659 State()660 State() 661 { 662 native_fd = -1; 663 valid = false; 664 closed = false; 665 } 666 isValid()667 public boolean isValid() 668 { 669 return valid; 670 } 671 isClosed()672 public boolean isClosed() 673 { 674 return closed; 675 } 676 getNativeFD()677 public int getNativeFD() throws IOException 678 { 679 if (!valid) 680 throw new IOException("invalid file descriptor"); 681 return native_fd; 682 } 683 setNativeFD(final int native_fd)684 void setNativeFD(final int native_fd) throws IOException 685 { 686 if (valid) 687 throw new IOException("file descriptor already initialized"); 688 this.native_fd = native_fd; 689 valid = true; 690 } 691 close()692 public void close() throws IOException 693 { 694 if (!valid) 695 throw new IOException("invalid file descriptor"); 696 try 697 { 698 VMChannel.close(native_fd); 699 } 700 finally 701 { 702 valid = false; 703 closed = true; 704 } 705 } 706 toString()707 public String toString() 708 { 709 if (closed) 710 return "<<closed>>"; 711 if (!valid) 712 return "<<invalid>>"; 713 return String.valueOf(native_fd); 714 } 715 finalize()716 protected void finalize() throws Throwable 717 { 718 try 719 { 720 if (valid) 721 close(); 722 } 723 finally 724 { 725 super.finalize(); 726 } 727 } 728 } 729 730 /** 731 * An enumeration of possible kinds of channel. 732 */ 733 static class Kind // XXX enum 734 { 735 /** A streaming (TCP) socket. */ 736 static final Kind SOCK_STREAM = new Kind(); 737 738 /** A datagram (UDP) socket. */ 739 static final Kind SOCK_DGRAM = new Kind(); 740 741 /** A file. */ 742 static final Kind FILE = new Kind(); 743 744 /** Something else; not a socket or file. */ 745 static final Kind OTHER = new Kind(); 746 Kind()747 private Kind() { } 748 } 749 } 750