# HG changeset patch # User alanb # Date 1220204341 -3600 # Node ID 29d6145d1097e8112aac382704f3b0b38fb634f8 # Parent 4070cecdb99d3e41aac9e1c79d30fc64a75b1e0d 4640544: New I/O: Complete socket-channel functionality Reviewed-by: iris, sherman, chegar diff -r 4070cecdb99d -r 29d6145d1097 jdk/make/java/nio/FILES_java.gmk --- a/jdk/make/java/nio/FILES_java.gmk Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/make/java/nio/FILES_java.gmk Sun Aug 31 18:39:01 2008 +0100 @@ -39,6 +39,9 @@ java/nio/channels/FileLock.java \ java/nio/channels/GatheringByteChannel.java \ java/nio/channels/InterruptibleChannel.java \ + java/nio/channels/MembershipKey.java \ + java/nio/channels/MulticastChannel.java \ + java/nio/channels/NetworkChannel.java \ java/nio/channels/ReadableByteChannel.java \ java/nio/channels/ScatteringByteChannel.java \ java/nio/channels/SelectableChannel.java \ @@ -73,6 +76,7 @@ sun/nio/ch/DatagramSocketAdaptor.java \ sun/nio/ch/DefaultSelectorProvider.java \ sun/nio/ch/DirectBuffer.java \ + sun/nio/ch/ExtendedSocketOption.java \ sun/nio/ch/FileChannelImpl.java \ sun/nio/ch/FileDispatcher.java \ sun/nio/ch/FileKey.java \ @@ -80,12 +84,14 @@ sun/nio/ch/IOUtil.java \ sun/nio/ch/IOStatus.java \ sun/nio/ch/IOVecWrapper.java \ + sun/nio/ch/MembershipKeyImpl.java \ + sun/nio/ch/MembershipRegistry.java \ sun/nio/ch/NativeDispatcher.java \ sun/nio/ch/NativeObject.java \ sun/nio/ch/NativeThread.java \ sun/nio/ch/NativeThreadSet.java \ sun/nio/ch/Net.java \ - sun/nio/ch/OptionAdaptor.java \ + sun/nio/ch/OptionKey.java \ sun/nio/ch/PipeImpl.java \ sun/nio/ch/PollArrayWrapper.java \ sun/nio/ch/Reflect.java \ @@ -99,8 +105,7 @@ sun/nio/ch/SocketAdaptor.java \ sun/nio/ch/SocketChannelImpl.java \ sun/nio/ch/SocketDispatcher.java \ - sun/nio/ch/SocketOpts.java \ - sun/nio/ch/SocketOptsImpl.java \ + sun/nio/ch/SocketOptionRegistry.java \ sun/nio/ch/SourceChannelImpl.java \ sun/nio/ch/Util.java \ \ @@ -240,6 +245,7 @@ java/nio/InvalidMarkException.java \ java/nio/ReadOnlyBufferException.java \ \ + java/nio/channels/AlreadyBoundException.java \ java/nio/channels/AlreadyConnectedException.java \ java/nio/channels/AsynchronousCloseException.java \ java/nio/channels/ClosedByInterruptException.java \ @@ -258,14 +264,15 @@ java/nio/channels/UnresolvedAddressException.java \ java/nio/channels/UnsupportedAddressTypeException.java \ \ - sun/nio/ch/AlreadyBoundException.java \ - \ java/nio/charset/CharacterCodingException.java \ java/nio/charset/IllegalCharsetNameException.java \ java/nio/charset/UnsupportedCharsetException.java FILES_gen_csp = sun/nio/cs/StandardCharsets.java -FILES_gen = $(FILES_gen_coder) $(FILES_gen_buffer) $(FILES_gen_ex) $(FILES_gen_csp) +FILES_gen_sor = sun/nio/ch/SocketOptionRegistry.java + +FILES_gen = $(FILES_gen_coder) $(FILES_gen_buffer) $(FILES_gen_ex) \ + $(FILES_gen_csp) $(FILES_gen_sor) FILES_java = $(FILES_src) $(FILES_gen) diff -r 4070cecdb99d -r 29d6145d1097 jdk/make/java/nio/Makefile --- a/jdk/make/java/nio/Makefile Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/make/java/nio/Makefile Sun Aug 31 18:39:01 2008 +0100 @@ -56,18 +56,18 @@ sun/nio/ch/DevPollSelectorProvider.java \ sun/nio/ch/InheritedChannel.java \ sun/nio/ch/PollSelectorProvider.java \ - sun/nio/ch/PollSelectorImpl.java + sun/nio/ch/PollSelectorImpl.java FILES_c += \ DevPollArrayWrapper.c \ InheritedChannel.c \ - PollArrayWrapper.c \ - NativeThread.c + NativeThread.c \ + PollArrayWrapper.c FILES_export += \ sun/nio/ch/DevPollArrayWrapper.java \ sun/nio/ch/InheritedChannel.java \ - sun/nio/ch/NativeThread.java + sun/nio/ch/NativeThread.java endif # PLATFORM = solaris ifeq ($(PLATFORM), windows) @@ -94,14 +94,14 @@ FILES_c += \ EPollArrayWrapper.c \ - PollArrayWrapper.c \ InheritedChannel.c \ - NativeThread.c + NativeThread.c \ + PollArrayWrapper.c FILES_export += \ sun/nio/ch/EPollArrayWrapper.java \ sun/nio/ch/InheritedChannel.java \ - sun/nio/ch/NativeThread.java + sun/nio/ch/NativeThread.java endif # PLATFORM = linux # Find platform-specific C source files @@ -618,12 +618,6 @@ @$(RM) $@.temp $(GEN_EX_CMD) $(BUF_SRC)/exceptions $(BUF_GEN) -$(SCH_GEN)/%Exception.java: genExceptions.sh $(SCH_SRC)/exceptions - $(prep-target) - @$(RM) $@.temp - $(GEN_EX_CMD) $(SCH_SRC)/exceptions $(SCH_GEN) - - # # Generated charset-provider classes # @@ -638,4 +632,29 @@ HASHER="$(BOOT_JAVA_CMD) -jar $(HASHER_JARFILE)" \ $(SH) -e genCharsetProvider.sh $(SCS_SRC)/standard-charsets $(SCS_GEN) +# +# Generated channel implementation classes. +# C source is compiled in TEMPDIR to avoid turds left by Windows compilers. +# + +GENSOR_SRC = $(SHARE_SRC)/native/sun/nio/ch/genSocketOptionRegistry.c + +GENSOR_EXE = $(TEMPDIR)/genSocketOptionRegistry$(EXE_SUFFIX) + +SOR_COPYRIGHT_YEARS = $(shell $(CAT) $(GENSOR_SRC) | \ + $(NAWK) '/^.*Copyright.*Sun/ { print $$3 }') + +$(TEMPDIR)/$(GENSOR_SRC) : $(GENSOR_SRC) + $(install-file) + +$(GENSOR_EXE) : $(TEMPDIR)/$(GENSOR_SRC) + $(prep-target) + ($(CD) $(TEMPDIR); $(CC) $(CPPFLAGS) $(LDDFLAGS) \ + -o genSocketOptionRegistry$(EXE_SUFFIX) $(GENSOR_SRC)) + +$(SCH_GEN)/SocketOptionRegistry.java: $(GENSOR_EXE) + $(prep-target) + NAWK="$(NAWK)" SH="$(SH)" $(SH) -e addNotices.sh $(SOR_COPYRIGHT_YEARS) > $@ + $(GENSOR_EXE) >> $@ + .PHONY: sources diff -r 4070cecdb99d -r 29d6145d1097 jdk/make/java/nio/mapfile-linux --- a/jdk/make/java/nio/mapfile-linux Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/make/java/nio/mapfile-linux Sun Aug 31 18:39:01 2008 +0100 @@ -1,3 +1,27 @@ +# +# Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Sun designates this +# particular file as subject to the "Classpath" exception as provided +# by Sun in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# SUNWprivate_1.1 { global: @@ -61,20 +85,29 @@ Java_sun_nio_ch_NativeThread_init; Java_sun_nio_ch_NativeThread_signal; Java_sun_nio_ch_Net_socket0; - Java_sun_nio_ch_Net_bind; - Java_sun_nio_ch_Net_connect; + Java_sun_nio_ch_Net_bind0; + Java_sun_nio_ch_Net_connect0; + Java_sun_nio_ch_Net_listen; Java_sun_nio_ch_Net_localPort; Java_sun_nio_ch_Net_localInetAddress; Java_sun_nio_ch_Net_getIntOption0; Java_sun_nio_ch_Net_setIntOption0; Java_sun_nio_ch_Net_initIDs; + Java_sun_nio_ch_Net_isIPv6Available0; + Java_sun_nio_ch_Net_joinOrDrop4; + Java_sun_nio_ch_Net_blockOrUnblock4; + Java_sun_nio_ch_Net_joinOrDrop6; + Java_sun_nio_ch_Net_blockOrUnblock6; + Java_sun_nio_ch_Net_setInterface4; + Java_sun_nio_ch_Net_getInterface4; + Java_sun_nio_ch_Net_setInterface6; + Java_sun_nio_ch_Net_getInterface6; + Java_sun_nio_ch_Net_shutdown; Java_sun_nio_ch_PollArrayWrapper_interrupt; Java_sun_nio_ch_PollArrayWrapper_poll0; Java_sun_nio_ch_ServerSocketChannelImpl_accept0; Java_sun_nio_ch_ServerSocketChannelImpl_initIDs; - Java_sun_nio_ch_ServerSocketChannelImpl_listen; Java_sun_nio_ch_SocketChannelImpl_checkConnect; - Java_sun_nio_ch_SocketChannelImpl_shutdown; local: *; diff -r 4070cecdb99d -r 29d6145d1097 jdk/make/java/nio/mapfile-solaris --- a/jdk/make/java/nio/mapfile-solaris Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/make/java/nio/mapfile-solaris Sun Aug 31 18:39:01 2008 +0100 @@ -1,3 +1,27 @@ +# +# Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Sun designates this +# particular file as subject to the "Classpath" exception as provided +# by Sun in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# SUNWprivate_1.1 { global: @@ -59,20 +83,29 @@ Java_sun_nio_ch_NativeThread_init; Java_sun_nio_ch_NativeThread_signal; Java_sun_nio_ch_Net_socket0; - Java_sun_nio_ch_Net_bind; - Java_sun_nio_ch_Net_connect; + Java_sun_nio_ch_Net_bind0; + Java_sun_nio_ch_Net_connect0; + Java_sun_nio_ch_Net_listen; Java_sun_nio_ch_Net_localPort; Java_sun_nio_ch_Net_localInetAddress; Java_sun_nio_ch_Net_getIntOption0; Java_sun_nio_ch_Net_setIntOption0; Java_sun_nio_ch_Net_initIDs; + Java_sun_nio_ch_Net_isIPv6Available0; + Java_sun_nio_ch_Net_joinOrDrop4; + Java_sun_nio_ch_Net_blockOrUnblock4; + Java_sun_nio_ch_Net_joinOrDrop6; + Java_sun_nio_ch_Net_blockOrUnblock6; + Java_sun_nio_ch_Net_setInterface4; + Java_sun_nio_ch_Net_getInterface4; + Java_sun_nio_ch_Net_setInterface6; + Java_sun_nio_ch_Net_getInterface6; + Java_sun_nio_ch_Net_shutdown; Java_sun_nio_ch_PollArrayWrapper_interrupt; Java_sun_nio_ch_PollArrayWrapper_poll0; Java_sun_nio_ch_ServerSocketChannelImpl_accept0; Java_sun_nio_ch_ServerSocketChannelImpl_initIDs; - Java_sun_nio_ch_ServerSocketChannelImpl_listen; Java_sun_nio_ch_SocketChannelImpl_checkConnect; - Java_sun_nio_ch_SocketChannelImpl_shutdown; local: *; diff -r 4070cecdb99d -r 29d6145d1097 jdk/make/mksample/nio/Makefile --- a/jdk/make/mksample/nio/Makefile Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/make/mksample/nio/Makefile Sun Aug 31 18:39:01 2008 +0100 @@ -31,7 +31,7 @@ PRODUCT = java include $(BUILDDIR)/common/Defs.gmk -SUBDIRS = server +SUBDIRS = multicast server all build clean clobber:: $(SUBDIRS-loop) diff -r 4070cecdb99d -r 29d6145d1097 jdk/make/mksample/nio/multicast/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/make/mksample/nio/multicast/Makefile Sun Aug 31 18:39:01 2008 +0100 @@ -0,0 +1,52 @@ +# +# Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Sun designates this +# particular file as subject to the "Classpath" exception as provided +# by Sun in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# + +# +# Makefile for the nio/multicast sample code +# + +BUILDDIR = ../../.. + +PRODUCT = java + +include $(BUILDDIR)/common/Defs.gmk + +SAMPLE_SRC_DIR = $(SHARE_SRC)/sample/nio/multicast +SAMPLE_DST_DIR = $(SAMPLEDIR)/nio/multicast + +SAMPLE_FILES = \ + $(SAMPLE_DST_DIR)/Reader.java \ + $(SAMPLE_DST_DIR)/Sender.java \ + $(SAMPLE_DST_DIR)/MulticastAddress.java + +all build: $(SAMPLE_FILES) + +$(SAMPLE_DST_DIR)/%: $(SAMPLE_SRC_DIR)/% + $(install-file) + +clean clobber: + $(RM) -r $(SAMPLE_DST_DIR) + +.PHONY: all build clean clobber diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/java/net/NetworkInterface.java --- a/jdk/src/share/classes/java/net/NetworkInterface.java Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/share/classes/java/net/NetworkInterface.java Sun Aug 31 18:39:01 2008 +0100 @@ -25,7 +25,6 @@ package java.net; -import java.net.SocketException; import java.util.Enumeration; import java.util.NoSuchElementException; import sun.security.action.*; diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/java/net/ProtocolFamily.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/java/net/ProtocolFamily.java Sun Aug 31 18:39:01 2008 +0100 @@ -0,0 +1,39 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.net; + +/** + * Represents a family of communication protocols. + * + * @since 1.7 + */ + +public interface ProtocolFamily { + /** + * Returns the name of the protocol family. + */ + String name(); +} diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/java/net/SocketOption.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/java/net/SocketOption.java Sun Aug 31 18:39:01 2008 +0100 @@ -0,0 +1,55 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.net; + +/** + * A socket option associated with a socket. + * + *

In the {@link java.nio.channels channels} package, the {@link + * java.nio.channels.NetworkChannel} interface defines the {@link + * java.nio.channels.NetworkChannel#setOption(SocketOption,Object) setOption} + * and {@link java.nio.channels.NetworkChannel#getOption(SocketOption) getOption} + * methods to set and query the channel's socket options. + * + * @param The type of the socket option value. + * + * @since 1.7 + * + * @see StandardSocketOption + */ + +public interface SocketOption { + + /** + * Returns the name of the socket option. + */ + String name(); + + /** + * Returns the type of the socket option value. + */ + Class type(); +} diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/java/net/StandardProtocolFamily.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/java/net/StandardProtocolFamily.java Sun Aug 31 18:39:01 2008 +0100 @@ -0,0 +1,45 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.net; + +/** + * Defines the standard family of communication protocols. + * + * @since 1.7 + */ + +public enum StandardProtocolFamily implements ProtocolFamily { + + /** + * Internet Protocol Version 4 (IPv4) + */ + INET, + + /** + * Internet Protocol Version 6 (IPv6) + */ + INET6 +} diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/java/net/StandardSocketOption.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/java/net/StandardSocketOption.java Sun Aug 31 18:39:01 2008 +0100 @@ -0,0 +1,352 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.net; + +/** + * Defines the standard socket options. + * + *

The {@link SocketOption#name name} of each socket option defined by this + * class is its field name. + * + *

In this release, the socket options defined here are used by {@link + * java.nio.channels.NetworkChannel network} channels in the {@link + * java.nio.channels channels} package. + * + * @since 1.7 + */ + +public final class StandardSocketOption { + private StandardSocketOption() { } + + // -- SOL_SOCKET -- + + /** + * Allow transmission of broadcast datagrams. + * + *

The value of this socket option is a {@code Boolean} that represents + * whether the option is enabled or disabled. The option is specific to + * datagram-oriented sockets sending to {@link java.net.Inet4Address IPv4} + * broadcast addresses. When the socket option is enabled then the socket + * can be used to send broadcast datagrams. + * + *

The initial value of this socket option is {@code FALSE}. The socket + * option may be enabled or disabled at any time. Some operating systems may + * require that the Java virtual machine be started with implementation + * specific privileges to enable this option or send broadcast datagrams. + * + * @see RFC 929: + * Broadcasting Internet Datagrams + */ + public static final SocketOption SO_BROADCAST = + new StdSocketOption("SO_BROADCAST", Boolean.class); + + /** + * Keep connection alive. + * + *

The value of this socket option is a {@code Boolean} that represents + * whether the option is enabled or disabled. When the {@code SO_KEEPALIVE} + * option is enabled the operating system may use a keep-alive + * mechanism to periodically probe the other end of a connection when the + * connection is otherwise idle. The exact semantics of the keep alive + * mechanism is system dependent and therefore unspecified. + * + *

The initial value of this socket option is {@code FALSE}. The socket + * option may be enabled or disabled at any time. + * + * @see RFC 1122 + * Requirements for Internet Hosts -- Communication Layers + */ + public static final SocketOption SO_KEEPALIVE = + new StdSocketOption("SO_KEEPALIVE", Boolean.class); + + /** + * The size of the socket send buffer. + * + *

The value of this socket option is an {@code Integer} that is the + * size of the socket send buffer in bytes. The socket send buffer is an + * output buffer used by the networking implementation. It may need to be + * increased for high-volume connections. The value of the socket option is + * a hint to the implementation to size the buffer and the actual + * size may differ. The socket option can be queried to retrieve the actual + * size. + * + *

For datagram-oriented sockets, the size of the send buffer may limit + * the size of the datagrams that may be sent by the socket. Whether + * datagrams larger than the buffer size are sent or discarded is system + * dependent. + * + *

The initial/default size of the socket send buffer and the range of + * allowable values is system dependent although a negative size is not + * allowed. An attempt to set the socket send buffer to larger than its + * maximum size causes it to be set to its maximum size. + * + *

An implementation allows this socket option to be set before the + * socket is bound or connected. Whether an implementation allows the + * socket send buffer to be changed after the socket is bound is system + * dependent. + */ + public static final SocketOption SO_SNDBUF = + new StdSocketOption("SO_SNDBUF", Integer.class); + + + /** + * The size of the socket receive buffer. + * + *

The value of this socket option is an {@code Integer} that is the + * size of the socket receive buffer in bytes. The socket receive buffer is + * an input buffer used by the networking implementation. It may need to be + * increased for high-volume connections or decreased to limit the possible + * backlog of incoming data. The value of the socket option is a + * hint to the implementation to size the buffer and the actual + * size may differ. + * + *

For datagram-oriented sockets, the size of the receive buffer may + * limit the size of the datagrams that can be received. Whether datagrams + * larger than the buffer size can be received is system dependent. + * Increasing the socket receive buffer may be important for cases where + * datagrams arrive in bursts faster than they can be processed. + * + *

In the case of stream-oriented sockets and the TCP/IP protocol, the + * size of the socket receive buffer may be used when advertising the size + * of the TCP receive window to the remote peer. + * + *

The initial/default size of the socket receive buffer and the range + * of allowable values is system dependent although a negative size is not + * allowed. An attempt to set the socket receive buffer to larger than its + * maximum size causes it to be set to its maximum size. + * + *

An implementation allows this socket option to be set before the + * socket is bound or connected. Whether an implementation allows the + * socket receive buffer to be changed after the socket is bound is system + * dependent. + * + * @see RFC 1323: TCP + * Extensions for High Performance + */ + public static final SocketOption SO_RCVBUF = + new StdSocketOption("SO_RCVBUF", Integer.class); + + /** + * Re-use address. + * + *

The value of this socket option is a {@code Boolean} that represents + * whether the option is enabled or disabled. The exact semantics of this + * socket option are socket type and system dependent. + * + *

In the case of stream-oriented sockets, this socket option will + * usually determine whether the socket can be bound to a socket address + * when a previous connection involving that socket address is in the + * TIME_WAIT state. On implementations where the semantics differ, + * and the socket option is not required to be enabled in order to bind the + * socket when a previous connection is in this state, then the + * implementation may choose to ignore this option. + * + *

For datagram-oriented sockets the socket option is used to allow + * multiple programs bind to the same address. This option should be enabled + * when the socket is to be used for Internet Protocol (IP) multicasting. + * + *

An implementation allows this socket option to be set before the + * socket is bound or connected. Changing the value of this socket option + * after the socket is bound has no effect. The default value of this + * socket option is system dependent. + * + * @see RFC 793: Transmission + * Control Protocol + */ + public static final SocketOption SO_REUSEADDR = + new StdSocketOption("SO_REUSEADDR", Boolean.class); + + /** + * Linger on close if data is present. + * + *

The value of this socket option is an {@code Integer} that controls + * the action taken when unsent data is queued on the socket and a method + * to close the socket is invoked. If the value of the socket option is zero + * or greater, then it represents a timeout value, in seconds, known as the + * linger interval. The linger interval is the timeout for the + * {@code close} method to block while the operating system attempts to + * transmit the unsent data or it decides that it is unable to transmit the + * data. If the value of the socket option is less than zero then the option + * is disabled. In that case the {@code close} method does not wait until + * unsent data is transmitted; if possible the operating system will transmit + * any unsent data before the connection is closed. + * + *

This socket option is intended for use with sockets that are configured + * in {@link java.nio.channels.SelectableChannel#isBlocking() blocking} mode + * only. The behavior of the {@code close} method when this option is + * enabled on a non-blocking socket is not defined. + * + *

The initial value of this socket option is a negative value, meaning + * that the option is disabled. The option may be enabled, or the linger + * interval changed, at any time. The maximum value of the linger interval + * is system dependent. Setting the linger interval to a value that is + * greater than its maximum value causes the linger interval to be set to + * its maximum value. + */ + public static final SocketOption SO_LINGER = + new StdSocketOption("SO_LINGER", Integer.class); + + + // -- IPPROTO_IP -- + + /** + * The Type of Service (ToS) octet in the Internet Protocol (IP) header. + * + *

The value of this socket option is an {@code Integer}, the least + * significant 8 bits of which represents the value of the ToS octet in IP + * packets sent by sockets to an {@link StandardProtocolFamily#INET IPv4} + * socket. The interpretation of the ToS octet is network specific and + * is not defined by this class. Further information on the ToS octet can be + * found in RFC 1349 + * and RFC 2474. The + * value of the socket option is a hint. An implementation may + * ignore the value, or ignore specific values. + * + *

The initial/default value of the TOS field in the ToS octet is + * implementation specific but will typically be {@code 0}. For + * datagram-oriented sockets the option may be configured at any time after + * the socket has been bound. The new value of the octet is used when sending + * subsequent datagrams. It is system dependent whether this option can be + * queried or changed prior to binding the socket. + * + *

The behavior of this socket option on a stream-oriented socket, or an + * {@link StandardProtocolFamily#INET6 IPv6} socket, is not defined in this + * release. + */ + public static final SocketOption IP_TOS = + new StdSocketOption("IP_TOS", Integer.class); + + /** + * The network interface for Internet Protocol (IP) multicast datagrams. + * + *

The value of this socket option is a {@link NetworkInterface} that + * represents the outgoing interface for multicast datagrams sent by the + * datagram-oriented socket. For {@link StandardProtocolFamily#INET6 IPv6} + * sockets then it is system dependent whether setting this option also + * sets the outgoing interface for multlicast datagrams sent to IPv4 + * addresses. + * + *

The initial/default value of this socket option may be {@code null} + * to indicate that outgoing interface will be selected by the operating + * system, typically based on the network routing tables. An implementation + * allows this socket option to be set after the socket is bound. Whether + * the socket option can be queried or changed prior to binding the socket + * is system dependent. + * + * @see java.nio.channels.MulticastChannel + */ + public static final SocketOption IP_MULTICAST_IF = + new StdSocketOption("IP_MULTICAST_IF", NetworkInterface.class); + + /** + * The time-to-live for Internet Protocol (IP) multicast datagrams. + * + *

The value of this socket option is an {@code Integer} in the range + * 0 <= value <= 255. It is used to control + * the scope of multicast datagrams sent by the datagram-oriented socket. + * In the case of an {@link StandardProtocolFamily#INET IPv4} socket + * the option is the time-to-live (TTL) on multicast datagrams sent by the + * socket. Datagrams with a TTL of zero are not transmitted on the network + * but may be delivered locally. In the case of an {@link + * StandardProtocolFamily#INET6 IPv6} socket the option is the + * hop limit which is number of hops that the datagram can + * pass through before expiring on the network. For IPv6 sockets it is + * system dependent whether the option also sets the time-to-live + * on multicast datagrams sent to IPv4 addresses. + * + *

The initial/default value of the time-to-live setting is typically + * {@code 1}. An implementation allows this socket option to be set after + * the socket is bound. Whether the socket option can be queried or changed + * prior to binding the socket is system dependent. + * + * @see java.nio.channels.MulticastChannel + */ + public static final SocketOption IP_MULTICAST_TTL = + new StdSocketOption("IP_MULTICAST_TTL", Integer.class); + + /** + * Loopback for Internet Protocol (IP) multicast datagrams. + * + *

The value of this socket option is a {@code Boolean} that controls + * the loopback of multicast datagrams. The value of the socket + * option represents if the option is enabled or disabled. + * + *

The exact semantics of this socket options are system dependent. + * In particular, it is system dependent whether the loopback applies to + * multicast datagrams sent from the socket or received by the socket. + * For {@link StandardProtocolFamily#INET6 IPv6} sockets then it is + * system dependent whether the option also applies to multicast datagrams + * sent to IPv4 addresses. + * + *

The initial/default value of this socket option is {@code TRUE}. An + * implementation allows this socket option to be set after the socket is + * bound. Whether the socket option can be queried or changed prior to + * binding the socket is system dependent. + * + * @see java.nio.channels.MulticastChannel + */ + public static final SocketOption IP_MULTICAST_LOOP = + new StdSocketOption("IP_MULTICAST_LOOP", Boolean.class); + + + // -- IPPROTO_TCP -- + + /** + * Disable the Nagle algorithm. + * + *

The value of this socket option is a {@code Boolean} that represents + * whether the option is enabled or disabled. The socket option is specific to + * stream-oriented sockets using the TCP/IP protocol. TCP/IP uses an algorithm + * known as The Nagle Algorithm to coalesce short segments and + * improve network efficiency. + * + *

The default value of this socket option is {@code FALSE}. The + * socket option should only be enabled in cases where it is known that the + * coalescing impacts performance. The socket option may be enabled at any + * time. In other words, the Nagle Algorithm can be disabled. Once the option + * is enabled, it is system dependent whether it can be subsequently + * disabled. In that case, invoking the {@code setOption} method to disable + * the option has no effect. + * + * @see RFC 1122: + * Requirements for Internet Hosts -- Communication Layers + */ + public static final SocketOption TCP_NODELAY = + new StdSocketOption("TCP_NODELAY", Boolean.class); + + + private static class StdSocketOption implements SocketOption { + private final String name; + private final Class type; + StdSocketOption(String name, Class type) { + this.name = name; + this.type = type; + } + @Override public String name() { return name; } + @Override public Class type() { return type; } + @Override public String toString() { return name; } + } +} diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/java/nio/channels/DatagramChannel.java --- a/jdk/src/share/classes/java/nio/channels/DatagramChannel.java Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/share/classes/java/nio/channels/DatagramChannel.java Sun Aug 31 18:39:01 2008 +0100 @@ -26,28 +26,21 @@ package java.nio.channels; import java.io.IOException; +import java.net.ProtocolFamily; import java.net.DatagramSocket; +import java.net.SocketOption; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.spi.*; - /** * A selectable channel for datagram-oriented sockets. * - * - *

Datagram channels are not a complete abstraction of network datagram - * sockets. Binding and the manipulation of socket options must be done - * through an associated {@link java.net.DatagramSocket} object obtained by - * invoking the {@link #socket() socket} method. It is not possible to create - * a channel for an arbitrary, pre-existing datagram socket, nor is it possible - * to specify the {@link java.net.DatagramSocketImpl} object to be used by a - * datagram socket associated with a datagram channel. - * - *

A datagram channel is created by invoking the {@link #open open} method - * of this class. A newly-created datagram channel is open but not connected. - * A datagram channel need not be connected in order for the {@link #send send} - * and {@link #receive receive} methods to be used. A datagram channel may be + *

A datagram channel is created by invoking one of the {@link #open open} methods + * of this class. It is not possible to create a channel for an arbitrary, + * pre-existing datagram socket. A newly-created datagram channel is open but not + * connected. A datagram channel need not be connected in order for the {@link #send + * send} and {@link #receive receive} methods to be used. A datagram channel may be * connected, by invoking its {@link #connect connect} method, in order to * avoid the overhead of the security checks are otherwise performed as part of * every send and receive operation. A datagram channel must be connected in @@ -59,11 +52,57 @@ * disconnected or closed. Whether or not a datagram channel is connected may * be determined by invoking its {@link #isConnected isConnected} method. * + *

Socket options are configured using the {@link #setOption(SocketOption,Object) + * setOption} method. Datagram channels support the following options: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Option NameDescription
{@link java.net.StandardSocketOption#SO_SNDBUF SO_SNDBUF} The size of the socket send buffer
{@link java.net.StandardSocketOption#SO_RCVBUF SO_RCVBUF} The size of the socket receive buffer
{@link java.net.StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} Re-use address
{@link java.net.StandardSocketOption#SO_BROADCAST SO_BROADCAST} Allow transmission of broadcast datagrams
{@link java.net.StandardSocketOption#IP_TOS IP_TOS} The Type of Service (ToS) octet in the Internet Protocol (IP) header
{@link java.net.StandardSocketOption#IP_MULTICAST_IF IP_MULTICAST_IF} The network interface for Internet Protocol (IP) multicast datagrams
{@link java.net.StandardSocketOption#IP_MULTICAST_TTL + * IP_MULTICAST_TTL} The time-to-live for Internet Protocol (IP) multicast + * datagrams
{@link java.net.StandardSocketOption#IP_MULTICAST_LOOP + * IP_MULTICAST_LOOP} Loopback for Internet Protocol (IP) multicast datagrams
+ *
+ * Additional (implementation specific) options may also be supported. + * *

Datagram channels are safe for use by multiple concurrent threads. They * support concurrent reading and writing, though at most one thread may be * reading and at most one thread may be writing at any given time.

* - * * @author Mark Reinhold * @author JSR-51 Expert Group * @since 1.4 @@ -71,7 +110,7 @@ public abstract class DatagramChannel extends AbstractSelectableChannel - implements ByteChannel, ScatteringByteChannel, GatheringByteChannel + implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, MulticastChannel { /** @@ -88,7 +127,13 @@ * java.nio.channels.spi.SelectorProvider#openDatagramChannel() * openDatagramChannel} method of the system-wide default {@link * java.nio.channels.spi.SelectorProvider} object. The channel will not be - * connected.

+ * connected. + * + *

The {@link ProtocolFamily ProtocolFamily} of the channel's socket + * is platform (and possibly configuration) dependent and therefore unspecified. + * The {@link #open(ProtocolFamily) open} allows the protocol family to be + * selected when opening a datagram channel, and should be used to open + * datagram channels that are intended for Internet Protocol multicasting. * * @return A new datagram channel * @@ -100,6 +145,39 @@ } /** + * Opens a datagram channel. + * + *

The {@code family} parameter is used to specify the {@link + * ProtocolFamily}. If the datagram channel is to be used for IP multicasing + * then this should correspond to the address type of the multicast groups + * that this channel will join. + * + *

The new channel is created by invoking the {@link + * java.nio.channels.spi.SelectorProvider#openDatagramChannel(ProtocolFamily) + * openDatagramChannel} method of the system-wide default {@link + * java.nio.channels.spi.SelectorProvider} object. The channel will not be + * connected. + * + * @param family + * The protocol family + * + * @return A new datagram channel + * + * @throws UnsupportedOperationException + * If the specified protocol family is not supported. For example, + * suppose the parameter is specified as {@link + * java.net.StandardProtocolFamily#INET6 StandardProtocolFamily.INET6} + * but IPv6 is not enabled on the platform. + * @throws IOException + * If an I/O error occurs + * + * @since 1.7 + */ + public static DatagramChannel open(ProtocolFamily family) throws IOException { + return SelectorProvider.provider().openDatagramChannel(family); + } + + /** * Returns an operation set identifying this channel's supported * operations. * @@ -118,6 +196,32 @@ // -- Socket-specific operations -- /** + * @throws AlreadyBoundException {@inheritDoc} + * @throws UnsupportedAddressTypeException {@inheritDoc} + * @throws ClosedChannelException {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws SecurityException + * If a security manager has been installed and its {@link + * SecurityManager#checkListen checkListen} method denies the + * operation + * + * @since 1.7 + */ + public abstract DatagramChannel bind(SocketAddress local) + throws IOException; + + /** + * @throws IllegalArgumentException {@inheritDoc} + * @throws ClosedChannelException {@inheritDoc} + * @throws IOException {@inheritDoc} + * + * @since 1.7 + */ + public abstract DatagramChannel setOption(SocketOption name, T value) + throws IOException; + + + /** * Retrieves a datagram socket associated with this channel. * *

The returned object will not declare any public methods that are not @@ -128,10 +232,10 @@ public abstract DatagramSocket socket(); /** - * Tells whether or not this channel's socket is connected.

+ * Tells whether or not this channel's socket is connected. * - * @return true if, and only if, this channel's socket - * is connected + * @return {@code true} if, and only if, this channel's socket + * is {@link #isOpen open} and connected */ public abstract boolean isConnected(); @@ -207,6 +311,19 @@ public abstract DatagramChannel disconnect() throws IOException; /** + * Returns the remote address to which this channel's socket is connected. + * + * @return The remote address; {@code null} if the channel is not {@link + * #isOpen open} or the channel's socket is not connected + * + * @throws IOException + * If an I/O error occurs + * + * @since 1.7 + */ + public abstract SocketAddress getConnectedAddress() throws IOException; + + /** * Receives a datagram via this channel. * *

If a datagram is immediately available, or if this channel is in diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/java/nio/channels/MembershipKey.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/java/nio/channels/MembershipKey.java Sun Aug 31 18:39:01 2008 +0100 @@ -0,0 +1,183 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.nio.channels; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.io.IOException; +import java.util.List; + +/** + * A token representing the membership of an Internet Protocol (IP) multicast + * group. + * + *

A membership key may represent a membership to receive all datagrams sent + * to the group, or it may be source-specific, meaning that it + * represents a membership that receives only datagrams from a specific source + * address. Whether or not a membership key is source-specific may be determined + * by invoking its {@link #getSourceAddress() getSourceAddress} method. + * + *

A membership key is valid upon creation and remains valid until the + * membership is dropped by invoking the {@link #drop() drop} method, or + * the channel is closed. The validity of the membership key may be tested + * by invoking its {@link #isValid() isValid} method. + * + *

Where a membership key is not source-specific and the underlying operation + * system supports source filtering, then the {@link #block block} and {@link + * #unblock unblock} methods can be used to block or unblock multicast datagrams + * from particular source addresses. + * + * @see MulticastChannel + * + * @since 1.7 + */ +public abstract class MembershipKey { + + /** + * Initializes a new instance of this class. + */ + protected MembershipKey() { + } + + /** + * Tells whether or not this membership is valid. + * + *

A multicast group membership is valid upon creation and remains + * valid until the membership is dropped by invoking the {@link #drop() drop} + * method, or the channel is closed. + * + * @return {@code true} if this membership key is valid, {@code false} + * otherwise + */ + public abstract boolean isValid(); + + /** + * Drop membership. + * + *

If the membership key represents a membership to receive all datagrams + * then the membership is dropped and the channel will no longer receive any + * datagrams sent to the group. If the membership key is source-specific + * then the channel will no longer receive datagrams sent to the group from + * that source address. + * + *

After membership is dropped it may still be possible to receive + * datagrams sent to the group. This can arise when datagrams are waiting to + * be received in the socket's receive buffer. After membership is dropped + * then the channel may {@link MulticastChannel#join join} the group again + * in which case a new membership key is returned. + * + *

Upon return, this membership object will be {@link #isValid() invalid}. + * If the multicast group membership is already invalid then invoking this + * method has no effect. Once a multicast group membership is invalid, + * it remains invalid forever. + * + * @throws IOException + * If an I/O error occurs + */ + public abstract void drop() throws IOException; + + /** + * Block multicast datagrams from the given source address. + * + *

If this membership key is not source-specific, and the underlying + * operating system supports source filtering, then this method blocks + * multicast datagrams from the given source address. If the given source + * address is already blocked then this method has no effect. + * After a source address is blocked it may still be possible to receive + * datagams from that source. This can arise when datagrams are waiting to + * be received in the socket's receive buffer. + * + * @param source + * The source address to block + * + * @return This membership key + * + * @throws IllegalArgumentException + * If the {@code source} parameter is not a unicast address or + * is not the same address type as the multicast group + * @throws IllegalStateException + * If this membership key is source-specific or is no longer valid + * @throws UnsupportedOperationException + * If the underlying operating system does not support source + * filtering + * @throws IOException + * If an I/O error occurs + */ + public abstract MembershipKey block(InetAddress source) throws IOException; + + /** + * Unblock multicast datagrams from the given source address that was + * previously blocked using the {@link #block(InetAddress) block} method. + * + * @param source + * The source address to unblock + * + * @return This membership key + * + * @throws IllegalStateException + * If the given source address is not currently blocked or the + * membership key is no longer valid + * @throws IOException + * If an I/O error occurs + */ + public abstract MembershipKey unblock(InetAddress source) throws IOException; + + /** + * Returns the channel for which this membership key was created. This + * method will continue to return the channel even after the membership + * becomes {@link #isValid invalid}. + * + * @return the channel + */ + public abstract MulticastChannel getChannel(); + + /** + * Returns the multicast group for which this membership key was created. + * This method will continue to return the group even after the membership + * becomes {@link #isValid invalid}. + * + * @return the multicast group + */ + public abstract InetAddress getGroup(); + + /** + * Returns the network interface for which this membership key was created. + * This method will continue to return the network interface even after the + * membership becomes {@link #isValid invalid}. + * + * @return the network interface + */ + public abstract NetworkInterface getNetworkInterface(); + + /** + * Returns the source address if this membership key is source-specific, + * or {@code null} if this membership is not source-specific. + * + * @return The source address if this membership key is source-specific, + * otherwise {@code null} + */ + public abstract InetAddress getSourceAddress(); +} diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/java/nio/channels/MulticastChannel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/java/nio/channels/MulticastChannel.java Sun Aug 31 18:39:01 2008 +0100 @@ -0,0 +1,211 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.nio.channels; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.io.IOException; +import java.net.ProtocolFamily; // javadoc +import java.net.StandardProtocolFamily; // javadoc +import java.net.StandardSocketOption; // javadoc + +/** + * A network channel that supports Internet Protocol (IP) multicasting. + * + *

IP multicasting is the transmission of IP datagrams to members of + * a group that is zero or more hosts identified by a single destination + * address. + * + *

In the case of a channel to an {@link StandardProtocolFamily#INET IPv4} socket, + * the underlying operating system supports + * RFC 2236: Internet Group Management Protocol, Version 2 (IGMPv2). + * It may optionally support source filtering as specified by RFC 3376: Internet Group + * Management Protocol, Version 3 (IGMPv3). + * For channels to an {@link StandardProtocolFamily#INET6 IPv6} socket, the equivalent + * standards are RFC 2710: + * Multicast Listener Discovery (MLD) for IPv6 and RFC 3810: Multicast Listener + * Discovery Version 2 (MLDv2) for IPv6. + * + *

The {@link #join(InetAddress,NetworkInterface)} method is used to + * join a group and receive all multicast datagrams sent to the group. A channel + * may join several multicast groups and may join the same group on several + * {@link NetworkInterface interfaces}. Membership is dropped by invoking the {@link + * MembershipKey#drop drop} method on the returned {@link MembershipKey}. If the + * underlying platform supports source filtering then the {@link MembershipKey#block + * block} and {@link MembershipKey#unblock unblock} methods can be used to block or + * unblock multicast datagrams from particular source addresses. + * + *

The {@link #join(InetAddress,NetworkInterface,InetAddress)} method + * is used to begin receiving datagrams sent to a group whose source address matches + * a given source address. This method throws {@link UnsupportedOperationException} + * if the underlying platform does not support source filtering. Membership is + * cumulative and this method may be invoked again with the same group + * and interface to allow receiving datagrams from other source addresses. The + * method returns a {@link MembershipKey} that represents membership to receive + * datagrams from the given source address. Invoking the key's {@link + * MembershipKey#drop drop} method drops membership so that datagrams from the + * source address can no longer be received. + * + *

Platform dependencies

+ * + * The multicast implementation is intended to map directly to the native + * multicasting facility. Consequently, the following items should be considered + * when developing an application that receives IP multicast datagrams: + * + *
    + * + *
  1. The creation of the channel should specify the {@link ProtocolFamily} + * that corresponds to the address type of the multicast groups that the channel + * will join. There is no guarantee that a channel to a socket in one protocol + * family can join and receive multicast datagrams when the address of the + * multicast group corresponds to another protocol family. For example, it is + * implementation specific if a channel to an {@link StandardProtocolFamily#INET6 IPv6} + * socket can join an {@link StandardProtocolFamily#INET IPv4} multicast group and receive + * multicast datagrams sent to the group.

  2. + * + *
  3. The channel's socket should be bound to the {@link + * InetAddress#isAnyLocalAddress wildcard} address. If the socket is bound to + * a specific address, rather than the wildcard address then it is implementation + * specific if multicast datagrams are received by the socket.

  4. + * + *
  5. The {@link StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} option should be + * enabled prior to {@link NetworkChannel#bind binding} the socket. This is + * required to allow multiple members of the group to bind to the same + * address.

  6. + * + *
+ * + *

Usage Example: + *

+ *     // join multicast group on this interface, and also use this
+ *     // interface for outgoing multicast datagrams
+ *     NetworkInterface ni = NetworkInterface.getByName("hme0");
+ *
+ *     DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET)
+ *         .setOption(StandardSocketOption.SO_REUSEADDR, true)
+ *         .bind(new InetSocketAddress(5000))
+ *         .setOption(StandardSocketOption.IP_MULTICAST_IF, ni);
+ *
+ *     InetAddress group = InetAddress.getByName("225.4.5.6");
+ *
+ *     MembershipKey key = dc.join(group, ni);
+ * 
+ * + * @since 1.7 + */ + +public interface MulticastChannel + extends NetworkChannel +{ + /** + * Joins a multicast group to begin receiving all datagrams sent to the group, + * returning a membership key. + * + *

If this channel is currently a member of the group on the given + * interface to receive all datagrams then the membership key, representing + * that membership, is returned. Otherwise this channel joins the group and + * the resulting new membership key is returned. The resulting membership key + * is not {@link MembershipKey#getSourceAddress source-specific}. + * + *

A multicast channel may join several multicast groups, including + * the same group on more than one interface. An implementation may impose a + * limit on the number of groups that may be joined at the same time. + * + * @param group + * The multicast address to join + * @param interf + * The network interface on which to join the group + * + * @return The membership key + * + * @throws IllegalArgumentException + * If the group parameter is not a {@link InetAddress#isMulticastAddress + * multicast} address, or the group parameter is an address type + * that is not supported by this channel + * @throws IllegalStateException + * If the channel already has source-specific membership of the + * group on the interface + * @throws ClosedChannelException + * If this channel is closed + * @throws IOException + * If an I/O error occurs + * @throws SecurityException + * If a security manager is set, and its + * {@link SecurityManager#checkMulticast(InetAddress) checkMulticast} + * method denies access to the multiast group + */ + MembershipKey join(InetAddress group, NetworkInterface interf) + throws IOException; + + /** + * Joins a multicast group to begin receiving datagrams sent to the group + * from a given source address. + * + *

If this channel is currently a member of the group on the given + * interface to receive datagrams from the given source address then the + * membership key, representing that membership, is returned. Otherwise this + * channel joins the group and the resulting new membership key is returned. + * The resulting membership key is {@link MembershipKey#getSourceAddress + * source-specific}. + * + *

Membership is cumulative and this method may be invoked + * again with the same group and interface to allow receiving datagrams sent + * by other source addresses to the group. + * + * @param group + * The multicast address to join + * @param interf + * The network interface on which to join the group + * @param source + * The source address + * + * @return The membership key + * + * @throws IllegalArgumentException + * If the group parameter is not a {@link + * InetAddress#isMulticastAddress multicast} address, the + * source parameter is not a unicast address, the group + * parameter is an address type that is not supported by this channel, + * or the source parameter is not the same address type as the group + * @throws IllegalStateException + * If the channel is currently a member of the group on the given + * interface to receive all datagrams + * @throws UnsupportedOperationException + * If the underlying operation system does not support source filtering + * @throws ClosedChannelException + * If this channel is closed + * @throws IOException + * If an I/O error occurs + * @throws SecurityException + * If a security manager is set, and its + * {@link SecurityManager#checkMulticast(InetAddress) checkMulticast} + * method denies access to the multiast group + */ + MembershipKey join(InetAddress group, NetworkInterface interf, InetAddress source) + throws IOException; +} diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/java/nio/channels/NetworkChannel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/java/nio/channels/NetworkChannel.java Sun Aug 31 18:39:01 2008 +0100 @@ -0,0 +1,158 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.nio.channels; + +import java.net.SocketOption; +import java.net.SocketAddress; +import java.util.Set; +import java.io.IOException; + +/** + * A channel to a network socket. + * + *

A channel that implements this interface is a channel to a network + * socket. The {@link #bind(SocketAddress) bind} method is used to bind the + * socket to a local {@link SocketAddress address}, the {@link #getLocalAddress() + * getLocalAddress} method returns the address that the socket is bound to, and + * the {@link #setOption(SocketOption,Object) setOption} and {@link + * #getOption(SocketOption) getOption} methods are used to set and query socket + * options. An implementation of this interface should specify the socket options + * that it supports. + * + *

The {@link #bind bind} and {@link #setOption setOption} methods that do + * not otherwise have a value to return are specified to return the network + * channel upon which they are invoked. This allows method invocations to be + * chained. Implementations of this interface should specialize the return type + * so that method invocations on the implementation class can be chained. + * + * @since 1.7 + */ + +public interface NetworkChannel + extends Channel +{ + /** + * Binds the channel's socket to a local address. + * + *

This method is used to establish an association between the socket and + * a local address. Once an association is established then the socket remains + * bound until the channel is closed. If the {@code local} parameter has the + * value {@code null} then the socket will be bound to an address that is + * assigned automatically. + * + * @param local + * The address to bind the socket, or {@code null} to bind the socket + * to an automatically assigned socket address + * + * @return This channel + * + * @throws AlreadyBoundException + * If the socket is already bound + * @throws UnsupportedAddressTypeException + * If the type of the given address is not supported + * @throws ClosedChannelException + * If the channel is closed + * @throws IOException + * If some other I/O error occurs + * @throws SecurityException + * If a security manager is installed and it denies an unspecified + * permission. An implementation of this interface should specify + * any required permissions. + * + * @see #getLocalAddress + */ + NetworkChannel bind(SocketAddress local) throws IOException; + + /** + * Returns the socket address that this channel's socket is bound to, or + * {@code null} if the socket is not bound. + * + *

Where the channel is {@link #bind bound} to an Internet Protocol + * socket address then the return value from this method is of type {@link + * java.net.InetSocketAddress}. + * + * @return The socket address that the socket is bound to, or {@code null} + * if the channel is not {@link #isOpen open} or the channel's socket + * is not bound + * + * @throws IOException + * If an I/O error occurs + */ + SocketAddress getLocalAddress() throws IOException; + + /** + * Sets the value of a socket option. + * + * @param name + * The socket option + * @param value + * The value of the socket option. A value of {@code null} may be + * a valid value for some socket options. + * + * @return This channel + * + * @throws IllegalArgumentException + * If the socket option is not supported by this channel, or + * the value is not a valid value for this socket option + * @throws ClosedChannelException + * If this channel is closed + * @throws IOException + * If an I/O error occurs + * + * @see java.net.StandardSocketOption + */ + NetworkChannel setOption(SocketOption name, T value) throws IOException; + + /** + * Returns the value of a socket option. + * + * @param name + * The socket option + * + * @return The value of the socket option. A value of {@code null} may be + * a valid value for some socket options. + * + * @throws IllegalArgumentException + * If the socket option is not supported by this channel + * @throws ClosedChannelException + * If this channel is closed + * @throws IOException + * If an I/O error occurs + * + * @see java.net.StandardSocketOption + */ + T getOption(SocketOption name) throws IOException; + + /** + * Returns a set of the socket options supported by this channel. + * + *

This method will continue to return the set of options even after the + * channel has been closed. + * + * @return A set of the socket options supported by this channel + */ + Set> options(); +} diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/java/nio/channels/ServerSocketChannel.java --- a/jdk/src/share/classes/java/nio/channels/ServerSocketChannel.java Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/share/classes/java/nio/channels/ServerSocketChannel.java Sun Aug 31 18:39:01 2008 +0100 @@ -27,33 +27,44 @@ import java.io.IOException; import java.net.ServerSocket; +import java.net.SocketOption; import java.net.SocketAddress; import java.nio.channels.spi.*; - /** * A selectable channel for stream-oriented listening sockets. * - *

Server-socket channels are not a complete abstraction of listening - * network sockets. Binding and the manipulation of socket options must be - * done through an associated {@link java.net.ServerSocket} object obtained by - * invoking the {@link #socket() socket} method. It is not possible to create - * a channel for an arbitrary, pre-existing server socket, nor is it possible - * to specify the {@link java.net.SocketImpl} object to be used by a server - * socket associated with a server-socket channel. + *

A server-socket channel is created by invoking the {@link #open() open} + * method of this class. It is not possible to create a channel for an arbitrary, + * pre-existing {@link ServerSocket}. A newly-created server-socket channel is + * open but not yet bound. An attempt to invoke the {@link #accept() accept} + * method of an unbound server-socket channel will cause a {@link NotYetBoundException} + * to be thrown. A server-socket channel can be bound by invoking one of the + * {@link #bind(java.net.SocketAddress,int) bind} methods defined by this class. * - *

A server-socket channel is created by invoking the {@link #open() open} - * method of this class. A newly-created server-socket channel is open but not - * yet bound. An attempt to invoke the {@link #accept() accept} method of an - * unbound server-socket channel will cause a {@link NotYetBoundException} to - * be thrown. A server-socket channel can be bound by invoking one of the - * {@link java.net.ServerSocket#bind(java.net.SocketAddress,int) bind} methods - * of an associated server socket. + *

Socket options are configured using the {@link #setOption(SocketOption,Object) + * setOption} method. Server-socket channels support the following options: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + *
Option NameDescription
{@link java.net.StandardSocketOption#SO_RCVBUF SO_RCVBUF} The size of the socket receive buffer
{@link java.net.StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} Re-use address
+ *
+ * Additional (implementation specific) options may also be supported. * *

Server-socket channels are safe for use by multiple concurrent threads. *

* - * * @author Mark Reinhold * @author JSR-51 Expert Group * @since 1.4 @@ -61,6 +72,7 @@ public abstract class ServerSocketChannel extends AbstractSelectableChannel + implements NetworkChannel { /** @@ -110,6 +122,89 @@ // -- ServerSocket-specific operations -- /** + * Binds the channel's socket to a local address and configures the socket + * to listen for connections. + * + *

An invocation of this method is equivalent to the following: + *

+     * bind(local, 0);
+     * 
+ * + * @param local + * The local address to bind the socket, or {@code null} to bind + * to an automatically assigned socket address + * + * @return This channel + * + * @throws AlreadyBoundException {@inheritDoc} + * @throws UnsupportedAddressTypeException {@inheritDoc} + * @throws ClosedChannelException {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws SecurityException + * If a security manager has been installed and its {@link + * SecurityManager#checkListen checkListen} method denies the + * operation + * + * @since 1.7 + */ + public final ServerSocketChannel bind(SocketAddress local) + throws IOException + { + return bind(local, 0); + } + + /** + * Binds the channel's socket to a local address and configures the socket to + * listen for connections. + * + *

This method is used to establish an association between the socket and + * a local address. Once an association is established then the socket remains + * bound until the channel is closed. + * + *

The {@code backlog} parameter is the maximum number of pending + * connections on the socket. Its exact semantics are implementation specific. + * In particular, an implementation may impose a maximum length or may choose + * to ignore the parameter altogther. If the {@code backlog} parameter has + * the value {@code 0}, or a negative value, then an implementation specific + * default is used. + * + * @param local + * The address to bind the socket, or {@code null} to bind to an + * automatically assigned socket address + * @param backlog + * The maximum number of pending connections + * + * @return This channel + * + * @throws AlreadyBoundException + * If the socket is already bound + * @throws UnsupportedAddressTypeException + * If the type of the given address is not supported + * @throws ClosedChannelException + * If this channel is closed + * @throws IOException + * If some other I/O error occurs + * @throws SecurityException + * If a security manager has been installed and its {@link + * SecurityManager#checkListen checkListen} method denies the + * operation + * + * @since 1.7 + */ + public abstract ServerSocketChannel bind(SocketAddress local, int backlog) + throws IOException; + + /** + * @throws IllegalArgumentException {@inheritDoc} + * @throws ClosedChannelException {@inheritDoc} + * @throws IOException {@inheritDoc} + * + * @since 1.7 + */ + public abstract ServerSocketChannel setOption(SocketOption name, T value) + throws IOException; + + /** * Retrieves a server socket associated with this channel. * *

The returned object will not declare any public methods that are not diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/java/nio/channels/SocketChannel.java --- a/jdk/src/share/classes/java/nio/channels/SocketChannel.java Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/share/classes/java/nio/channels/SocketChannel.java Sun Aug 31 18:39:01 2008 +0100 @@ -27,24 +27,17 @@ import java.io.IOException; import java.net.Socket; +import java.net.SocketOption; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.spi.*; - /** * A selectable channel for stream-oriented connecting sockets. * - *

Socket channels are not a complete abstraction of connecting network - * sockets. Binding, shutdown, and the manipulation of socket options must be - * done through an associated {@link java.net.Socket} object obtained by - * invoking the {@link #socket() socket} method. It is not possible to create - * a channel for an arbitrary, pre-existing socket, nor is it possible to - * specify the {@link java.net.SocketImpl} object to be used by a socket - * associated with a socket channel. - * *

A socket channel is created by invoking one of the {@link #open open} - * methods of this class. A newly-created socket channel is open but not yet + * methods of this class. It is not possible to create a channel for an arbitrary, + * pre-existing socket. A newly-created socket channel is open but not yet * connected. An attempt to invoke an I/O operation upon an unconnected * channel will cause a {@link NotYetConnectedException} to be thrown. A * socket channel can be connected by invoking its {@link #connect connect} @@ -59,16 +52,6 @@ * Whether or not a connection operation is in progress may be determined by * invoking the {@link #isConnectionPending isConnectionPending} method. * - *

The input and output sides of a socket channel may independently be - * shut down without actually closing the channel. Shutting down the - * input side of a channel by invoking the {@link java.net.Socket#shutdownInput - * shutdownInput} method of an associated socket object will cause further - * reads on the channel to return -1, the end-of-stream indication. - * Shutting down the output side of the channel by invoking the {@link - * java.net.Socket#shutdownOutput shutdownOutput} method of an associated - * socket object will cause further writes on the channel to throw a {@link - * ClosedChannelException}. - * *

Socket channels support asynchronous shutdown, which is similar * to the asynchronous close operation specified in the {@link Channel} class. * If the input side of a socket is shut down by one thread while another @@ -79,6 +62,43 @@ * channel, then the blocked thread will receive an {@link * AsynchronousCloseException}. * + *

Socket options are configured using the {@link #setOption(SocketOption,Object) + * setOption} method. Socket channels support the following options: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Option NameDescription
{@link java.net.StandardSocketOption#SO_SNDBUF SO_SNDBUF} The size of the socket send buffer
{@link java.net.StandardSocketOption#SO_RCVBUF SO_RCVBUF} The size of the socket receive buffer
{@link java.net.StandardSocketOption#SO_KEEPALIVE SO_KEEPALIVE} Keep connection alive
{@link java.net.StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} Re-use address
{@link java.net.StandardSocketOption#SO_LINGER SO_LINGER} Linger on close if data is present (when configured in blocking mode + * only)
{@link java.net.StandardSocketOption#TCP_NODELAY TCP_NODELAY} Disable the Nagle algorithm
+ *
+ * Additional (implementation specific) options may also be supported. + * *

Socket channels are safe for use by multiple concurrent threads. They * support concurrent reading and writing, though at most one thread may be * reading and at most one thread may be writing at any given time. The {@link @@ -87,7 +107,6 @@ * or write operation while an invocation of one of these methods is in * progress will block until that invocation is complete.

* - * * @author Mark Reinhold * @author JSR-51 Expert Group * @since 1.4 @@ -95,7 +114,7 @@ public abstract class SocketChannel extends AbstractSelectableChannel - implements ByteChannel, ScatteringByteChannel, GatheringByteChannel + implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, NetworkChannel { /** @@ -192,6 +211,73 @@ // -- Socket-specific operations -- /** + * @throws ConnectionPendingException + * If a non-blocking connection operation is already in progress on + * this channel + * @throws AlreadyBoundException {@inheritDoc} + * @throws UnsupportedAddressTypeException {@inheritDoc} + * @throws ClosedChannelException {@inheritDoc} + * @throws IOException {@inheritDoc} + * + * @since 1.7 + */ + @Override + public abstract SocketChannel bind(SocketAddress local) + throws IOException; + + /** + * @throws IllegalArgumentException {@inheritDoc} + * @throws ClosedChannelException {@inheritDoc} + * @throws IOException {@inheritDoc} + * + * @since 1.7 + */ + @Override + public abstract SocketChannel setOption(SocketOption name, T value) + throws IOException; + + /** + * Shutdown the connection for reading without closing the channel. + * + *

Once shutdown for reading then further reads on the channel will + * return {@code -1}, the end-of-stream indication. If the input side of the + * connection is already shutdown then invoking this method has no effect. + * + * @return The channel + * + * @throws NotYetConnectedException + * If this channel is not yet connected + * @throws ClosedChannelException + * If this channel is closed + * @throws IOException + * If some other I/O error occurs + * + * @since 1.7 + */ + public abstract SocketChannel shutdownInput() throws IOException; + + /** + * Shutdown the connection for writing without closing the channel. + * + *

Once shutdown for writing then further attempts to write to the + * channel will throw {@link ClosedChannelException}. If the output side of + * the connection is already shutdown then invoking this method has no + * effect. + * + * @return The channel + * + * @throws NotYetConnectedException + * If this channel is not yet connected + * @throws ClosedChannelException + * If this channel is closed + * @throws IOException + * If some other I/O error occurs + * + * @since 1.7 + */ + public abstract SocketChannel shutdownOutput() throws IOException; + + /** * Retrieves a socket associated with this channel. * *

The returned object will not declare any public methods that are not @@ -202,10 +288,10 @@ public abstract Socket socket(); /** - * Tells whether or not this channel's network socket is connected.

+ * Tells whether or not this channel's network socket is connected. * * @return true if, and only if, this channel's network socket - * is connected + * is {@link #isOpen open} and connected */ public abstract boolean isConnected(); @@ -339,6 +425,22 @@ */ public abstract boolean finishConnect() throws IOException; + /** + * Returns the remote address to which this channel's socket is connected. + * + *

Where the channel is bound and connected to an Internet Protocol + * socket address then the return value from this method is of type {@link + * java.net.InetSocketAddress}. + * + * @return The remote address; {@code null} if the channel is not {@link + * #isOpen open} or the channel's socket is not connected + * + * @throws IOException + * If an I/O error occurs + * + * @since 1.7 + */ + public abstract SocketAddress getConnectedAddress() throws IOException; // -- ByteChannel operations -- diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/java/nio/channels/exceptions --- a/jdk/src/share/classes/java/nio/channels/exceptions Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/share/classes/java/nio/channels/exceptions Sun Aug 31 18:39:01 2008 +0100 @@ -146,3 +146,14 @@ * virtual machine, or when another thread is already waiting to lock an * overlapping region of the same file." \ 2047812138163068433L + + +SINCE=1.7 + +SUPER=IllegalStateException + +gen AlreadyBoundException " + * Unchecked exception thrown when an attempt is made to bind the socket a + * network oriented channel that is already bound." \ + 6796072983322737592L + diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/java/nio/channels/package-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/java/nio/channels/package-info.java Sun Aug 31 18:39:01 2008 +0100 @@ -0,0 +1,231 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/** + * Defines channels, which represent connections to entities that are capable of + * performing I/O operations, such as files and sockets; defines selectors, for + * multiplexed, non-blocking I/O operations. + * + * + * + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *

Channels

Description

{@link java.nio.channels.Channel}A nexus for I/O operations
  {@link java.nio.channels.ReadableByteChannel}Can read into a buffer
    {@link java.nio.channels.ScatteringByteChannel}  Can read into a sequence of buffers
  {@link java.nio.channels.WritableByteChannel}Can write from a buffer
    {@link java.nio.channels.GatheringByteChannel}Can write from a sequence of buffers
  {@link java.nio.channels.ByteChannel}Can read/write to/from a buffer
    {@link java.nio.channels.SeekableByteChannel}A {@code ByteChannel} connected to an entity that contains a variable-length sequence of bytes
  {@link java.nio.channels.NetworkChannel}A channel to a network socket
    {@link java.nio.channels.MulticastChannel}Can join Internet Protocol (IP) multicast groups
{@link java.nio.channels.Channels}Utility methods for channel/stream interoperation
+ * + *

A channel represents an open connection to an entity such as a + * hardware device, a file, a network socket, or a program component that is + * capable of performing one or more distinct I/O operations, for example reading + * or writing. As specified in the {@link java.nio.channels.Channel} interface, + * channels are either open or closed, and they are both asynchronously + * closeable and interruptible. + * + *

The {@link java.nio.channels.Channel} interface is extended by several + * other interfaces. + * + *

The {@link java.nio.channels.ReadableByteChannel} interface specifies a + * {@link java.nio.channels.ReadableByteChannel#read read} method that reads bytes + * from the channel into a buffer; similarly, the {@link + * java.nio.channels.WritableByteChannel} interface specifies a {@link + * java.nio.channels.WritableByteChannel#write write} method that writes bytes + * from a buffer to the channel. The {@link java.nio.channels.ByteChannel} + * interface unifies these two interfaces for the common case of channels that can + * both read and write bytes. The {@link java.nio.channels.SeekableByteChannel} + * interface extends the {@code ByteChannel} interface with methods to {@link + * java.nio.channels.SeekableByteChannel#position() query} and {@link + * java.nio.channels.SeekableByteChannel#position(long) modify} the channel's + * current position, and its {@link java.nio.channels.SeekableByteChannel#size + * size}. + * + *

The {@link java.nio.channels.ScatteringByteChannel} and {@link + * java.nio.channels.GatheringByteChannel} interfaces extend the {@link + * java.nio.channels.ReadableByteChannel} and {@link + * java.nio.channels.WritableByteChannel} interfaces, respectively, adding {@link + * java.nio.channels.ScatteringByteChannel#read read} and {@link + * java.nio.channels.GatheringByteChannel#write write} methods that take a + * sequence of buffers rather than a single buffer. + * + *

The {@link java.nio.channels.NetworkChannel} interface specifies methods + * to {@link java.nio.channels.NetworkChannel#bind bind} the channel's socket, + * obtain the address to which the socket is bound, and methods to {@link + * java.nio.channels.NetworkChannel#getOption get} and {@link + * java.nio.channels.NetworkChannel#setOption set} socket options. The {@link + * java.nio.channels.MulticastChannel} interface specifies methods to join + * Internet Protocol (IP) multicast groups. + * + *

The {@link java.nio.channels.Channels} utility class defines static methods + * that support the interoperation of the stream classes of the {@link + * java.io} package with the channel classes of this package. An appropriate + * channel can be constructed from an {@link java.io.InputStream} or an {@link + * java.io.OutputStream}, and conversely an {@link java.io.InputStream} or an + * {@link java.io.OutputStream} can be constructed from a channel. A {@link + * java.io.Reader} can be constructed that uses a given charset to decode bytes + * from a given readable byte channel, and conversely a {@link java.io.Writer} can + * be constructed that uses a given charset to encode characters into bytes and + * write them to a given writable byte channel. + * + *

+ * + * + * + * + * + * + * + *

File channels

Description

{@link java.nio.channels.FileChannel}Reads, writes, maps, and manipulates files
{@link java.nio.channels.FileLock}A lock on a (region of a) file
{@link java.nio.MappedByteBuffer}/{@link java.nio.MappedBigByteBuffer}  A direct byte buffer or big byte buffer mapped to a region of a file
+ * + *

The {@link java.nio.channels.FileChannel} class supports the usual + * operations of reading bytes from, and writing bytes to, a channel connected to + * a file, as well as those of querying and modifying the current file position + * and truncating the file to a specific size. It defines methods for acquiring + * locks on the whole file or on a specific region of a file; these methods return + * instances of the {@link java.nio.channels.FileLock} class. Finally, it defines + * methods for forcing updates to the file to be written to the storage device that + * contains it, for efficiently transferring bytes between the file and other + * channels, and for mapping a region of the file directly into memory. + * + *

A {@code FileChannel} is created by invoking one of its static {@link + * java.nio.channels.FileChannel#open open} methods, or by invoking the {@code + * getChannel} method of a {@link java.io.FileInputStream}, {@link + * java.io.FileOutputStream}, or {@link java.io.RandomAccessFile} to return a + * file channel connected to the same underlying file as the {@link java.io} + * class. + * + * + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *

Multiplexed, non-blocking I/O

Description

{@link java.nio.channels.SelectableChannel}A channel that can be multiplexed
  {@link java.nio.channels.DatagramChannel}A channel to a datagram-oriented socket
  {@link java.nio.channels.Pipe.SinkChannel}The write end of a pipe
  {@link java.nio.channels.Pipe.SourceChannel}The read end of a pipe
  {@link java.nio.channels.ServerSocketChannel}  A channel to a stream-oriented listening socket
  {@link java.nio.channels.SocketChannel}A channel for a stream-oriented connecting socket
{@link java.nio.channels.Selector}A multiplexor of selectable channels
{@link java.nio.channels.SelectionKey}A token representing the registration
of a channel + * with a selector
{@link java.nio.channels.Pipe}Two channels that form a unidirectional pipe
+ * + *

Multiplexed, non-blocking I/O, which is much more scalable than + * thread-oriented, blocking I/O, is provided by selectors, selectable + * channels, and selection keys. + * + *

A selector is a multiplexor of selectable channels, which in turn are + * a special type of channel that can be put into non-blocking mode. To perform + * multiplexed I/O operations, one or more selectable channels are first created, + * put into non-blocking mode, and {@link + * java.nio.channels.SelectableChannel#register registered} + * with a selector. Registering a channel specifies the set of I/O operations + * that will be tested for readiness by the selector, and returns a selection key that represents the + * registration. + * + *

Once some channels have been registered with a selector, a selection operation can be performed in + * order to discover which channels, if any, have become ready to perform one or + * more of the operations in which interest was previously declared. If a channel + * is ready then the key returned when it was registered will be added to the + * selector's selected-key set. The key set, and the keys within it, can + * be examined in order to determine the operations for which each channel is + * ready. From each key one can retrieve the corresponding channel in order to + * perform whatever I/O operations are required. + * + *

That a selection key indicates that its channel is ready for some operation + * is a hint, but not a guarantee, that such an operation can be performed by a + * thread without causing the thread to block. It is imperative that code that + * performs multiplexed I/O be written so as to ignore these hints when they prove + * to be incorrect. + * + *

This package defines selectable-channel classes corresponding to the {@link + * java.net.DatagramSocket}, {@link java.net.ServerSocket}, and {@link + * java.net.Socket} classes defined in the {@link java.net} package. + * Minor changes to these classes have been made in order to support sockets that + * are associated with channels. This package also defines a simple class that + * implements unidirectional pipes. In all cases, a new selectable channel is + * created by invoking the static open method of the corresponding class. + * If a channel needs an associated socket then a socket will be created as a side + * effect of this operation. + * + *

The implementation of selectors, selectable channels, and selection keys + * can be replaced by "plugging in" an alternative definition or instance of the + * {@link java.nio.channels.spi.SelectorProvider} class defined in the {@link + * java.nio.channels.spi} package. It is not expected that many developers + * will actually make use of this facility; it is provided primarily so that + * sophisticated users can take advantage of operating-system-specific + * I/O-multiplexing mechanisms when very high performance is required. + * + *

Much of the bookkeeping and synchronization required to implement the + * multiplexed-I/O abstractions is performed by the {@link + * java.nio.channels.spi.AbstractInterruptibleChannel}, {@link + * java.nio.channels.spi.AbstractSelectableChannel}, {@link + * java.nio.channels.spi.AbstractSelectionKey}, and {@link + * java.nio.channels.spi.AbstractSelector} classes in the {@link + * java.nio.channels.spi} package. When defining a custom selector provider, + * only the {@link java.nio.channels.spi.AbstractSelector} and {@link + * java.nio.channels.spi.AbstractSelectionKey} classes should be subclassed + * directly; custom channel classes should extend the appropriate {@link + * java.nio.channels.SelectableChannel} subclasses defined in this package. + * + *


+ *

Unless otherwise noted, passing a null argument to a constructor + * or method in any class or interface in this package will cause a {@link + * java.lang.NullPointerException NullPointerException} to be thrown. + * + * @since 1.4 + * @author Mark Reinhold + * @author JSR-51 Expert Group + */ + +package java.nio.channels; diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/java/nio/channels/package.html --- a/jdk/src/share/classes/java/nio/channels/package.html Sun Aug 31 18:32:59 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,222 +0,0 @@ - - - - - - -Defines channels, which represent connections to entities that are capable of -performing I/O operations, such as files and sockets; defines selectors, for -multiplexed, non-blocking I/O operations. - - - - -

- - - - - - - - - - - - - - - -

Channels

Description

{@link java.nio.channels.Channel}A nexus for I/O operations
  {@link java.nio.channels.ReadableByteChannel}Can read into a buffer
    {@link java.nio.channels.ScatteringByteChannel}  Can read into a sequence of buffers
  {@link java.nio.channels.WritableByteChannel}Can write from a buffer
    {@link java.nio.channels.GatheringByteChannel}Can write from a sequence of buffers
  {@link java.nio.channels.ByteChannel}Can read/write to/from a buffer
{@link java.nio.channels.Channels}Utility methods for channel/stream interoperation
- -

A channel represents an open connection to an entity such as a -hardware device, a file, a network socket, or a program component that is -capable of performing one or more distinct I/O operations, for example reading -or writing. As specified in the {@link java.nio.channels.Channel} interface, -channels are either open or closed, and they are both asynchronously -closeable and interruptible. - -

The {@link java.nio.channels.Channel} interface is extended by several -other interfaces, each of which specifies a new I/O operation. - -

The {@link java.nio.channels.ReadableByteChannel} interface specifies a -{@link java.nio.channels.ReadableByteChannel#read read} method that reads bytes -from the channel into a buffer; similarly, the {@link -java.nio.channels.WritableByteChannel} interface specifies a {@link -java.nio.channels.WritableByteChannel#write write} method that writes bytes -from a buffer to the channel. The {@link java.nio.channels.ByteChannel} -interface unifies these two interfaces for the common case of channels that can -both read and write bytes. - -

The {@link java.nio.channels.ScatteringByteChannel} and {@link -java.nio.channels.GatheringByteChannel} interfaces extend the {@link -java.nio.channels.ReadableByteChannel} and {@link -java.nio.channels.WritableByteChannel} interfaces, respectively, adding {@link -java.nio.channels.ScatteringByteChannel#read read} and {@link -java.nio.channels.GatheringByteChannel#write write} methods that take a -sequence of buffers rather than a single buffer. - -

The {@link java.nio.channels.Channels} utility class defines static methods -that support the interoperation of the stream classes of the {@link -java.io} package with the channel classes of this package. An appropriate -channel can be constructed from an {@link java.io.InputStream} or an {@link -java.io.OutputStream}, and conversely an {@link java.io.InputStream} or an -{@link java.io.OutputStream} can be constructed from a channel. A {@link -java.io.Reader} can be constructed that uses a given charset to decode bytes -from a given readable byte channel, and conversely a {@link java.io.Writer} can -be constructed that uses a given charset to encode characters into bytes and -write them to a given writable byte channel. - - -

- - - - - - - -

File channels

Description

{@link java.nio.channels.FileChannel}Reads, writes, maps, and manipulates files
{@link java.nio.channels.FileLock}A lock on a (region of a) file
{@link java.nio.MappedByteBuffer}  A direct byte buffer mapped to a region of a file
- -

The {@link java.nio.channels.FileChannel} class supports the usual -operations of reading bytes from, and writing bytes to, a channel connected to -a file, as well as those of querying and modifying the current file position -and truncating the file to a specific size. It defines methods for acquiring -locks on the whole file or on a specific region of a file; these methods return -instances of the {@link java.nio.channels.FileLock} class. Finally, it defines -methods for forcing updates to the file to be written to the storage device that -contains it, for efficiently transferring bytes between the file and other -channels, and for mapping a region of the file directly into memory. This last -operation creates an instance of the {@link java.nio.MappedByteBuffer} -class, which extends the {@link java.nio.ByteBuffer} class with several -file-related operations. - -

A getChannel method has been added to each of the {@link -java.io.FileInputStream#getChannel FileInputStream}, {@link -java.io.FileOutputStream#getChannel FileOutputStream}, and {@link -java.io.RandomAccessFile#getChannel RandomAccessFile} classes of the {@link -java.io java.io} package. Invoking this method upon an instance of one of -these classes will return a file channel connected to the underlying file. - - - - -

- - - - - - - - - - - - - - - - - - - -

Multiplexed, non-blocking I/O

Description

{@link java.nio.channels.SelectableChannel}A channel that can be multiplexed
  {@link java.nio.channels.DatagramChannel}A channel for a {@link java.net.DatagramSocket java.net.DatagramSocket}
  {@link java.nio.channels.Pipe.SinkChannel}The write end of a pipe
  {@link java.nio.channels.Pipe.SourceChannel}The read end of a pipe
  {@link java.nio.channels.ServerSocketChannel}  A channel for a {@link java.net.ServerSocket java.net.ServerSocket}
  {@link java.nio.channels.SocketChannel}A channel for a {@link java.net.Socket java.net.Socket}
{@link java.nio.channels.Selector}A multiplexor of selectable channels
{@link java.nio.channels.SelectionKey}A token representing the registration
of a channel - with a selector
{@link java.nio.channels.Pipe}Two channels that form a unidirectional pipe
- -

Multiplexed, non-blocking I/O, which is much more scalable than -thread-oriented, blocking I/O, is provided by selectors, selectable -channels, and selection keys. - -

A selector is a multiplexor of selectable channels, which in turn are -a special type of channel that can be put into non-blocking mode. To perform -multiplexed I/O operations, one or more selectable channels are first created, -put into non-blocking mode, and {@link -java.nio.channels.SelectableChannel#register registered} -with a selector. Registering a channel specifies the set of I/O operations -that will be tested for readiness by the selector, and returns a selection key that represents the -registration. - -

Once some channels have been registered with a selector, a selection operation can be performed in -order to discover which channels, if any, have become ready to perform one or -more of the operations in which interest was previously declared. If a channel -is ready then the key returned when it was registered will be added to the -selector's selected-key set. The key set, and the keys within it, can -be examined in order to determine the operations for which each channel is -ready. From each key one can retrieve the corresponding channel in order to -perform whatever I/O operations are required. - -

That a selection key indicates that its channel is ready for some operation -is a hint, but not a guarantee, that such an operation can be performed by a -thread without causing the thread to block. It is imperative that code that -performs multiplexed I/O be written so as to ignore these hints when they prove -to be incorrect. - -

This package defines selectable-channel classes corresponding to the {@link -java.net.DatagramSocket}, {@link java.net.ServerSocket}, and {@link -java.net.Socket} classes defined in the {@link java.net} package. -Minor changes to these classes have been made in order to support sockets that -are associated with channels. This package also defines a simple class that -implements unidirectional pipes. In all cases, a new selectable channel is -created by invoking the static open method of the corresponding class. -If a channel needs an associated socket then a socket will be created as a side -effect of this operation. - -

The implementation of selectors, selectable channels, and selection keys -can be replaced by "plugging in" an alternative definition or instance of the -{@link java.nio.channels.spi.SelectorProvider} class defined in the {@link -java.nio.channels.spi} package. It is not expected that many developers -will actually make use of this facility; it is provided primarily so that -sophisticated users can take advantage of operating-system-specific -I/O-multiplexing mechanisms when very high performance is required. - -

Much of the bookkeeping and synchronization required to implement the -multiplexed-I/O abstractions is performed by the {@link -java.nio.channels.spi.AbstractInterruptibleChannel}, {@link -java.nio.channels.spi.AbstractSelectableChannel}, {@link -java.nio.channels.spi.AbstractSelectionKey}, and {@link -java.nio.channels.spi.AbstractSelector} classes in the {@link -java.nio.channels.spi} package. When defining a custom selector provider, -only the {@link java.nio.channels.spi.AbstractSelector} and {@link -java.nio.channels.spi.AbstractSelectionKey} classes should be subclassed -directly; custom channel classes should extend the appropriate {@link -java.nio.channels.SelectableChannel} subclasses defined in this package. - -

Unless otherwise noted, passing a null argument to a constructor -or method in any class or interface in this package will cause a {@link -java.lang.NullPointerException NullPointerException} to be thrown. - - -@since 1.4 -@author Mark Reinhold -@author JSR-51 Expert Group - - - diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/java/nio/channels/spi/SelectorProvider.java --- a/jdk/src/share/classes/java/nio/channels/spi/SelectorProvider.java Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/share/classes/java/nio/channels/spi/SelectorProvider.java Sun Aug 31 18:39:01 2008 +0100 @@ -25,10 +25,8 @@ package java.nio.channels.spi; -import java.io.FileDescriptor; import java.io.IOException; -import java.net.ServerSocket; -import java.net.Socket; +import java.net.ProtocolFamily; import java.nio.channels.*; import java.security.AccessController; import java.security.PrivilegedAction; @@ -190,7 +188,25 @@ throws IOException; /** - * Opens a pipe.

+ * Opens a datagram channel. + * + * @param family + * The protocol family + * + * @return A new datagram channel + * + * @throws UnsupportedOperationException + * If the specified protocol family is not supported + * @throws IOException + * If an I/O error occurs + * + * @since 1.7 + */ + public abstract DatagramChannel openDatagramChannel(ProtocolFamily family) + throws IOException; + + /** + * Opens a pipe.

* * @return The new pipe */ diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java --- a/jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java Sun Aug 31 18:39:01 2008 +0100 @@ -31,7 +31,7 @@ import java.nio.ByteBuffer; import java.nio.channels.*; import java.nio.channels.spi.*; -import java.lang.ref.SoftReference; +import java.util.*; /** @@ -47,11 +47,14 @@ private static NativeDispatcher nd = new DatagramDispatcher(); // Our file descriptor - FileDescriptor fd = null; + private final FileDescriptor fd; // fd value needed for dev/poll. This value will remain valid // even after the value in the file descriptor object has been set to -1 - int fdVal; + private final int fdVal; + + // The protocol family of the socket + private final ProtocolFamily family; // IDs of native threads doing reads and writes, for signalling private volatile long readerThread = 0; @@ -59,8 +62,8 @@ // Cached InetAddress and port for unconnected DatagramChannels // used by receive0 - private InetAddress cachedSenderInetAddress = null; - private int cachedSenderPort = 0; + private InetAddress cachedSenderInetAddress; + private int cachedSenderPort; // Lock held by current reading or connecting thread private final Object readLock = new Object(); @@ -76,20 +79,20 @@ // State (does not necessarily increase monotonically) private static final int ST_UNINITIALIZED = -1; - private static int ST_UNCONNECTED = 0; - private static int ST_CONNECTED = 1; + private static final int ST_UNCONNECTED = 0; + private static final int ST_CONNECTED = 1; private static final int ST_KILLED = 2; private int state = ST_UNINITIALIZED; // Binding - private SocketAddress localAddress = null; - SocketAddress remoteAddress = null; - - // Options - private SocketOpts.IP options = null; + private SocketAddress localAddress; + private SocketAddress remoteAddress; // Our socket adaptor, if any - private DatagramSocket socket = null; + private DatagramSocket socket; + + // Multicast support + private MembershipRegistry registry; // -- End of fields protected by stateLock @@ -98,7 +101,26 @@ throws IOException { super(sp); - this.fd = Net.socket(false); + this.family = Net.isIPv6Available() ? + StandardProtocolFamily.INET6 : StandardProtocolFamily.INET; + this.fd = Net.socket(family, false); + this.fdVal = IOUtil.fdVal(fd); + this.state = ST_UNCONNECTED; + } + + public DatagramChannelImpl(SelectorProvider sp, ProtocolFamily family) { + super(sp); + if ((family != StandardProtocolFamily.INET) && + (family != StandardProtocolFamily.INET6)) { + throw new UnsupportedOperationException("Protocol family not supported"); + } + if (family == StandardProtocolFamily.INET6) { + if (!Net.isIPv6Available()) { + throw new UnsupportedOperationException("IPv6 not available"); + } + } + this.family = family; + this.fd = Net.socket(family, false); this.fdVal = IOUtil.fdVal(fd); this.state = ST_UNCONNECTED; } @@ -107,9 +129,12 @@ throws IOException { super(sp); + this.family = Net.isIPv6Available() ? + StandardProtocolFamily.INET6 : StandardProtocolFamily.INET; this.fd = fd; this.fdVal = IOUtil.fdVal(fd); this.state = ST_UNCONNECTED; + this.localAddress = Net.localAddress(fd); } public DatagramSocket socket() { @@ -120,6 +145,156 @@ } } + @Override + public SocketAddress getLocalAddress() throws IOException { + synchronized (stateLock) { + if (!isOpen()) + return null; + return localAddress; + } + } + + @Override + public SocketAddress getConnectedAddress() throws IOException { + synchronized (stateLock) { + if (!isOpen()) + return null; + return remoteAddress; + } + } + + @Override + public DatagramChannel setOption(SocketOption name, Object value) + throws IOException + { + if (name == null) + throw new NullPointerException(); + if (!options().contains(name)) + throw new IllegalArgumentException("Invalid option name"); + + synchronized (stateLock) { + ensureOpen(); + + if (name == StandardSocketOption.IP_TOS) { + // IPv4 only; no-op for IPv6 + if (family == StandardProtocolFamily.INET) { + Net.setSocketOption(fd, family, name, value); + } + return this; + } + + if (name == StandardSocketOption.IP_MULTICAST_TTL || + name == StandardSocketOption.IP_MULTICAST_LOOP) + { + // options are protocol dependent + Net.setSocketOption(fd, family, name, value); + return this; + } + + if (name == StandardSocketOption.IP_MULTICAST_IF) { + if (value == null) + throw new IllegalArgumentException("Cannot set IP_MULTICAST_IF to 'null'"); + NetworkInterface interf = (NetworkInterface)value; + if (family == StandardProtocolFamily.INET6) { + int index = interf.getIndex(); + if (index == -1) + throw new IOException("Network interface cannot be identified"); + Net.setInterface6(fd, index); + } else { + // need IPv4 address to identify interface + Inet4Address target = Net.anyInet4Address(interf); + if (target == null) + throw new IOException("Network interface not configured for IPv4"); + int targetAddress = Net.inet4AsInt(target); + Net.setInterface4(fd, targetAddress); + } + return this; + } + + // remaining options don't need any special handling + Net.setSocketOption(fd, Net.UNSPEC, name, value); + return this; + } + } + + @Override + @SuppressWarnings("unchecked") + public T getOption(SocketOption name) + throws IOException + { + if (name == null) + throw new NullPointerException(); + if (!options().contains(name)) + throw new IllegalArgumentException("Invalid option name"); + + synchronized (stateLock) { + ensureOpen(); + + if (name == StandardSocketOption.IP_TOS) { + // IPv4 only; always return 0 on IPv6 + if (family == StandardProtocolFamily.INET) { + return (T) Net.getSocketOption(fd, family, name); + } else { + return (T) Integer.valueOf(0); + } + } + + if (name == StandardSocketOption.IP_MULTICAST_TTL || + name == StandardSocketOption.IP_MULTICAST_LOOP) + { + return (T) Net.getSocketOption(fd, family, name); + } + + if (name == StandardSocketOption.IP_MULTICAST_IF) { + if (family == StandardProtocolFamily.INET) { + int address = Net.getInterface4(fd); + if (address == 0) + return null; // default interface + + InetAddress ia = Net.inet4FromInt(address); + NetworkInterface ni = NetworkInterface.getByInetAddress(ia); + if (ni == null) + throw new IOException("Unable to map address to interface"); + return (T) ni; + } else { + int index = Net.getInterface6(fd); + if (index == 0) + return null; // default interface + + NetworkInterface ni = NetworkInterface.getByIndex(index); + if (ni == null) + throw new IOException("Unable to map index to interface"); + return (T) ni; + } + } + + // no special handling + return (T) Net.getSocketOption(fd, Net.UNSPEC, name); + } + } + + private static class LazyInitialization { + static final Set> defaultOptions = defaultOptions(); + + private static Set> defaultOptions() { + HashSet> set = new HashSet>(8); + set.add(StandardSocketOption.SO_SNDBUF); + set.add(StandardSocketOption.SO_RCVBUF); + set.add(StandardSocketOption.SO_REUSEADDR); + set.add(StandardSocketOption.SO_BROADCAST); + set.add(StandardSocketOption.IP_TOS); + set.add(StandardSocketOption.IP_MULTICAST_IF); + set.add(StandardSocketOption.IP_MULTICAST_TTL); + set.add(StandardSocketOption.IP_MULTICAST_LOOP); + return Collections.unmodifiableSet(set); + } + } + + @Override + public final Set> options() { + return LazyInitialization.defaultOptions; + } + private void ensureOpen() throws ClosedChannelException { if (!isOpen()) throw new ClosedChannelException(); @@ -135,8 +310,10 @@ synchronized (readLock) { ensureOpen(); // If socket is not bound then behave as if nothing received - if (!isBound()) // ## NotYetBoundException ?? + // Will be fixed by 6621699 + if (localAddress() == null) { return null; + } int n = 0; ByteBuffer bb = null; try { @@ -267,6 +444,12 @@ do { n = send(fd, src, target); } while ((n == IOStatus.INTERRUPTED) && isOpen()); + + synchronized (stateLock) { + if (isOpen() && (localAddress == null)) { + localAddress = Net.localAddress(fd); + } + } return IOStatus.normalize(n); } finally { writerThread = 0; @@ -316,7 +499,8 @@ assert (pos <= lim); int rem = (pos <= lim ? lim - pos : 0); - int written = send0(fd, ((DirectBuffer)bb).address() + pos, + boolean preferIPv6 = (family != StandardProtocolFamily.INET); + int written = send0(preferIPv6, fd, ((DirectBuffer)bb).address() + pos, rem, target); if (written > 0) bb.position(pos + written); @@ -453,42 +637,8 @@ IOUtil.configureBlocking(fd, block); } - public SocketOpts options() { - synchronized (stateLock) { - if (options == null) { - SocketOptsImpl.Dispatcher d - = new SocketOptsImpl.Dispatcher() { - int getInt(int opt) throws IOException { - return Net.getIntOption(fd, opt); - } - void setInt(int opt, int arg) - throws IOException - { - Net.setIntOption(fd, opt, arg); - } - }; - options = new SocketOptsImpl.IP(d); - } - return options; - } - } - - public boolean isBound() { - return Net.localPortNumber(fd) != 0; - } - public SocketAddress localAddress() { synchronized (stateLock) { - if (isConnected() && (localAddress == null)) { - // Socket was not bound before connecting, - // so ask what the address turned out to be - localAddress = Net.localAddress(fd); - } - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - InetSocketAddress isa = (InetSocketAddress)localAddress; - sm.checkConnect(isa.getAddress().getHostAddress(), -1); - } return localAddress; } } @@ -499,22 +649,37 @@ } } - public void bind(SocketAddress local) throws IOException { + @Override + public DatagramChannel bind(SocketAddress local) throws IOException { synchronized (readLock) { synchronized (writeLock) { synchronized (stateLock) { ensureOpen(); - if (isBound()) + if (localAddress != null) throw new AlreadyBoundException(); - InetSocketAddress isa = Net.checkAddress(local); + InetSocketAddress isa; + if (local == null) { + isa = new InetSocketAddress(0); + } else { + isa = Net.checkAddress(local); + + // only Inet4Address allowed with IPv4 socket + if (family == StandardProtocolFamily.INET) { + InetAddress addr = isa.getAddress(); + if (!(addr instanceof Inet4Address)) + throw new UnsupportedAddressTypeException(); + } + } SecurityManager sm = System.getSecurityManager(); - if (sm != null) + if (sm != null) { sm.checkListen(isa.getPort()); - Net.bind(fd, isa.getAddress(), isa.getPort()); + } + Net.bind(family, fd, isa.getAddress(), isa.getPort()); localAddress = Net.localAddress(fd); } } } + return this; } public boolean isConnected() { @@ -533,7 +698,6 @@ } public DatagramChannel connect(SocketAddress sa) throws IOException { - int trafficClass = 0; int localPort = 0; synchronized(readLock) { @@ -545,10 +709,10 @@ if (sm != null) sm.checkConnect(isa.getAddress().getHostAddress(), isa.getPort()); - int n = Net.connect(fd, + int n = Net.connect(family, + fd, isa.getAddress(), - isa.getPort(), - trafficClass); + isa.getPort()); if (n <= 0) throw new Error(); // Can't happen @@ -558,6 +722,11 @@ sender = isa; cachedSenderInetAddress = isa.getAddress(); cachedSenderPort = isa.getPort(); + + // Socket was not bound before connecting, + if (localAddress == null) { + localAddress = Net.localAddress(fd); + } } } } @@ -584,9 +753,215 @@ return this; } + /** + * Joins channel's socket to the given group/interface and + * optional source address. + */ + private MembershipKey innerJoin(InetAddress group, + NetworkInterface interf, + InetAddress source) + throws IOException + { + if (!group.isMulticastAddress()) + throw new IllegalArgumentException("Group not a multicast address"); + + // check multicast address is compatible with this socket + if (!(group instanceof Inet4Address)) { + if (family == StandardProtocolFamily.INET) + throw new IllegalArgumentException("Group is not IPv4 address"); + if (!(group instanceof Inet6Address)) + throw new IllegalArgumentException("Address type not supported"); + } + + // check source address + if (source != null) { + if (source.isAnyLocalAddress()) + throw new IllegalArgumentException("Source address is a wildcard address"); + if (source.isMulticastAddress()) + throw new IllegalArgumentException("Source address is multicast address"); + if (source.getClass() != group.getClass()) + throw new IllegalArgumentException("Source address is different type to group"); + } + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkMulticast(group); + + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + + // check the registry to see if we are already a member of the group + if (registry == null) { + registry = new MembershipRegistry(); + } else { + // return existing membership key + MembershipKey key = registry.checkMembership(group, interf, source); + if (key != null) + return key; + } + + MembershipKeyImpl key; + if (family == StandardProtocolFamily.INET6) { + int index = interf.getIndex(); + if (index == -1) + throw new IOException("Network interface cannot be identified"); + + // need multicast and source address as byte arrays + byte[] groupAddress = Net.inet6AsByteArray(group); + byte[] sourceAddress = (source == null) ? null : + Net.inet6AsByteArray(source); + + // join the group + int n = Net.join6(fd, groupAddress, index, sourceAddress); + if (n == IOStatus.UNAVAILABLE) + throw new UnsupportedOperationException(); + + key = new MembershipKeyImpl.Type6(this, group, interf, source, + groupAddress, index, sourceAddress); + + } else { + // need IPv4 address to identify interface + Inet4Address target = Net.anyInet4Address(interf); + if (target == null) + throw new IOException("Network interface not configured for IPv4"); + + int groupAddress = Net.inet4AsInt(group); + int targetAddress = Net.inet4AsInt(target); + int sourceAddress = (source == null) ? 0 : Net.inet4AsInt(source); + + // join the group + int n = Net.join4(fd, groupAddress, targetAddress, sourceAddress); + if (n == IOStatus.UNAVAILABLE) + throw new UnsupportedOperationException(); + + key = new MembershipKeyImpl.Type4(this, group, interf, source, + groupAddress, targetAddress, sourceAddress); + } + + registry.add(key); + return key; + } + } + + @Override + public MembershipKey join(InetAddress group, + NetworkInterface interf) + throws IOException + { + return innerJoin(group, interf, null); + } + + @Override + public MembershipKey join(InetAddress group, + NetworkInterface interf, + InetAddress source) + throws IOException + { + if (source == null) + throw new NullPointerException("source address is null"); + return innerJoin(group, interf, source); + } + + // package-private + void drop(MembershipKeyImpl key) + throws IOException + { + assert key.getChannel() == this; + + synchronized (stateLock) { + if (!key.isValid()) + return; + + if (family == StandardProtocolFamily.INET6) { + MembershipKeyImpl.Type6 key6 = + (MembershipKeyImpl.Type6)key; + Net.drop6(fd, key6.group(), key6.index(), key6.source()); + } else { + MembershipKeyImpl.Type4 key4 = + (MembershipKeyImpl.Type4)key; + Net.drop4(fd, key4.group(), key4.interfaceAddress(), key4.source()); + } + + key.invalidate(); + registry.remove(key); + } + } + + /** + * Block datagrams from given source if a memory to receive all + * datagrams. + */ + void block(MembershipKeyImpl key, InetAddress source) + throws IOException + { + assert key.getChannel() == this; + assert key.getSourceAddress() == null; + + synchronized (stateLock) { + if (!key.isValid()) + throw new IllegalStateException("key is no longer valid"); + if (source.isAnyLocalAddress()) + throw new IllegalArgumentException("Source address is a wildcard address"); + if (source.isMulticastAddress()) + throw new IllegalArgumentException("Source address is multicast address"); + if (source.getClass() != key.getGroup().getClass()) + throw new IllegalArgumentException("Source address is different type to group"); + + int n; + if (family == StandardProtocolFamily.INET6) { + MembershipKeyImpl.Type6 key6 = + (MembershipKeyImpl.Type6)key; + n = Net.block6(fd, key6.group(), key6.index(), + Net.inet6AsByteArray(source)); + } else { + MembershipKeyImpl.Type4 key4 = + (MembershipKeyImpl.Type4)key; + n = Net.block4(fd, key4.group(), key4.interfaceAddress(), + Net.inet4AsInt(source)); + } + if (n == IOStatus.UNAVAILABLE) { + // ancient kernel + throw new UnsupportedOperationException(); + } + } + } + + /** + * Unblock given source. + */ + void unblock(MembershipKeyImpl key, InetAddress source) + throws IOException + { + assert key.getChannel() == this; + assert key.getSourceAddress() == null; + + synchronized (stateLock) { + if (!key.isValid()) + throw new IllegalStateException("key is no longer valid"); + + if (family == StandardProtocolFamily.INET6) { + MembershipKeyImpl.Type6 key6 = + (MembershipKeyImpl.Type6)key; + Net.unblock6(fd, key6.group(), key6.index(), + Net.inet6AsByteArray(source)); + } else { + MembershipKeyImpl.Type4 key4 = + (MembershipKeyImpl.Type4)key; + Net.unblock4(fd, key4.group(), key4.interfaceAddress(), + Net.inet4AsInt(source)); + } + } + } + protected void implCloseSelectableChannel() throws IOException { synchronized (stateLock) { nd.preClose(fd); + + // if member of mulitcast group then invalidate all keys + if (registry != null) + registry.invalidateAll(); + long th; if ((th = readerThread) != 0) NativeThread.signal(th); @@ -695,8 +1070,8 @@ boolean connected) throws IOException; - private native int send0(FileDescriptor fd, long address, int len, - SocketAddress sa) + private native int send0(boolean preferIPv6, FileDescriptor fd, long address, int len, + SocketAddress sa) throws IOException; static { diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/sun/nio/ch/DatagramSocketAdaptor.java --- a/jdk/src/share/classes/sun/nio/ch/DatagramSocketAdaptor.java Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/share/classes/sun/nio/ch/DatagramSocketAdaptor.java Sun Aug 31 18:39:01 2008 +0100 @@ -45,16 +45,9 @@ // The channel being adapted private final DatagramChannelImpl dc; - // Option adaptor object, created on demand - private volatile OptionAdaptor opts = null; - // Timeout "option" value for receives private volatile int timeout = 0; - // Traffic-class/Type-of-service - private volatile int trafficClass = 0; - - // ## super will create a useless impl private DatagramSocketAdaptor(DatagramChannelImpl dc) throws IOException { // Invoke the DatagramSocketAdaptor(SocketAddress) constructor, @@ -82,7 +75,7 @@ throw new IllegalArgumentException("connect: " + port); if (remote == null) throw new IllegalArgumentException("connect: null address"); - if (!isClosed()) + if (isClosed()) return; try { dc.connect(remote); @@ -124,11 +117,11 @@ } public boolean isBound() { - return dc.isBound(); + return dc.localAddress() != null; } public boolean isConnected() { - return dc.isConnected(); + return dc.remoteAddress() != null; } public InetAddress getInetAddress() { @@ -157,7 +150,7 @@ // Legacy DatagramSocket will send in this case // and set address and port of the packet InetSocketAddress isa = (InetSocketAddress) - dc.remoteAddress; + dc.remoteAddress(); p.setPort(isa.getPort()); p.setAddress(isa.getAddress()); dc.write(bb); @@ -241,21 +234,32 @@ public InetAddress getLocalAddress() { if (isClosed()) return null; - try { - return Net.asInetSocketAddress(dc.localAddress()).getAddress(); - } catch (Exception x) { - return new InetSocketAddress(0).getAddress(); + SocketAddress local = dc.localAddress(); + if (local == null) + local = new InetSocketAddress(0); + InetAddress result = ((InetSocketAddress)local).getAddress(); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + try { + sm.checkConnect(result.getHostAddress(), -1); + } catch (SecurityException x) { + return new InetSocketAddress(0).getAddress(); + } } + return result; } public int getLocalPort() { if (isClosed()) return -1; try { - return Net.asInetSocketAddress(dc.localAddress()).getPort(); + SocketAddress local = dc.getLocalAddress(); + if (local != null) { + return ((InetSocketAddress)local).getPort(); + } } catch (Exception x) { - return 0; } + return 0; } public void setSoTimeout(int timeout) throws SocketException { @@ -266,55 +270,87 @@ return timeout; } - private OptionAdaptor opts() { - if (opts == null) - opts = new OptionAdaptor(dc); - return opts; + private void setBooleanOption(SocketOption name, boolean value) + throws SocketException + { + try { + dc.setOption(name, value); + } catch (IOException x) { + Net.translateToSocketException(x); + } + } + + private void setIntOption(SocketOption name, int value) + throws SocketException + { + try { + dc.setOption(name, value); + } catch (IOException x) { + Net.translateToSocketException(x); + } + } + + private boolean getBooleanOption(SocketOption name) throws SocketException { + try { + return dc.getOption(name).booleanValue(); + } catch (IOException x) { + Net.translateToSocketException(x); + return false; // keep compiler happy + } + } + + private int getIntOption(SocketOption name) throws SocketException { + try { + return dc.getOption(name).intValue(); + } catch (IOException x) { + Net.translateToSocketException(x); + return -1; // keep compiler happy + } } public void setSendBufferSize(int size) throws SocketException { - opts().setSendBufferSize(size); + if (size <= 0) + throw new IllegalArgumentException("Invalid send size"); + setIntOption(StandardSocketOption.SO_SNDBUF, size); } public int getSendBufferSize() throws SocketException { - return opts().getSendBufferSize(); + return getIntOption(StandardSocketOption.SO_SNDBUF); } public void setReceiveBufferSize(int size) throws SocketException { - opts().setReceiveBufferSize(size); + if (size <= 0) + throw new IllegalArgumentException("Invalid receive size"); + setIntOption(StandardSocketOption.SO_RCVBUF, size); } public int getReceiveBufferSize() throws SocketException { - return opts().getReceiveBufferSize(); + return getIntOption(StandardSocketOption.SO_RCVBUF); } public void setReuseAddress(boolean on) throws SocketException { - opts().setReuseAddress(on); + setBooleanOption(StandardSocketOption.SO_REUSEADDR, on); } public boolean getReuseAddress() throws SocketException { - return opts().getReuseAddress(); + return getBooleanOption(StandardSocketOption.SO_REUSEADDR); + } public void setBroadcast(boolean on) throws SocketException { - opts().setBroadcast(on); + setBooleanOption(StandardSocketOption.SO_BROADCAST, on); } public boolean getBroadcast() throws SocketException { - return opts().getBroadcast(); + return getBooleanOption(StandardSocketOption.SO_BROADCAST); } public void setTrafficClass(int tc) throws SocketException { - opts().setTrafficClass(tc); - trafficClass = tc; + setIntOption(StandardSocketOption.IP_TOS, tc); } public int getTrafficClass() throws SocketException { - int tc = opts().getTrafficClass(); - if (tc < 0) { - tc = trafficClass; - } - return tc; + return getIntOption(StandardSocketOption.IP_TOS); } public void close() { diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/sun/nio/ch/ExtendedSocketOption.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/sun/nio/ch/ExtendedSocketOption.java Sun Aug 31 18:39:01 2008 +0100 @@ -0,0 +1,44 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.nio.ch; + +import java.net.SocketOption; + +/** + * Defines socket options that are supported by the implementation + * but not defined in StandardSocketOption. + */ + +class ExtendedSocketOption { + private ExtendedSocketOption() { } + + static final SocketOption SO_OOBINLINE = + new SocketOption() { + public String name() { return "SO_OOBINLINE"; } + public Class type() { return Boolean.class; } + public String toString() { return name(); } + }; +} diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/sun/nio/ch/MembershipKeyImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/sun/nio/ch/MembershipKeyImpl.java Sun Aug 31 18:39:01 2008 +0100 @@ -0,0 +1,222 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.nio.ch; + +import java.nio.channels.*; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.io.IOException; +import java.util.HashSet; + +/** + * MembershipKey implementation. + */ + +class MembershipKeyImpl + extends MembershipKey +{ + private final MulticastChannel ch; + private final InetAddress group; + private final NetworkInterface interf; + private final InetAddress source; + + // true when key is valid + private volatile boolean valid = true; + + // lock used when creating or accessing blockedSet + private Object stateLock = new Object(); + + // set of source addresses that are blocked + private HashSet blockedSet; + + private MembershipKeyImpl(MulticastChannel ch, + InetAddress group, + NetworkInterface interf, + InetAddress source) + { + this.ch = ch; + this.group = group; + this.interf = interf; + this.source = source; + } + + /** + * MembershipKey will additional context for IPv4 membership + */ + static class Type4 extends MembershipKeyImpl { + private final int groupAddress; + private final int interfAddress; + private final int sourceAddress; + + Type4(MulticastChannel ch, + InetAddress group, + NetworkInterface interf, + InetAddress source, + int groupAddress, + int interfAddress, + int sourceAddress) + { + super(ch, group, interf, source); + this.groupAddress = groupAddress; + this.interfAddress = interfAddress; + this.sourceAddress = sourceAddress; + } + + int group() { + return groupAddress; + } + + int interfaceAddress() { + return interfAddress; + } + + int source() { + return sourceAddress; + } + } + + /** + * MembershipKey will additional context for IPv6 membership + */ + static class Type6 extends MembershipKeyImpl { + private final byte[] groupAddress; + private final int index; + private final byte[] sourceAddress; + + Type6(MulticastChannel ch, + InetAddress group, + NetworkInterface interf, + InetAddress source, + byte[] groupAddress, + int index, + byte[] sourceAddress) + { + super(ch, group, interf, source); + this.groupAddress = groupAddress; + this.index = index; + this.sourceAddress = sourceAddress; + } + + byte[] group() { + return groupAddress; + } + + int index() { + return index; + } + + byte[] source() { + return sourceAddress; + } + } + + public boolean isValid() { + return valid; + } + + // package-private + void invalidate() { + valid = false; + } + + public void drop() throws IOException { + // delegate to channel + ((DatagramChannelImpl)ch).drop(this); + } + + @Override + public MulticastChannel getChannel() { + return ch; + } + + @Override + public InetAddress getGroup() { + return group; + } + + @Override + public NetworkInterface getNetworkInterface() { + return interf; + } + + @Override + public InetAddress getSourceAddress() { + return source; + } + + @Override + public MembershipKey block(InetAddress toBlock) + throws IOException + { + if (source != null) + throw new IllegalStateException("key is source-specific"); + + synchronized (stateLock) { + if ((blockedSet != null) && blockedSet.contains(toBlock)) { + // already blocked, nothing to do + return this; + } + + ((DatagramChannelImpl)ch).block(this, toBlock); + + // created blocked set if required and add source address + if (blockedSet == null) + blockedSet = new HashSet(); + blockedSet.add(toBlock); + } + return this; + } + + @Override + public MembershipKey unblock(InetAddress toUnblock) + throws IOException + { + synchronized (stateLock) { + if ((blockedSet == null) || !blockedSet.contains(toUnblock)) + throw new IllegalStateException("not blocked"); + + ((DatagramChannelImpl)ch).unblock(this, toUnblock); + + blockedSet.remove(toUnblock); + } + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(64); + sb.append('<'); + sb.append(group.getHostAddress()); + sb.append(','); + sb.append(interf.getName()); + if (source != null) { + sb.append(','); + sb.append(source.getHostAddress()); + } + sb.append('>'); + return sb.toString(); + } +} diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/sun/nio/ch/MembershipRegistry.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/sun/nio/ch/MembershipRegistry.java Sun Aug 31 18:39:01 2008 +0100 @@ -0,0 +1,129 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.nio.ch; + +import java.nio.channels.*; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.util.*; + +/** + * Simple registry of membership keys for a MulticastChannel. + * + * Instances of this object are not safe by multiple concurrent threads. + */ + +class MembershipRegistry { + + // map multicast group to keys + private Map> groups = null; + + MembershipRegistry() { + } + + /** + * Checks registry for membership of the group on the given + * network interface. + */ + MembershipKey checkMembership(InetAddress group, NetworkInterface interf, + InetAddress source) + { + if (groups != null) { + List keys = groups.get(group); + if (keys != null) { + for (MembershipKeyImpl key: keys) { + if (key.getNetworkInterface().equals(interf)) { + // already a member to receive all packets so return + // existing key or detect conflict + if (source == null) { + if (key.getSourceAddress() == null) + return key; + throw new IllegalStateException("Already a member to receive all packets"); + } + + // already have source-specific membership so return key + // or detect conflict + if (key.getSourceAddress() == null) + throw new IllegalStateException("Already have source-specific membership"); + if (source.equals(key.getSourceAddress())) + return key; + } + } + } + } + return null; + } + + /** + * Add membership to the registry, returning a new membership key. + */ + void add(MembershipKeyImpl key) { + InetAddress group = key.getGroup(); + List keys; + if (groups == null) { + groups = new HashMap>(); + keys = null; + } else { + keys = groups.get(group); + } + if (keys == null) { + keys = new LinkedList(); + groups.put(group, keys); + } + keys.add(key); + } + + /** + * Remove a key from the registry + */ + void remove(MembershipKeyImpl key) { + InetAddress group = key.getGroup(); + List keys = groups.get(group); + if (keys != null) { + Iterator i = keys.iterator(); + while (i.hasNext()) { + if (i.next() == key) { + i.remove(); + break; + } + } + if (keys.isEmpty()) { + groups.remove(group); + } + } + } + + /** + * Invalidate all keys in the registry + */ + void invalidateAll() { + for (InetAddress group: groups.keySet()) { + for (MembershipKeyImpl key: groups.get(group)) { + key.invalidate(); + } + } + } +} diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/sun/nio/ch/Net.java --- a/jdk/src/share/classes/sun/nio/ch/Net.java Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/share/classes/sun/nio/ch/Net.java Sun Aug 31 18:39:01 2008 +0100 @@ -26,21 +26,43 @@ package sun.nio.ch; import java.io.*; -import java.lang.reflect.*; import java.net.*; import java.nio.channels.*; +import java.util.*; +import java.security.AccessController; +import java.security.PrivilegedAction; class Net { // package-private private Net() { } + // unspecified protocol family + static final ProtocolFamily UNSPEC = new ProtocolFamily() { + public String name() { + return "UNSPEC"; + } + }; // -- Miscellaneous utilities -- + private static volatile boolean checkedIPv6 = false; + private static volatile boolean isIPv6Available; + + /** + * Tells whether dual-IPv4/IPv6 sockets should be used. + */ + static boolean isIPv6Available() { + if (!checkedIPv6) { + isIPv6Available = isIPv6Available0(); + checkedIPv6 = true; + } + return isIPv6Available; + } + static InetSocketAddress checkAddress(SocketAddress sa) { if (sa == null) - throw new IllegalArgumentException(); + throw new NullPointerException(); if (!(sa instanceof InetSocketAddress)) throw new UnsupportedAddressTypeException(); // ## needs arg InetSocketAddress isa = (InetSocketAddress)sa; @@ -63,6 +85,8 @@ Exception nx = x; if (x instanceof ClosedChannelException) nx = new SocketException("Socket is closed"); + else if (x instanceof NotYetConnectedException) + nx = new SocketException("Socket is not connected"); else if (x instanceof AlreadyBoundException) nx = new SocketException("Already bound"); else if (x instanceof NotYetBoundException) @@ -105,73 +129,359 @@ translateException(x, false); } + /** + * Returns any IPv4 address of the given network interface, or + * null if the interface does not have any IPv4 addresses. + */ + static Inet4Address anyInet4Address(final NetworkInterface interf) { + return AccessController.doPrivileged(new PrivilegedAction() { + public Inet4Address run() { + Enumeration addrs = interf.getInetAddresses(); + while (addrs.hasMoreElements()) { + InetAddress addr = addrs.nextElement(); + if (addr instanceof Inet4Address) { + return (Inet4Address)addr; + } + } + return null; + } + }); + } + + /** + * Returns an IPv4 address as an int. + */ + static int inet4AsInt(InetAddress ia) { + if (ia instanceof Inet4Address) { + byte[] addr = ia.getAddress(); + int address = addr[3] & 0xFF; + address |= ((addr[2] << 8) & 0xFF00); + address |= ((addr[1] << 16) & 0xFF0000); + address |= ((addr[0] << 24) & 0xFF000000); + return address; + } + throw new AssertionError("Should not reach here"); + } + + /** + * Returns an InetAddress from the given IPv4 address + * represented as an int. + */ + static InetAddress inet4FromInt(int address) { + byte[] addr = new byte[4]; + addr[0] = (byte) ((address >>> 24) & 0xFF); + addr[1] = (byte) ((address >>> 16) & 0xFF); + addr[2] = (byte) ((address >>> 8) & 0xFF); + addr[3] = (byte) (address & 0xFF); + try { + return InetAddress.getByAddress(addr); + } catch (UnknownHostException uhe) { + throw new AssertionError("Should not reach here"); + } + } + + /** + * Returns an IPv6 address as a byte array + */ + static byte[] inet6AsByteArray(InetAddress ia) { + if (ia instanceof Inet6Address) { + return ia.getAddress(); + } + + // need to construct IPv4-mapped address + if (ia instanceof Inet4Address) { + byte[] ip4address = ia.getAddress(); + byte[] address = new byte[16]; + address[10] = (byte)0xff; + address[11] = (byte)0xff; + address[12] = ip4address[0]; + address[13] = ip4address[1]; + address[14] = ip4address[2]; + address[15] = ip4address[3]; + return address; + } + + throw new AssertionError("Should not reach here"); + } + + // -- Socket options + + static void setSocketOption(FileDescriptor fd, ProtocolFamily family, + SocketOption name, Object value) + throws IOException + { + if (value == null) + throw new IllegalArgumentException("Invalid option value"); + + // only simple values supported by this method + Class type = name.type(); + if (type != Integer.class && type != Boolean.class) + throw new AssertionError("Should not reach here"); + + // special handling + if (name == StandardSocketOption.SO_RCVBUF || + name == StandardSocketOption.SO_SNDBUF) + { + int i = ((Integer)value).intValue(); + if (i < 0) + throw new IllegalArgumentException("Invalid send/receive buffer size"); + } + if (name == StandardSocketOption.SO_LINGER) { + int i = ((Integer)value).intValue(); + if (i < 0) + value = Integer.valueOf(-1); + if (i > 65535) + value = Integer.valueOf(65535); + } + if (name == StandardSocketOption.IP_TOS) { + int i = ((Integer)value).intValue(); + if (i < 0 || i > 255) + throw new IllegalArgumentException("Invalid IP_TOS value"); + } + if (name == StandardSocketOption.IP_MULTICAST_TTL) { + int i = ((Integer)value).intValue(); + if (i < 0 || i > 255) + throw new IllegalArgumentException("Invalid TTL/hop value"); + } + + // map option name to platform level/name + OptionKey key = SocketOptionRegistry.findOption(name, family); + if (key == null) + throw new AssertionError("Option not found"); + + int arg; + if (type == Integer.class) { + arg = ((Integer)value).intValue(); + } else { + boolean b = ((Boolean)value).booleanValue(); + arg = (b) ? 1 : 0; + } + + boolean mayNeedConversion = (family == UNSPEC); + setIntOption0(fd, mayNeedConversion, key.level(), key.name(), arg); + } + + static Object getSocketOption(FileDescriptor fd, ProtocolFamily family, + SocketOption name) + throws IOException + { + Class type = name.type(); + + // only simple values supported by this method + if (type != Integer.class && type != Boolean.class) + throw new AssertionError("Should not reach here"); + + // map option name to platform level/name + OptionKey key = SocketOptionRegistry.findOption(name, family); + if (key == null) + throw new AssertionError("Option not found"); + + boolean mayNeedConversion = (family == UNSPEC); + int value = getIntOption0(fd, mayNeedConversion, key.level(), key.name()); + + if (type == Integer.class) { + return Integer.valueOf(value); + } else { + return (value == 0) ? Boolean.FALSE : Boolean.TRUE; + } + } // -- Socket operations -- + static native boolean isIPv6Available0(); + static FileDescriptor socket(boolean stream) { - return IOUtil.newFD(socket0(stream, false)); + return socket(UNSPEC, stream); + } + + static FileDescriptor socket(ProtocolFamily family, boolean stream) { + boolean preferIPv6 = isIPv6Available() && + (family != StandardProtocolFamily.INET); + return IOUtil.newFD(socket0(preferIPv6, stream, false)); } static FileDescriptor serverSocket(boolean stream) { - return IOUtil.newFD(socket0(stream, true)); + return IOUtil.newFD(socket0(isIPv6Available(), stream, true)); } // Due to oddities SO_REUSEADDR on windows reuse is ignored - private static native int socket0(boolean stream, boolean reuse); + private static native int socket0(boolean preferIPv6, boolean stream, boolean reuse); + + static void bind(FileDescriptor fd, InetAddress addr, int port) + throws IOException + { + bind(UNSPEC, fd, addr, port); + } - static native void bind(FileDescriptor fd, InetAddress addr, int port) + static void bind(ProtocolFamily family, FileDescriptor fd, + InetAddress addr, int port) throws IOException + { + boolean preferIPv6 = isIPv6Available() && + (family != StandardProtocolFamily.INET); + bind0(preferIPv6, fd, addr, port); + } + + private static native void bind0(boolean preferIPv6, FileDescriptor fd, + InetAddress addr, int port) throws IOException; - static native int connect(FileDescriptor fd, - InetAddress remote, - int remotePort, - int trafficClass) + static native void listen(FileDescriptor fd, int backlog) throws IOException; + + static int connect(FileDescriptor fd, InetAddress remote, int remotePort) + throws IOException + { + return connect(UNSPEC, fd, remote, remotePort); + } + + static int connect(ProtocolFamily family, FileDescriptor fd, InetAddress remote, int remotePort) + throws IOException + { + boolean preferIPv6 = isIPv6Available() && + (family != StandardProtocolFamily.INET); + return connect0(preferIPv6, fd, remote, remotePort); + } + + private static native int connect0(boolean preferIPv6, + FileDescriptor fd, + InetAddress remote, + int remotePort) throws IOException; + public final static int SHUT_RD = 0; + public final static int SHUT_WR = 1; + public final static int SHUT_RDWR = 2; + + static native void shutdown(FileDescriptor fd, int how) throws IOException; + private static native int localPort(FileDescriptor fd) throws IOException; private static native InetAddress localInetAddress(FileDescriptor fd) throws IOException; - static InetSocketAddress localAddress(FileDescriptor fd) { - try { - return new InetSocketAddress(localInetAddress(fd), - localPort(fd)); - } catch (IOException x) { - throw new Error(x); // Can't happen - } + static InetSocketAddress localAddress(FileDescriptor fd) + throws IOException + { + return new InetSocketAddress(localInetAddress(fd), localPort(fd)); + } + + private static native int remotePort(FileDescriptor fd) + throws IOException; + + private static native InetAddress remoteInetAddress(FileDescriptor fd) + throws IOException; + + static InetSocketAddress remoteAddress(FileDescriptor fd) + throws IOException + { + return new InetSocketAddress(remoteInetAddress(fd), remotePort(fd)); } - static int localPortNumber(FileDescriptor fd) { - try { - return localPort(fd); - } catch (IOException x) { - throw new Error(x); // Can't happen - } + private static native int getIntOption0(FileDescriptor fd, boolean mayNeedConversion, + int level, int opt) + throws IOException; + + private static native void setIntOption0(FileDescriptor fd, boolean mayNeedConversion, + int level, int opt, int arg) + throws IOException; + + // -- Multicast support -- + + + /** + * Join IPv4 multicast group + */ + static int join4(FileDescriptor fd, int group, int interf, int source) + throws IOException + { + return joinOrDrop4(true, fd, group, interf, source); + } + + /** + * Drop membership of IPv4 multicast group + */ + static void drop4(FileDescriptor fd, int group, int interf, int source) + throws IOException + { + joinOrDrop4(false, fd, group, interf, source); + } + + private static native int joinOrDrop4(boolean join, FileDescriptor fd, int group, int interf, int source) + throws IOException; + + /** + * Block IPv4 source + */ + static int block4(FileDescriptor fd, int group, int interf, int source) + throws IOException + { + return blockOrUnblock4(true, fd, group, interf, source); } - private static native int getIntOption0(FileDescriptor fd, int opt) + /** + * Unblock IPv6 source + */ + static void unblock4(FileDescriptor fd, int group, int interf, int source) + throws IOException + { + blockOrUnblock4(false, fd, group, interf, source); + } + + private static native int blockOrUnblock4(boolean block, FileDescriptor fd, int group, + int interf, int source) throws IOException; - static int getIntOption(FileDescriptor fd, int opt) + /** + * Join IPv6 multicast group + */ + static int join6(FileDescriptor fd, byte[] group, int index, byte[] source) throws IOException { - return getIntOption0(fd, opt); + return joinOrDrop6(true, fd, group, index, source); + } + + /** + * Drop membership of IPv6 multicast group + */ + static void drop6(FileDescriptor fd, byte[] group, int index, byte[] source) + throws IOException + { + joinOrDrop6(false, fd, group, index, source); } - - private static native void setIntOption0(FileDescriptor fd, - int opt, int arg) + private static native int joinOrDrop6(boolean join, FileDescriptor fd, byte[] group, int index, byte[] source) throws IOException; - static void setIntOption(FileDescriptor fd, int opt, int arg) + /** + * Block IPv6 source + */ + static int block6(FileDescriptor fd, byte[] group, int index, byte[] source) throws IOException { - setIntOption0(fd, opt, arg); + return blockOrUnblock6(true, fd, group, index, source); + } + + /** + * Unblock IPv6 source + */ + static void unblock6(FileDescriptor fd, byte[] group, int index, byte[] source) + throws IOException + { + blockOrUnblock6(false, fd, group, index, source); } + static native int blockOrUnblock6(boolean block, FileDescriptor fd, byte[] group, int index, byte[] source) + throws IOException; + + static native void setInterface4(FileDescriptor fd, int interf) throws IOException; + + static native int getInterface4(FileDescriptor fd) throws IOException; + + static native void setInterface6(FileDescriptor fd, int index) throws IOException; + + static native int getInterface6(FileDescriptor fd) throws IOException; + private static native void initIDs(); static { diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/sun/nio/ch/OptionAdaptor.java --- a/jdk/src/share/classes/sun/nio/ch/OptionAdaptor.java Sun Aug 31 18:32:59 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,229 +0,0 @@ -/* - * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - */ - -package sun.nio.ch; - -import java.io.*; -import java.net.*; -import java.nio.*; -import java.nio.channels.*; - - -// Adaptor class for java.net-style options -// -// The option get/set methods in the socket, server-socket, and datagram-socket -// adaptors delegate to an instance of this class. -// - -class OptionAdaptor { // package-private - - private final SocketOpts.IP opts; - - OptionAdaptor(SocketChannelImpl sc) { - opts = (SocketOpts.IP)sc.options(); - } - - OptionAdaptor(ServerSocketChannelImpl ssc) { - opts = (SocketOpts.IP)ssc.options(); - } - - OptionAdaptor(DatagramChannelImpl dc) { - opts = (SocketOpts.IP)dc.options(); - } - - private SocketOpts.IP opts() { - return opts; - } - - private SocketOpts.IP.TCP tcpOpts() { - return (SocketOpts.IP.TCP)opts; - } - - public void setTcpNoDelay(boolean on) throws SocketException { - try { - tcpOpts().noDelay(on); - } catch (Exception x) { - Net.translateToSocketException(x); - } - } - - public boolean getTcpNoDelay() throws SocketException { - try { - return tcpOpts().noDelay(); - } catch (Exception x) { - Net.translateToSocketException(x); - return false; // Never happens - } - } - - public void setSoLinger(boolean on, int linger) throws SocketException { - try { - if (linger > 65535) - linger = 65535; - opts().linger(on ? linger : -1); - } catch (Exception x) { - Net.translateToSocketException(x); - } - } - - public int getSoLinger() throws SocketException { - try { - return opts().linger(); - } catch (Exception x) { - Net.translateToSocketException(x); - return 0; // Never happens - } - } - - public void setOOBInline(boolean on) throws SocketException { - try { - opts().outOfBandInline(on); - } catch (Exception x) { - Net.translateToSocketException(x); - } - } - - public boolean getOOBInline() throws SocketException { - try { - return opts().outOfBandInline(); - } catch (Exception x) { - Net.translateToSocketException(x); - return false; // Never happens - } - } - - public void setSendBufferSize(int size) - throws SocketException - { - try { - opts().sendBufferSize(size); - } catch (Exception x) { - Net.translateToSocketException(x); - } - } - - public int getSendBufferSize() throws SocketException { - try { - return opts().sendBufferSize(); - } catch (Exception x) { - Net.translateToSocketException(x); - return 0; // Never happens - } - } - - public void setReceiveBufferSize(int size) - throws SocketException - { - try { - opts().receiveBufferSize(size); - } catch (Exception x) { - Net.translateToSocketException(x); - } - } - - public int getReceiveBufferSize() throws SocketException { - try { - return opts().receiveBufferSize(); - } catch (Exception x) { - Net.translateToSocketException(x); - return 0; // Never happens - } - } - - public void setKeepAlive(boolean on) throws SocketException { - try { - opts().keepAlive(on); - } catch (Exception x) { - Net.translateToSocketException(x); - } - } - - public boolean getKeepAlive() throws SocketException { - try { - return opts().keepAlive(); - } catch (Exception x) { - Net.translateToSocketException(x); - return false; // Never happens - } - } - - public void setTrafficClass(int tc) throws SocketException { - if (tc < 0 || tc > 255) - throw new IllegalArgumentException("tc is not in range 0 -- 255"); - try { - opts().typeOfService(tc); - } catch (Exception x) { - Net.translateToSocketException(x); - } - } - - public int getTrafficClass() throws SocketException { - try { - return opts().typeOfService(); - } catch (Exception x) { - Net.translateToSocketException(x); - return 0; // Never happens - } - } - - public void setReuseAddress(boolean on) - throws SocketException - { - try { - opts().reuseAddress(on); - } catch (Exception x) { - Net.translateToSocketException(x); - } - } - - public boolean getReuseAddress() throws SocketException { - try { - return opts().reuseAddress(); - } catch (Exception x) { - Net.translateToSocketException(x); - return false; // Never happens - } - } - - public void setBroadcast(boolean on) - throws SocketException - { - try { - opts().broadcast(on); - } catch (Exception x) { - Net.translateToSocketException(x); - } - } - - public boolean getBroadcast() throws SocketException { - try { - return opts().broadcast(); - } catch (Exception x) { - Net.translateToSocketException(x); - return false; // Never happens - } - } - -} diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/sun/nio/ch/OptionKey.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/sun/nio/ch/OptionKey.java Sun Aug 31 18:39:01 2008 +0100 @@ -0,0 +1,48 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.nio.ch; + +/** + * Represents the level/name of a socket option + */ + +class OptionKey { + private int level; + private int name; + + OptionKey(int level, int name) { + this.level = level; + this.name = name; + } + + int level() { + return level; + } + + int name() { + return name; + } +} diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/sun/nio/ch/SelectorProviderImpl.java --- a/jdk/src/share/classes/sun/nio/ch/SelectorProviderImpl.java Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/share/classes/sun/nio/ch/SelectorProviderImpl.java Sun Aug 31 18:39:01 2008 +0100 @@ -29,6 +29,7 @@ import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; +import java.net.ProtocolFamily; import java.nio.channels.*; import java.nio.channels.spi.*; @@ -41,6 +42,10 @@ return new DatagramChannelImpl(this); } + public DatagramChannel openDatagramChannel(ProtocolFamily family) throws IOException { + return new DatagramChannelImpl(this, family); + } + public Pipe openPipe() throws IOException { return new PipeImpl(this); } @@ -54,5 +59,4 @@ public SocketChannel openSocketChannel() throws IOException { return new SocketChannelImpl(this); } - } diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/sun/nio/ch/ServerSocketAdaptor.java --- a/jdk/src/share/classes/sun/nio/ch/ServerSocketAdaptor.java Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/share/classes/sun/nio/ch/ServerSocketAdaptor.java Sun Aug 31 18:39:01 2008 +0100 @@ -44,9 +44,6 @@ // The channel being adapted private final ServerSocketChannelImpl ssc; - // Option adaptor object, created on demand - private volatile OptionAdaptor opts = null; - // Timeout "option" value for accepts private volatile int timeout = 0; @@ -174,18 +171,21 @@ return timeout; } - private OptionAdaptor opts() { - if (opts == null) - opts = new OptionAdaptor(ssc); - return opts; - } - public void setReuseAddress(boolean on) throws SocketException { - opts().setReuseAddress(on); + try { + ssc.setOption(StandardSocketOption.SO_REUSEADDR, on); + } catch (IOException x) { + Net.translateToSocketException(x); + } } public boolean getReuseAddress() throws SocketException { - return opts().getReuseAddress(); + try { + return ssc.getOption(StandardSocketOption.SO_REUSEADDR).booleanValue(); + } catch (IOException x) { + Net.translateToSocketException(x); + return false; // Never happens + } } public String toString() { @@ -197,11 +197,23 @@ } public void setReceiveBufferSize(int size) throws SocketException { - opts().setReceiveBufferSize(size); + // size 0 valid for ServerSocketChannel, invalid for ServerSocket + if (size <= 0) + throw new IllegalArgumentException("size cannot be 0 or negative"); + try { + ssc.setOption(StandardSocketOption.SO_RCVBUF, size); + } catch (IOException x) { + Net.translateToSocketException(x); + } } public int getReceiveBufferSize() throws SocketException { - return opts().getReceiveBufferSize(); + try { + return ssc.getOption(StandardSocketOption.SO_RCVBUF).intValue(); + } catch (IOException x) { + Net.translateToSocketException(x); + return -1; // Never happens + } } } diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java --- a/jdk/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java Sun Aug 31 18:39:01 2008 +0100 @@ -33,8 +33,7 @@ import java.nio.channels.spi.*; import java.security.AccessController; import java.security.PrivilegedAction; -import java.util.HashSet; -import java.util.Iterator; +import java.util.*; /** @@ -75,10 +74,7 @@ private int state = ST_UNINITIALIZED; // Binding - private SocketAddress localAddress = null; // null => unbound - - // Options, created on demand - private SocketOpts.IP.TCP options = null; + private SocketAddress localAddress; // null => unbound // Our socket adaptor, if any ServerSocket socket; @@ -103,7 +99,6 @@ localAddress = Net.localAddress(fd); } - public ServerSocket socket() { synchronized (stateLock) { if (socket == null) @@ -112,6 +107,69 @@ } } + @Override + public SocketAddress getLocalAddress() throws IOException { + synchronized (stateLock) { + if (!isOpen()) + return null; + return localAddress; + } + } + + @Override + public ServerSocketChannel setOption(SocketOption name, Object value) + throws IOException + { + if (name == null) + throw new NullPointerException(); + if (!options().contains(name)) + throw new IllegalArgumentException("invalid option name"); + + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + + // no options that require special handling + Net.setSocketOption(fd, Net.UNSPEC, name, value); + return this; + } + } + + @Override + @SuppressWarnings("unchecked") + public T getOption(SocketOption name) + throws IOException + { + if (name == null) + throw new NullPointerException(); + if (!options().contains(name)) + throw new IllegalArgumentException("invalid option name"); + + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + + // no options that require special handling + return (T) Net.getSocketOption(fd, Net.UNSPEC, name); + } + } + + private static class LazyInitialization { + static final Set> defaultOptions = defaultOptions(); + + private static Set> defaultOptions() { + HashSet> set = new HashSet>(2); + set.add(StandardSocketOption.SO_RCVBUF); + set.add(StandardSocketOption.SO_REUSEADDR); + return Collections.unmodifiableSet(set); + } + } + + @Override + public final Set> options() { + return LazyInitialization.defaultOptions; + } + public boolean isBound() { synchronized (stateLock) { return localAddress != null; @@ -124,22 +182,25 @@ } } - public void bind(SocketAddress local, int backlog) throws IOException { + @Override + public ServerSocketChannel bind(SocketAddress local, int backlog) throws IOException { synchronized (lock) { if (!isOpen()) throw new ClosedChannelException(); if (isBound()) throw new AlreadyBoundException(); - InetSocketAddress isa = Net.checkAddress(local); + InetSocketAddress isa = (local == null) ? new InetSocketAddress(0) : + Net.checkAddress(local); SecurityManager sm = System.getSecurityManager(); if (sm != null) sm.checkListen(isa.getPort()); Net.bind(fd, isa.getAddress(), isa.getPort()); - listen(fd, backlog < 1 ? 50 : backlog); + Net.listen(fd, backlog < 1 ? 50 : backlog); synchronized (stateLock) { localAddress = Net.localAddress(fd); } } + return this; } public SocketChannel accept() throws IOException { @@ -196,24 +257,6 @@ IOUtil.configureBlocking(fd, block); } - public SocketOpts options() { - synchronized (stateLock) { - if (options == null) { - SocketOptsImpl.Dispatcher d - = new SocketOptsImpl.Dispatcher() { - int getInt(int opt) throws IOException { - return Net.getIntOption(fd, opt); - } - void setInt(int opt, int arg) throws IOException { - Net.setIntOption(fd, opt, arg); - } - }; - options = new SocketOptsImpl.IP.TCP(d); - } - return options; - } - } - protected void implCloseSelectableChannel() throws IOException { synchronized (stateLock) { nd.preClose(fd); @@ -320,9 +363,6 @@ // -- Native methods -- - private static native void listen(FileDescriptor fd, int backlog) - throws IOException; - // Accepts a new connection, setting the given file descriptor to refer to // the new socket and setting isaa[0] to the socket's remote address. // Returns 1 on success, or IOStatus.UNAVAILABLE (if non-blocking and no diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/sun/nio/ch/SocketAdaptor.java --- a/jdk/src/share/classes/sun/nio/ch/SocketAdaptor.java Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/share/classes/sun/nio/ch/SocketAdaptor.java Sun Aug 31 18:39:01 2008 +0100 @@ -54,16 +54,9 @@ // The channel being adapted private final SocketChannelImpl sc; - // Option adaptor object, created on demand - private volatile OptionAdaptor opts = null; - // Timeout "option" value for reads private volatile int timeout = 0; - // Traffic-class/Type-of-service - private volatile int trafficClass = 0; - - // ## super will create a useless impl private SocketAdaptor(SocketChannelImpl sc) { this.sc = sc; @@ -145,8 +138,6 @@ public void bind(SocketAddress local) throws IOException { try { - if (local == null) - local = new InetSocketAddress(0); sc.bind(local); } catch (Exception x) { Net.translateException(x); @@ -154,27 +145,39 @@ } public InetAddress getInetAddress() { - if (!sc.isConnected()) + SocketAddress remote = sc.remoteAddress(); + if (remote == null) { return null; - return Net.asInetSocketAddress(sc.remoteAddress()).getAddress(); + } else { + return ((InetSocketAddress)remote).getAddress(); + } } public InetAddress getLocalAddress() { - if (!sc.isBound()) - return new InetSocketAddress(0).getAddress(); - return Net.asInetSocketAddress(sc.localAddress()).getAddress(); + if (sc.isOpen()) { + SocketAddress local = sc.localAddress(); + if (local != null) + return ((InetSocketAddress)local).getAddress(); + } + return new InetSocketAddress(0).getAddress(); } public int getPort() { - if (!sc.isConnected()) + SocketAddress remote = sc.remoteAddress(); + if (remote == null) { return 0; - return Net.asInetSocketAddress(sc.remoteAddress()).getPort(); + } else { + return ((InetSocketAddress)remote).getPort(); + } } public int getLocalPort() { - if (!sc.isBound()) + SocketAddress local = sc.localAddress(); + if (local == null) { return -1; - return Net.asInetSocketAddress(sc.localAddress()).getPort(); + } else { + return ((InetSocketAddress)local).getPort(); + } } private class SocketInputStream @@ -276,26 +279,60 @@ return os; } - private OptionAdaptor opts() { - if (opts == null) - opts = new OptionAdaptor(sc); - return opts; + private void setBooleanOption(SocketOption name, boolean value) + throws SocketException + { + try { + sc.setOption(name, value); + } catch (IOException x) { + Net.translateToSocketException(x); + } + } + + private void setIntOption(SocketOption name, int value) + throws SocketException + { + try { + sc.setOption(name, value); + } catch (IOException x) { + Net.translateToSocketException(x); + } + } + + private boolean getBooleanOption(SocketOption name) throws SocketException { + try { + return sc.getOption(name).booleanValue(); + } catch (IOException x) { + Net.translateToSocketException(x); + return false; // keep compiler happy + } + } + + private int getIntOption(SocketOption name) throws SocketException { + try { + return sc.getOption(name).intValue(); + } catch (IOException x) { + Net.translateToSocketException(x); + return -1; // keep compiler happy + } } public void setTcpNoDelay(boolean on) throws SocketException { - opts().setTcpNoDelay(on); + setBooleanOption(StandardSocketOption.TCP_NODELAY, on); } public boolean getTcpNoDelay() throws SocketException { - return opts().getTcpNoDelay(); + return getBooleanOption(StandardSocketOption.TCP_NODELAY); } public void setSoLinger(boolean on, int linger) throws SocketException { - opts().setSoLinger(on, linger); + if (!on) + linger = -1; + setIntOption(StandardSocketOption.SO_LINGER, linger); } public int getSoLinger() throws SocketException { - return opts().getSoLinger(); + return getIntOption(StandardSocketOption.SO_LINGER); } public void sendUrgentData(int data) throws IOException { @@ -303,11 +340,11 @@ } public void setOOBInline(boolean on) throws SocketException { - opts().setOOBInline(on); + setBooleanOption(ExtendedSocketOption.SO_OOBINLINE, on); } public boolean getOOBInline() throws SocketException { - return opts().getOOBInline(); + return getBooleanOption(ExtendedSocketOption.SO_OOBINLINE); } public void setSoTimeout(int timeout) throws SocketException { @@ -321,48 +358,49 @@ } public void setSendBufferSize(int size) throws SocketException { - opts().setSendBufferSize(size); + // size 0 valid for SocketChannel, invalid for Socket + if (size <= 0) + throw new IllegalArgumentException("Invalid send size"); + setIntOption(StandardSocketOption.SO_SNDBUF, size); } public int getSendBufferSize() throws SocketException { - return opts().getSendBufferSize(); + return getIntOption(StandardSocketOption.SO_SNDBUF); } public void setReceiveBufferSize(int size) throws SocketException { - opts().setReceiveBufferSize(size); + // size 0 valid for SocketChannel, invalid for Socket + if (size <= 0) + throw new IllegalArgumentException("Invalid receive size"); + setIntOption(StandardSocketOption.SO_RCVBUF, size); } public int getReceiveBufferSize() throws SocketException { - return opts().getReceiveBufferSize(); + return getIntOption(StandardSocketOption.SO_RCVBUF); } public void setKeepAlive(boolean on) throws SocketException { - opts().setKeepAlive(on); + setBooleanOption(StandardSocketOption.SO_KEEPALIVE, on); } public boolean getKeepAlive() throws SocketException { - return opts().getKeepAlive(); + return getBooleanOption(StandardSocketOption.SO_KEEPALIVE); } public void setTrafficClass(int tc) throws SocketException { - opts().setTrafficClass(tc); - trafficClass = tc; + setIntOption(StandardSocketOption.IP_TOS, tc); } public int getTrafficClass() throws SocketException { - int tc = opts().getTrafficClass(); - if (tc < 0) { - tc = trafficClass; - } - return tc; + return getIntOption(StandardSocketOption.IP_TOS); } public void setReuseAddress(boolean on) throws SocketException { - opts().setReuseAddress(on); + setBooleanOption(StandardSocketOption.SO_REUSEADDR, on); } public boolean getReuseAddress() throws SocketException { - return opts().getReuseAddress(); + return getBooleanOption(StandardSocketOption.SO_REUSEADDR); } public void close() throws IOException { @@ -402,7 +440,7 @@ } public boolean isBound() { - return sc.isBound(); + return sc.localAddress() != null; } public boolean isClosed() { diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/sun/nio/ch/SocketChannelImpl.java --- a/jdk/src/share/classes/sun/nio/ch/SocketChannelImpl.java Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/share/classes/sun/nio/ch/SocketChannelImpl.java Sun Aug 31 18:39:01 2008 +0100 @@ -31,6 +31,7 @@ import java.nio.ByteBuffer; import java.nio.channels.*; import java.nio.channels.spi.*; +import java.util.*; /** @@ -78,19 +79,16 @@ private int state = ST_UNINITIALIZED; // Binding - private SocketAddress localAddress = null; - private SocketAddress remoteAddress = null; + private SocketAddress localAddress; + private SocketAddress remoteAddress; // Input/Output open private boolean isInputOpen = true; private boolean isOutputOpen = true; private boolean readyToConnect = false; - // Options, created on demand - private SocketOpts.IP.TCP options = null; - // Socket adaptor, created on demand - private Socket socket = null; + private Socket socket; // -- End of fields protected by stateLock @@ -114,6 +112,7 @@ this.fd = fd; this.fdVal = IOUtil.fdVal(fd); this.state = ST_CONNECTED; + this.localAddress = Net.localAddress(fd); this.remoteAddress = remote; } @@ -125,6 +124,98 @@ } } + @Override + public SocketAddress getLocalAddress() throws IOException { + synchronized (stateLock) { + if (!isOpen()) + return null; + return localAddress; + } + } + + @Override + public SocketAddress getConnectedAddress() throws IOException { + synchronized (stateLock) { + if (!isOpen()) + return null; + return remoteAddress; + } + } + + @Override + public SocketChannel setOption(SocketOption name, Object value) + throws IOException + { + if (name == null) + throw new NullPointerException(); + if (!options().contains(name)) + throw new IllegalArgumentException("Invalid option name"); + + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + + // special handling for IP_TOS: no-op when IPv6 + if (name == StandardSocketOption.IP_TOS) { + if (!Net.isIPv6Available()) + Net.setSocketOption(fd, StandardProtocolFamily.INET, name, value); + return this; + } + + // no options that require special handling + Net.setSocketOption(fd, Net.UNSPEC, name, value); + return this; + } + } + + @Override + @SuppressWarnings("unchecked") + public T getOption(SocketOption name) + throws IOException + { + if (name == null) + throw new NullPointerException(); + if (!options().contains(name)) + throw new IllegalArgumentException("Invalid option name"); + + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + + // special handling for IP_TOS: always return 0 when IPv6 + if (name == StandardSocketOption.IP_TOS) { + return (Net.isIPv6Available()) ? (T) Integer.valueOf(0) : + (T) Net.getSocketOption(fd, StandardProtocolFamily.INET, name); + } + + // no options that require special handling + return (T) Net.getSocketOption(fd, Net.UNSPEC, name); + } + } + + private static class LazyInitialization { + static final Set> defaultOptions = defaultOptions(); + + private static Set> defaultOptions() { + HashSet> set = new HashSet>(8); + set.add(StandardSocketOption.SO_SNDBUF); + set.add(StandardSocketOption.SO_RCVBUF); + set.add(StandardSocketOption.SO_KEEPALIVE); + set.add(StandardSocketOption.SO_REUSEADDR); + set.add(StandardSocketOption.SO_LINGER); + set.add(StandardSocketOption.TCP_NODELAY); + // additional options required by socket adaptor + set.add(StandardSocketOption.IP_TOS); + set.add(ExtendedSocketOption.SO_OOBINLINE); + return Collections.unmodifiableSet(set); + } + } + + @Override + public final Set> options() { + return LazyInitialization.defaultOptions; + } + private boolean ensureReadOpen() throws ClosedChannelException { synchronized (stateLock) { if (!isOpen()) @@ -410,43 +501,8 @@ IOUtil.configureBlocking(fd, block); } - public SocketOpts options() { - synchronized (stateLock) { - if (options == null) { - SocketOptsImpl.Dispatcher d - = new SocketOptsImpl.Dispatcher() { - int getInt(int opt) throws IOException { - return Net.getIntOption(fd, opt); - } - void setInt(int opt, int arg) - throws IOException - { - Net.setIntOption(fd, opt, arg); - } - }; - options = new SocketOptsImpl.IP.TCP(d); - } - return options; - } - } - - public boolean isBound() { - synchronized (stateLock) { - if (state == ST_CONNECTED) - return true; - return localAddress != null; - } - } - public SocketAddress localAddress() { synchronized (stateLock) { - if (state == ST_CONNECTED && - (localAddress == null || - ((InetSocketAddress)localAddress).getAddress().isAnyLocalAddress())) { - // Socket was not bound before connecting or - // Socket was bound with an "anyLocalAddress" - localAddress = Net.localAddress(fd); - } return localAddress; } } @@ -457,19 +513,25 @@ } } - public void bind(SocketAddress local) throws IOException { + @Override + public SocketChannel bind(SocketAddress local) throws IOException { synchronized (readLock) { synchronized (writeLock) { synchronized (stateLock) { - ensureOpenAndUnconnected(); + if (!isOpen()) + throw new ClosedChannelException(); + if (state == ST_PENDING) + throw new ConnectionPendingException(); if (localAddress != null) throw new AlreadyBoundException(); - InetSocketAddress isa = Net.checkAddress(local); + InetSocketAddress isa = (local == null) ? + new InetSocketAddress(0) : Net.checkAddress(local); Net.bind(fd, isa.getAddress(), isa.getPort()); localAddress = Net.localAddress(fd); } } } + return this; } public boolean isConnected() { @@ -496,7 +558,6 @@ } public boolean connect(SocketAddress sa) throws IOException { - int trafficClass = 0; // ## Pick up from options int localPort = 0; synchronized (readLock) { @@ -524,13 +585,24 @@ ia = InetAddress.getLocalHost(); n = Net.connect(fd, ia, - isa.getPort(), - trafficClass); + isa.getPort()); if ( (n == IOStatus.INTERRUPTED) && isOpen()) continue; break; } + + synchronized (stateLock) { + if (isOpen() && (localAddress == null) || + ((InetSocketAddress)localAddress) + .getAddress().isAnyLocalAddress()) + { + // Socket was not bound before connecting or + // Socket was bound with an "anyLocalAddress" + localAddress = Net.localAddress(fd); + } + } + } finally { readerCleanup(); end((n > 0) || (n == IOStatus.UNAVAILABLE)); @@ -646,29 +718,37 @@ } } - public final static int SHUT_RD = 0; - public final static int SHUT_WR = 1; - public final static int SHUT_RDWR = 2; - - public void shutdownInput() throws IOException { + @Override + public SocketChannel shutdownInput() throws IOException { synchronized (stateLock) { if (!isOpen()) throw new ClosedChannelException(); - isInputOpen = false; - shutdown(fd, SHUT_RD); - if (readerThread != 0) - NativeThread.signal(readerThread); + if (!isConnected()) + throw new NotYetConnectedException(); + if (isInputOpen) { + Net.shutdown(fd, Net.SHUT_RD); + if (readerThread != 0) + NativeThread.signal(readerThread); + isInputOpen = false; + } + return this; } } - public void shutdownOutput() throws IOException { + @Override + public SocketChannel shutdownOutput() throws IOException { synchronized (stateLock) { if (!isOpen()) throw new ClosedChannelException(); - isOutputOpen = false; - shutdown(fd, SHUT_WR); - if (writerThread != 0) - NativeThread.signal(writerThread); + if (!isConnected()) + throw new NotYetConnectedException(); + if (isOutputOpen) { + Net.shutdown(fd, Net.SHUT_WR); + if (writerThread != 0) + NativeThread.signal(writerThread); + isOutputOpen = false; + } + return this; } } @@ -869,9 +949,6 @@ boolean block, boolean ready) throws IOException; - private static native void shutdown(FileDescriptor fd, int how) - throws IOException; - static { Util.load(); nd = new SocketDispatcher(); diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/sun/nio/ch/SocketOpts.java --- a/jdk/src/share/classes/sun/nio/ch/SocketOpts.java Sun Aug 31 18:32:59 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,115 +0,0 @@ -/* - * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - */ - -package sun.nio.ch; - -import java.io.IOException; -import java.nio.*; -import java.net.NetworkInterface; - - -// Typical use: -// -// sc.options() -// .noDelay(true) -// .typeOfService(SocketOpts.IP.TOS_RELIABILITY) -// .sendBufferSize(1024) -// .receiveBufferSize(1024) -// .keepAlive(true); -// - - -public interface SocketOpts { // SocketOptions already used in java.net - - // Options that apply to all kinds of sockets - - // SO_BROADCAST - public abstract boolean broadcast() throws IOException; - public abstract SocketOpts broadcast(boolean b) throws IOException; - - // SO_KEEPALIVE - public abstract boolean keepAlive() throws IOException; - public abstract SocketOpts keepAlive(boolean b) throws IOException; - - // SO_LINGER - public abstract int linger() throws IOException; - public abstract SocketOpts linger(int n) throws IOException; - - // SO_OOBINLINE - public abstract boolean outOfBandInline() throws IOException; - public abstract SocketOpts outOfBandInline(boolean b) throws IOException; - - // SO_RCVBUF - public abstract int receiveBufferSize() throws IOException; - public abstract SocketOpts receiveBufferSize(int n) throws IOException; - - // SO_SNDBUF - public abstract int sendBufferSize() throws IOException; - public abstract SocketOpts sendBufferSize(int n) throws IOException; - - // SO_REUSEADDR - public abstract boolean reuseAddress() throws IOException; - public abstract SocketOpts reuseAddress(boolean b) throws IOException; - - - // IP-specific options - - public static interface IP - extends SocketOpts - { - - // IP_MULTICAST_IF2 - public abstract NetworkInterface multicastInterface() - throws IOException; - public abstract IP multicastInterface(NetworkInterface ni) - throws IOException; - - // IP_MULTICAST_LOOP - public abstract boolean multicastLoop() throws IOException; - public abstract IP multicastLoop(boolean b) throws IOException; - - // IP_TOS - public static final int TOS_LOWDELAY = 0x10; - public static final int TOS_THROUGHPUT = 0x08; - public static final int TOS_RELIABILITY = 0x04; - public static final int TOS_MINCOST = 0x02; - public abstract int typeOfService() throws IOException; - public abstract IP typeOfService(int tos) throws IOException; - - - // TCP-specific options - - public static interface TCP - extends IP - { - // TCP_NODELAY - public abstract boolean noDelay() throws IOException; - public abstract TCP noDelay(boolean b) throws IOException; - - } - - } - -} diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/sun/nio/ch/SocketOptsImpl.java --- a/jdk/src/share/classes/sun/nio/ch/SocketOptsImpl.java Sun Aug 31 18:32:59 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,318 +0,0 @@ -/* - * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - */ - -package sun.nio.ch; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.net.NetworkInterface; -import java.net.SocketOptions; -import java.nio.channels.*; - - -class SocketOptsImpl - implements SocketOpts -{ - - static abstract class Dispatcher { - abstract int getInt(int opt) throws IOException; - abstract void setInt(int opt, int arg) throws IOException; - // Others that pass addresses, etc., will come later - } - - private final Dispatcher d; - - SocketOptsImpl(Dispatcher d) { - this.d = d; - } - - protected boolean getBoolean(int opt) throws IOException { - return d.getInt(opt) > 0; - } - - protected void setBoolean(int opt, boolean b) throws IOException { - d.setInt(opt, b ? 1 : 0); - } - - protected int getInt(int opt) throws IOException { - return d.getInt(opt); - } - - protected void setInt(int opt, int n) throws IOException { - d.setInt(opt, n); - } - - protected NetworkInterface getNetworkInterface(int opt) - throws IOException - { - throw new UnsupportedOperationException("NYI"); - } - - protected void setNetworkInterface(int opt, NetworkInterface ni) - throws IOException - { - throw new UnsupportedOperationException("NYI"); - } - - protected void addToString(StringBuffer sb, String s) { - char c = sb.charAt(sb.length() - 1); - if ((c != '[') && (c != '=')) - sb.append(' '); - sb.append(s); - } - - protected void addToString(StringBuffer sb, int n) { - addToString(sb, Integer.toString(n)); - } - - - // SO_BROADCAST - - public boolean broadcast() throws IOException { - return getBoolean(SocketOptions.SO_BROADCAST); - } - - public SocketOpts broadcast(boolean b) throws IOException { - setBoolean(SocketOptions.SO_BROADCAST, b); - return this; - } - - - // SO_KEEPALIVE - - public boolean keepAlive() throws IOException { - return getBoolean(SocketOptions.SO_KEEPALIVE); - } - - public SocketOpts keepAlive(boolean b) throws IOException { - setBoolean(SocketOptions.SO_KEEPALIVE, b); - return this; - } - - - // SO_LINGER - - public int linger() throws IOException { - return getInt(SocketOptions.SO_LINGER); - } - - public SocketOpts linger(int n) throws IOException { - setInt(SocketOptions.SO_LINGER, n); - return this; - } - - - // SO_OOBINLINE - - public boolean outOfBandInline() throws IOException { - return getBoolean(SocketOptions.SO_OOBINLINE); - } - - public SocketOpts outOfBandInline(boolean b) throws IOException { - setBoolean(SocketOptions.SO_OOBINLINE, b); - return this; - } - - - // SO_RCVBUF - - public int receiveBufferSize() throws IOException { - return getInt(SocketOptions.SO_RCVBUF); - } - - public SocketOpts receiveBufferSize(int n) throws IOException { - if (n <= 0) - throw new IllegalArgumentException("Invalid receive size"); - setInt(SocketOptions.SO_RCVBUF, n); - return this; - } - - - // SO_SNDBUF - - public int sendBufferSize() throws IOException { - return getInt(SocketOptions.SO_SNDBUF); - } - - public SocketOpts sendBufferSize(int n) throws IOException { - if (n <= 0) - throw new IllegalArgumentException("Invalid send size"); - setInt(SocketOptions.SO_SNDBUF, n); - return this; - } - - - // SO_REUSEADDR - - public boolean reuseAddress() throws IOException { - return getBoolean(SocketOptions.SO_REUSEADDR); - } - - public SocketOpts reuseAddress(boolean b) throws IOException { - setBoolean(SocketOptions.SO_REUSEADDR, b); - return this; - } - - - // toString - - protected void toString(StringBuffer sb) throws IOException { - int n; - if (broadcast()) - addToString(sb, "broadcast"); - if (keepAlive()) - addToString(sb, "keepalive"); - if ((n = linger()) > 0) { - addToString(sb, "linger="); - addToString(sb, n); - } - if (outOfBandInline()) - addToString(sb, "oobinline"); - if ((n = receiveBufferSize()) > 0) { - addToString(sb, "rcvbuf="); - addToString(sb, n); - } - if ((n = sendBufferSize()) > 0) { - addToString(sb, "sndbuf="); - addToString(sb, n); - } - if (reuseAddress()) - addToString(sb, "reuseaddr"); - } - - public String toString() { - StringBuffer sb = new StringBuffer(); - sb.append(this.getClass().getInterfaces()[0].getName()); - sb.append('['); - int i = sb.length(); - try { - toString(sb); - } catch (IOException x) { - sb.setLength(i); - sb.append("closed"); - } - sb.append(']'); - return sb.toString(); - } - - - // IP-specific socket options - - static class IP - extends SocketOptsImpl - implements SocketOpts.IP - { - - IP(Dispatcher d) { - super(d); - } - - - // IP_MULTICAST_IF2 - // ## Do we need IP_MULTICAST_IF also? - - public NetworkInterface multicastInterface() throws IOException { - return getNetworkInterface(SocketOptions.IP_MULTICAST_IF2); - } - - public SocketOpts.IP multicastInterface(NetworkInterface ni) - throws IOException - { - setNetworkInterface(SocketOptions.IP_MULTICAST_IF2, ni); - return this; - } - - - // IP_MULTICAST_LOOP - - public boolean multicastLoop() throws IOException { - return getBoolean(SocketOptions.IP_MULTICAST_LOOP); - } - - public SocketOpts.IP multicastLoop(boolean b) throws IOException { - setBoolean(SocketOptions.IP_MULTICAST_LOOP, b); - return this; - } - - - // IP_TOS - - public int typeOfService() throws IOException { - return getInt(SocketOptions.IP_TOS); - } - - public SocketOpts.IP typeOfService(int tos) throws IOException { - setInt(SocketOptions.IP_TOS, tos); - return this; - } - - - // toString - - protected void toString(StringBuffer sb) throws IOException { - super.toString(sb); - int n; - if ((n = typeOfService()) > 0) { - addToString(sb, "tos="); - addToString(sb, n); - } - } - - - // TCP-specific IP options - - public static class TCP - extends SocketOptsImpl.IP - implements SocketOpts.IP.TCP - { - - TCP(Dispatcher d) { - super(d); - } - - // TCP_NODELAY - - public boolean noDelay() throws IOException { - return getBoolean(SocketOptions.TCP_NODELAY); - } - - public SocketOpts.IP.TCP noDelay(boolean b) throws IOException { - setBoolean(SocketOptions.TCP_NODELAY, b); - return this; - } - - - // toString - - protected void toString(StringBuffer sb) throws IOException { - super.toString(sb); - if (noDelay()) - addToString(sb, "nodelay"); - } - - } - } - -} diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/classes/sun/nio/ch/exceptions --- a/jdk/src/share/classes/sun/nio/ch/exceptions Sun Aug 31 18:32:59 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,39 +0,0 @@ -# -# Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Sun designates this -# particular file as subject to the "Classpath" exception as provided -# by Sun in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, -# CA 95054 USA or visit www.sun.com if you need additional information or -# have any questions. -# - -# Generated exception classes for sun.nio.ch - -SINCE=1.4 -PACKAGE=sun.nio.ch -# This year should only change if the generated source is modified. -COPYRIGHT_YEARS=2000-2007 - - -SUPER=IllegalStateException - -gen AlreadyBoundException " - * Unchecked exception thrown when an attempt is made to bind a {@link - * SocketChannel} that is already bound." \ - 9002280723481772026L diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/native/java/net/net_util.c --- a/jdk/src/share/native/java/net/net_util.c Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/share/native/java/net/net_util.c Sun Aug 31 18:39:01 2008 +0100 @@ -82,7 +82,7 @@ } } -jobject +JNIEXPORT jobject JNICALL NET_SockaddrToInetAddress(JNIEnv *env, struct sockaddr *him, int *port) { jobject iaObj; init(env); @@ -159,7 +159,7 @@ return iaObj; } -jint +JNIEXPORT jint JNICALL NET_SockaddrEqualsInetAddress(JNIEnv *env, struct sockaddr *him, jobject iaObj) { jint family = (*env)->GetIntField(env, iaObj, ia_familyID) == IPv4? diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/native/java/net/net_util.h --- a/jdk/src/share/native/java/net/net_util.h Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/share/native/java/net/net_util.h Sun Aug 31 18:39:01 2008 +0100 @@ -116,7 +116,7 @@ JNIEXPORT int JNICALL NET_InetAddressToSockaddr(JNIEnv *env, jobject iaObj, int port, struct sockaddr *him, int *len, jboolean v4MappedAddress); -jobject +JNIEXPORT jobject JNICALL NET_SockaddrToInetAddress(JNIEnv *env, struct sockaddr *him, int *port); void initLocalAddrTable (); @@ -124,10 +124,10 @@ void NET_SetTrafficClass(struct sockaddr *him, int trafficClass); -jint +JNIEXPORT jint JNICALL NET_GetPortFromSockaddr(struct sockaddr *him); -jint +JNIEXPORT jint JNICALL NET_SockaddrEqualsInetAddress(JNIEnv *env,struct sockaddr *him, jobject iaObj); int diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/native/sun/nio/ch/genSocketOptionRegistry.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/native/sun/nio/ch/genSocketOptionRegistry.c Sun Aug 31 18:39:01 2008 +0100 @@ -0,0 +1,129 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +#include +#ifdef _WIN32 +#include +#include +#else +#include +#include +#include +#endif + +/** + * Generates sun.nio.ch.SocketOptionRegistry, a class that maps Java-level + * socket options to the platform specific level and option. + */ + +static void out(char* s) { + printf("%s\n", s); +} + +static void emit(const char *name, char * family, int level, int optname) { + printf(" map.put(new RegistryKey(%s, %s),", name, family); + printf(" new OptionKey(%d, %d));\n", level, optname); +} + +static void emit_unspec(const char *name, int level, int optname) { + emit(name, "Net.UNSPEC", level, optname); +} + +static void emit_inet(const char *name, int level, int optname) { + emit(name, "StandardProtocolFamily.INET", level, optname); +} + +static void emit_inet6(const char *name, int level, int optname) { + emit(name, "StandardProtocolFamily.INET6", level, optname); +} + +int main(int argc, const char* argv[]) { + out("// AUTOMATICALLY GENERATED FILE - DO NOT EDIT "); + out("package sun.nio.ch; "); + out("import java.net.SocketOption; "); + out("import java.net.StandardSocketOption; "); + out("import java.net.ProtocolFamily; "); + out("import java.net.StandardProtocolFamily; "); + out("import java.util.Map; "); + out("import java.util.HashMap; "); + out("class SocketOptionRegistry { "); + out(" private SocketOptionRegistry() { } "); + out(" private static class RegistryKey { "); + out(" private final SocketOption name; "); + out(" private final ProtocolFamily family; "); + out(" RegistryKey(SocketOption name, ProtocolFamily family) { "); + out(" this.name = name; "); + out(" this.family = family; "); + out(" } "); + out(" public int hashCode() { "); + out(" return name.hashCode() + family.hashCode(); "); + out(" } "); + out(" public boolean equals(Object ob) { "); + out(" if (ob == null) return false; "); + out(" if (!(ob instanceof RegistryKey)) return false; "); + out(" RegistryKey other = (RegistryKey)ob; "); + out(" if (this.name != other.name) return false; "); + out(" if (this.family != other.family) return false; "); + out(" return true; "); + out(" } "); + out(" } "); + out(" private static class LazyInitialization { "); + out(" static final Map options = options(); "); + out(" private static Map options() { "); + out(" Map map = "); + out(" new HashMap(); "); + + emit_unspec("StandardSocketOption.SO_BROADCAST", SOL_SOCKET, SO_BROADCAST); + emit_unspec("StandardSocketOption.SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE); + emit_unspec("StandardSocketOption.SO_LINGER", SOL_SOCKET, SO_LINGER); + emit_unspec("StandardSocketOption.SO_SNDBUF", SOL_SOCKET, SO_SNDBUF); + emit_unspec("StandardSocketOption.SO_RCVBUF", SOL_SOCKET, SO_RCVBUF); + emit_unspec("StandardSocketOption.SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR); + emit_unspec("StandardSocketOption.TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY); + + emit_inet("StandardSocketOption.IP_TOS", IPPROTO_IP, IP_TOS); + emit_inet("StandardSocketOption.IP_MULTICAST_IF", IPPROTO_IP, IP_MULTICAST_IF); + emit_inet("StandardSocketOption.IP_MULTICAST_TTL", IPPROTO_IP, IP_MULTICAST_TTL); + emit_inet("StandardSocketOption.IP_MULTICAST_LOOP", IPPROTO_IP, IP_MULTICAST_LOOP); + +#ifdef AF_INET6 + emit_inet6("StandardSocketOption.IP_MULTICAST_IF", IPPROTO_IPV6, IPV6_MULTICAST_IF); + emit_inet6("StandardSocketOption.IP_MULTICAST_TTL", IPPROTO_IPV6, IPV6_MULTICAST_HOPS); + emit_inet6("StandardSocketOption.IP_MULTICAST_LOOP", IPPROTO_IPV6, IPV6_MULTICAST_LOOP); +#endif + + emit_unspec("ExtendedSocketOption.SO_OOBINLINE", SOL_SOCKET, SO_OOBINLINE); + + out(" return map; "); + out(" } "); + out(" } "); + out(" public static OptionKey findOption(SocketOption name, ProtocolFamily family) { "); + out(" RegistryKey key = new RegistryKey(name, family); "); + out(" return LazyInitialization.options.get(key); "); + out(" } "); + out("} "); + + return 0; +} diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/sample/nio/multicast/MulticastAddress.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/sample/nio/multicast/MulticastAddress.java Sun Aug 31 18:39:01 2008 +0100 @@ -0,0 +1,127 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Sun Microsystems nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.UnknownHostException; +import java.net.SocketException; + +/** + * Parses and represents a multicast address. + */ + +class MulticastAddress { + private final InetAddress group; + private final int port; + private final NetworkInterface interf; + + private MulticastAddress(InetAddress group, int port, NetworkInterface interf) { + this.group = group; + this.port = port; + this.interf = interf; + } + + InetAddress group() { + return group; + } + + int port() { + return port; + } + + /** + * @return The network interface, may be {@code null} + */ + NetworkInterface interf() { + return interf; + } + + /** + * Parses a string of the form "group:port[@interface]", returning + * a MulticastAddress representing the address + */ + static MulticastAddress parse(String s) { + String[] components = s.split("@"); + if (components.length > 2) + throw new IllegalArgumentException("At most one '@' expected"); + + // get group and port + String target = components[0]; + int len = components[0].length(); + int colon = components[0].lastIndexOf(':'); + if ((colon < 1) || (colon > (len-2))) + throw new IllegalArgumentException("group:port expected"); + String groupString = target.substring(0, colon); + int port = -1; + try { + port = Integer.parseInt(target.substring(colon+1, len)); + } catch (NumberFormatException x) { + throw new IllegalArgumentException(x); + } + + // handle IPv6 literal address + if (groupString.charAt(0) == '[') { + len = groupString.length(); + if (groupString.charAt(len-1) != ']') + throw new IllegalArgumentException("missing ']'"); + groupString = groupString.substring(1,len-1); + if (groupString.length() == 0) + throw new IllegalArgumentException("missing IPv6 address"); + } + + // get group address + InetAddress group = null; + try { + group = InetAddress.getByName(groupString); + } catch (UnknownHostException x) { + throw new IllegalArgumentException(x); + } + if (!group.isMulticastAddress()) { + throw new IllegalArgumentException("'" + group.getHostAddress() + + "' is not multicast address"); + } + + // optional interface + NetworkInterface interf = null; + if (components.length == 2) { + try { + interf = NetworkInterface.getByName(components[1]); + } catch (SocketException x) { + throw new IllegalArgumentException(x); + } + if (interf == null) { + throw new IllegalArgumentException("'" + components[1] + + "' is not valid interface"); + } + } + return new MulticastAddress(group, port, interf); + } +} diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/sample/nio/multicast/Reader.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/sample/nio/multicast/Reader.java Sun Aug 31 18:39:01 2008 +0100 @@ -0,0 +1,142 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Sun Microsystems nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import java.nio.channels.*; +import java.nio.charset.*; +import java.nio.ByteBuffer; +import java.net.*; +import java.io.IOException; +import java.util.*; + +public class Reader { + + static void usage() { + System.err.println("usage: java Reader group:port@interf [-only source...] [-block source...]"); + System.exit(-1); + } + + static void printDatagram(SocketAddress sa, ByteBuffer buf) { + System.out.format("-- datagram from %s --\n", + ((InetSocketAddress)sa).getAddress().getHostAddress()); + System.out.println(Charset.defaultCharset().decode(buf)); + } + + static void parseAddessList(String s, List list) + throws UnknownHostException + { + String[] sources = s.split(","); + for (int i=0; i includeList = new ArrayList(); + List excludeList = new ArrayList(); + int argc = 1; + while (argc < args.length) { + String option = args[argc++]; + if (argc >= args.length) + usage(); + String value = args[argc++]; + if (option.equals("-only")) { + parseAddessList(value, includeList); + continue; + } + if (option.equals("-block")) { + parseAddessList(value, excludeList); + continue; + } + usage(); + } + if (!includeList.isEmpty() && !excludeList.isEmpty()) { + usage(); + } + + // create and bind socket + ProtocolFamily family = StandardProtocolFamily.INET; + if (target.group() instanceof Inet6Address) { + family = StandardProtocolFamily.INET6; + } + DatagramChannel dc = DatagramChannel.open(family) + .setOption(StandardSocketOption.SO_REUSEADDR, true) + .bind(new InetSocketAddress(target.port())); + + if (includeList.isEmpty()) { + // join group and block addresses on the exclude list + MembershipKey key = dc.join(target.group(), target.interf()); + for (InetAddress source: excludeList) { + key.block(source); + } + } else { + // join with source-specific membership for each source + for (InetAddress source: includeList) { + dc.join(target.group(), target.interf(), source); + } + } + + // register socket with Selector + Selector sel = Selector.open(); + dc.configureBlocking(false); + dc.register(sel, SelectionKey.OP_READ); + + // print out each datagram that we receive + ByteBuffer buf = ByteBuffer.allocateDirect(4096); + for (;;) { + int updated = sel.select(); + if (updated > 0) { + Iterator iter = sel.selectedKeys().iterator(); + while (iter.hasNext()) { + SelectionKey sk = iter.next(); + iter.remove(); + + DatagramChannel ch = (DatagramChannel)sk.channel(); + SocketAddress sa = ch.receive(buf); + if (sa != null) { + buf.flip(); + printDatagram(sa, buf); + buf.rewind(); + buf.limit(buf.capacity()); + } + } + } + } + } +} diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/share/sample/nio/multicast/Sender.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/sample/nio/multicast/Sender.java Sun Aug 31 18:39:01 2008 +0100 @@ -0,0 +1,71 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Sun Microsystems nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import java.nio.channels.*; +import java.nio.charset.Charset; +import java.net.*; +import java.io.IOException; +import java.util.*; + +/** + * Sample multicast sender to send a message in a multicast datagram + * to a given group. + */ + +public class Sender { + + private static void usage() { + System.err.println("usage: java Sender group:port[@interface] message"); + System.exit(-1); + } + + public static void main(String[] args) throws IOException { + if (args.length < 2) + usage(); + + MulticastAddress target = MulticastAddress.parse(args[0]); + + // create socket + ProtocolFamily family = StandardProtocolFamily.INET; + if (target.group() instanceof Inet6Address) + family = StandardProtocolFamily.INET6; + DatagramChannel dc = DatagramChannel.open(family).bind(new InetSocketAddress(0)); + if (target.interf() != null) { + dc.setOption(StandardSocketOption.IP_MULTICAST_IF, target.interf()); + } + + // send multicast packet + dc.send(Charset.defaultCharset().encode(args[1]), + new InetSocketAddress(target.group(), target.port())); + dc.close(); + } + +} diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/solaris/native/java/net/net_util_md.c --- a/jdk/src/solaris/native/java/net/net_util_md.c Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/solaris/native/java/net/net_util_md.c Sun Aug 31 18:39:01 2008 +0100 @@ -791,7 +791,7 @@ #endif /* AF_INET6 */ } -jint +JNIEXPORT jint JNICALL NET_GetPortFromSockaddr(struct sockaddr *him) { #ifdef AF_INET6 if (him->sa_family == AF_INET6) { diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/solaris/native/sun/nio/ch/DatagramChannelImpl.c --- a/jdk/src/solaris/native/sun/nio/ch/DatagramChannelImpl.c Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/solaris/native/sun/nio/ch/DatagramChannelImpl.c Sun Aug 31 18:39:01 2008 +0100 @@ -198,7 +198,7 @@ JNIEXPORT jint JNICALL Java_sun_nio_ch_DatagramChannelImpl_send0(JNIEnv *env, jobject this, - jobject fdo, jlong address, + jboolean preferIPv6, jobject fdo, jlong address, jint len, jobject dest) { jint fd = fdval(env, fdo); @@ -215,7 +215,7 @@ if (NET_InetAddressToSockaddr(env, destAddress, destPort, (struct sockaddr *)&sa, - &sa_len, JNI_TRUE) != 0) { + &sa_len, preferIPv6) != 0) { return IOS_THROWN; } diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/solaris/native/sun/nio/ch/FileKey.c --- a/jdk/src/solaris/native/sun/nio/ch/FileKey.c Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/solaris/native/sun/nio/ch/FileKey.c Sun Aug 31 18:39:01 2008 +0100 @@ -33,12 +33,6 @@ static jfieldID key_st_dev; /* id for FileKey.st_dev */ static jfieldID key_st_ino; /* id for FileKey.st_ino */ -#define RESTARTABLE(_cmd, _result) do { \ - do { \ - _result = _cmd; \ - } while ((_result == -1) && (errno == EINTR)); \ -} while(0) - JNIEXPORT void JNICALL Java_sun_nio_ch_FileKey_initIDs(JNIEnv *env, jclass clazz) diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/solaris/native/sun/nio/ch/Net.c --- a/jdk/src/solaris/native/sun/nio/ch/Net.c Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/solaris/native/sun/nio/ch/Net.c Sun Aug 31 18:39:01 2008 +0100 @@ -37,17 +37,109 @@ #include "net_util.h" #include "net_util_md.h" #include "nio_util.h" -#include "java_net_SocketOptions.h" #include "nio.h" +/** + * Definitions for source-specific multicast to allow for building + * with older header files. + */ + +#ifdef __solaris__ + +#ifndef IP_BLOCK_SOURCE + +#define IP_BLOCK_SOURCE 0x15 +#define IP_UNBLOCK_SOURCE 0x16 +#define IP_ADD_SOURCE_MEMBERSHIP 0x17 +#define IP_DROP_SOURCE_MEMBERSHIP 0x18 + +#define MCAST_BLOCK_SOURCE 0x2b +#define MCAST_UNBLOCK_SOURCE 0x2c +#define MCAST_JOIN_SOURCE_GROUP 0x2d +#define MCAST_LEAVE_SOURCE_GROUP 0x2e + +#endif /* IP_BLOCK_SOURCE */ + +struct my_ip_mreq_source { + struct in_addr imr_multiaddr; + struct in_addr imr_sourceaddr; + struct in_addr imr_interface; +}; + +/* + * Use #pragma pack() construct to force 32-bit alignment on amd64. + */ +#if defined(amd64) +#pragma pack(4) +#endif + +struct my_group_source_req { + uint32_t gsr_interface; /* interface index */ + struct sockaddr_storage gsr_group; /* group address */ + struct sockaddr_storage gsr_source; /* source address */ +}; + +#if defined(amd64) +#pragma pack() +#endif + +#endif /* __solaris__ */ + + #ifdef __linux__ -#include + +#ifndef IP_BLOCK_SOURCE + +#define IP_BLOCK_SOURCE 38 +#define IP_UNBLOCK_SOURCE 37 +#define IP_ADD_SOURCE_MEMBERSHIP 39 +#define IP_DROP_SOURCE_MEMBERSHIP 40 + +#define MCAST_BLOCK_SOURCE 43 +#define MCAST_UNBLOCK_SOURCE 44 +#define MCAST_JOIN_SOURCE_GROUP 42 +#define MCAST_LEAVE_SOURCE_GROUP 45 + +#endif /* IP_BLOCK_SOURCE */ + +struct my_ip_mreq_source { + struct in_addr imr_multiaddr; + struct in_addr imr_interface; + struct in_addr imr_sourceaddr; +}; + +struct my_group_source_req { + uint32_t gsr_interface; /* interface index */ + struct sockaddr_storage gsr_group; /* group address */ + struct sockaddr_storage gsr_source; /* source address */ +}; -#define IPV6_MULTICAST_IF 17 -#ifndef SO_BSDCOMPAT -#define SO_BSDCOMPAT 14 -#endif -#endif +#endif /* __linux__ */ + + +#define COPY_INET6_ADDRESS(env, source, target) \ + (*env)->GetByteArrayRegion(env, source, 0, 16, target) + +/* + * Copy IPv6 group, interface index, and IPv6 source address + * into group_source_req structure. + */ +static void initGroupSourceReq(JNIEnv* env, jbyteArray group, jint index, + jbyteArray source, struct my_group_source_req* req) +{ + struct sockaddr_in6* sin6; + + req->gsr_interface = (uint32_t)index; + + sin6 = (struct sockaddr_in6*)&(req->gsr_group); + sin6->sin6_family = AF_INET6; + COPY_INET6_ADDRESS(env, group, (jbyte*)&(sin6->sin6_addr)); + + sin6 = (struct sockaddr_in6*)&(req->gsr_source); + sin6->sin6_family = AF_INET6; + COPY_INET6_ADDRESS(env, source, (jbyte*)&(sin6->sin6_addr)); +} + JNIEXPORT void JNICALL Java_sun_nio_ch_Net_initIDs(JNIEnv *env, jclass clazz) @@ -55,43 +147,61 @@ /* Here because Windows native code does need to init IDs */ } +JNIEXPORT jboolean JNICALL +Java_sun_nio_ch_Net_isIPv6Available0(JNIEnv* env, jclass cl) +{ + return (ipv6_available()) ? JNI_TRUE : JNI_FALSE; +} + JNIEXPORT int JNICALL -Java_sun_nio_ch_Net_socket0(JNIEnv *env, jclass cl, jboolean stream, - jboolean reuse) +Java_sun_nio_ch_Net_socket0(JNIEnv *env, jclass cl, jboolean preferIPv6, + jboolean stream, jboolean reuse) { int fd; + int type = (stream ? SOCK_STREAM : SOCK_DGRAM); + int domain = (ipv6_available() && preferIPv6) ? AF_INET6 : AF_INET; -#ifdef AF_INET6 - if (ipv6_available()) - fd = socket(AF_INET6, (stream ? SOCK_STREAM : SOCK_DGRAM), 0); - else -#endif /* AF_INET6 */ - fd = socket(AF_INET, (stream ? SOCK_STREAM : SOCK_DGRAM), 0); - + fd = socket(domain, type, 0); if (fd < 0) { return handleSocketError(env, errno); } if (reuse) { int arg = 1; - if (NET_SetSockOpt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&arg, - sizeof(arg)) < 0) { + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&arg, + sizeof(arg)) < 0) { JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "sun.nio.ch.Net.setIntOption"); + close(fd); + return -1; } } +#ifdef __linux__ + /* By default, Linux uses the route default */ + if (domain == AF_INET6 && type == SOCK_DGRAM) { + int arg = 1; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &arg, + sizeof(arg)) < 0) { + JNU_ThrowByNameWithLastError(env, + JNU_JAVANETPKG "SocketException", + "sun.nio.ch.Net.setIntOption"); + close(fd); + return -1; + } + } +#endif return fd; } JNIEXPORT void JNICALL -Java_sun_nio_ch_Net_bind(JNIEnv *env, jclass clazz, /* ## Needs rest of PSI gunk */ - jobject fdo, jobject ia, int port) +Java_sun_nio_ch_Net_bind0(JNIEnv *env, jclass clazz, jboolean preferIPv6, + jobject fdo, jobject iao, int port) { SOCKADDR sa; int sa_len = SOCKADDR_LEN; int rv = 0; - if (NET_InetAddressToSockaddr(env, ia, port, (struct sockaddr *)&sa, &sa_len, JNI_TRUE) != 0) { + if (NET_InetAddressToSockaddr(env, iao, port, (struct sockaddr *)&sa, &sa_len, preferIPv6) != 0) { return; } @@ -101,27 +211,27 @@ } } +JNIEXPORT void JNICALL +Java_sun_nio_ch_Net_listen(JNIEnv *env, jclass cl, jobject fdo, jint backlog) +{ + if (listen(fdval(env, fdo), backlog) < 0) + handleSocketError(env, errno); +} + JNIEXPORT jint JNICALL -Java_sun_nio_ch_Net_connect(JNIEnv *env, jclass clazz, - jobject fdo, jobject iao, jint port, - jint trafficClass) +Java_sun_nio_ch_Net_connect0(JNIEnv *env, jclass clazz, jboolean preferIPv6, + jobject fdo, jobject iao, jint port) { SOCKADDR sa; int sa_len = SOCKADDR_LEN; int rv; - if (NET_InetAddressToSockaddr(env, iao, port, (struct sockaddr *) &sa, &sa_len, JNI_TRUE) != 0) { + if (NET_InetAddressToSockaddr(env, iao, port, (struct sockaddr *) &sa, + &sa_len, preferIPv6) != 0) + { return IOS_THROWN; } -#ifdef AF_INET6 -#if 0 - if (trafficClass != 0 && ipv6_available()) { /* ## FIX */ - NET_SetTrafficClass((struct sockaddr *)&sa, trafficClass); - } -#endif -#endif - rv = connect(fdval(env, fdo), (struct sockaddr *)&sa, sa_len); if (rv != 0) { if (errno == EINPROGRESS) { @@ -159,119 +269,79 @@ return NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa, &port); } - -#ifdef NEEDED - -/* ## This is gross. We should generate platform-specific constant - * ## definitions into a .java file and use those directly. - */ - -static int -mapOption(JNIEnv *env, int opt, int *klevel, int *kopt) +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_getIntOption0(JNIEnv *env, jclass clazz, jobject fdo, + jboolean mayNeedConversion, jint level, jint opt) { - - switch (opt) { - - case java_net_SocketOptions_IP_TOS: - *klevel = IPPROTO_IP; - *kopt = IP_TOS; - break; + int result; + struct linger linger; + u_char carg; + void *arg; + int arglen, n; - case java_net_SocketOptions_SO_BROADCAST: - case java_net_SocketOptions_SO_KEEPALIVE: - case java_net_SocketOptions_SO_LINGER: - case java_net_SocketOptions_SO_OOBINLINE: - case java_net_SocketOptions_SO_RCVBUF: - case java_net_SocketOptions_SO_REUSEADDR: - case java_net_SocketOptions_SO_SNDBUF: - *klevel = SOL_SOCKET; - break; + /* Option value is an int except for a few specific cases */ + + arg = (void *)&result; + arglen = sizeof(result); - case java_net_SocketOptions_TCP_NODELAY: - *klevel = IPPROTO_IP; - *kopt = TCP_NODELAY; - return 0; - - default: - JNU_ThrowByName(env, "java/lang/IllegalArgumentException", NULL); - return -1; + if (level == IPPROTO_IP && + (opt == IP_MULTICAST_TTL || opt == IP_MULTICAST_LOOP)) { + arg = (void*)&carg; + arglen = sizeof(carg); } - switch (opt) { - - case java_net_SocketOptions_SO_BROADCAST: *kopt = SO_BROADCAST; break; - case java_net_SocketOptions_SO_KEEPALIVE: *kopt = SO_KEEPALIVE; break; - case java_net_SocketOptions_SO_LINGER: *kopt = SO_LINGER; break; - case java_net_SocketOptions_SO_OOBINLINE: *kopt = SO_OOBINLINE; break; - case java_net_SocketOptions_SO_RCVBUF: *kopt = SO_RCVBUF; break; - case java_net_SocketOptions_SO_REUSEADDR: *kopt = SO_REUSEADDR; break; - case java_net_SocketOptions_SO_SNDBUF: *kopt = SO_SNDBUF; break; - - default: - return -1; + if (level == SOL_SOCKET && opt == SO_LINGER) { + arg = (void *)&linger; + arglen = sizeof(linger); } - return 0; -} -#endif - - -JNIEXPORT jint JNICALL -Java_sun_nio_ch_Net_getIntOption0(JNIEnv *env, jclass clazz, - jobject fdo, jint opt) -{ - int klevel, kopt; - int result; - struct linger linger; - void *arg; - int arglen; - - if (NET_MapSocketOption(opt, &klevel, &kopt) < 0) { - JNU_ThrowByNameWithLastError(env, - JNU_JAVANETPKG "SocketException", - "Unsupported socket option"); - return -1; + if (mayNeedConversion) { + n = NET_GetSockOpt(fdval(env, fdo), level, opt, arg, &arglen); + } else { + n = getsockopt(fdval(env, fdo), level, opt, arg, &arglen); } - - if (opt == java_net_SocketOptions_SO_LINGER) { - arg = (void *)&linger; - arglen = sizeof(linger); - } else { - arg = (void *)&result; - arglen = sizeof(result); - } - - if (NET_GetSockOpt(fdval(env, fdo), klevel, kopt, arg, &arglen) < 0) { + if (n < 0) { JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "sun.nio.ch.Net.getIntOption"); return -1; } - if (opt == java_net_SocketOptions_SO_LINGER) - return linger.l_onoff ? linger.l_linger : -1; - else - return result; + if (level == IPPROTO_IP && + (opt == IP_MULTICAST_TTL || opt == IP_MULTICAST_LOOP)) + { + return (jint)carg; + } + + if (level == SOL_SOCKET && opt == SO_LINGER) + return linger.l_onoff ? (jint)linger.l_linger : (jint)-1; + + return (jint)result; } JNIEXPORT void JNICALL -Java_sun_nio_ch_Net_setIntOption0(JNIEnv *env, jclass clazz, - jobject fdo, jint opt, jint arg) +Java_sun_nio_ch_Net_setIntOption0(JNIEnv *env, jclass clazz, jobject fdo, + jboolean mayNeedConversion, jint level, jint opt, jint arg) { - int klevel, kopt; int result; struct linger linger; + u_char carg; void *parg; - int arglen; + int arglen, n; + + /* Option value is an int except for a few specific cases */ - if (NET_MapSocketOption(opt, &klevel, &kopt) < 0) { - JNU_ThrowByNameWithLastError(env, - JNU_JAVANETPKG "SocketException", - "Unsupported socket option"); - return; + parg = (void*)&arg; + arglen = sizeof(arg); + + if (level == IPPROTO_IP && + (opt == IP_MULTICAST_TTL || opt == IP_MULTICAST_LOOP)) { + parg = (void*)&carg; + arglen = sizeof(carg); + carg = (u_char)arg; } - if (opt == java_net_SocketOptions_SO_LINGER) { + if (level == SOL_SOCKET && opt == SO_LINGER) { parg = (void *)&linger; arglen = sizeof(linger); if (arg >= 0) { @@ -281,19 +351,199 @@ linger.l_onoff = 0; linger.l_linger = 0; } - } else { - parg = (void *)&arg; - arglen = sizeof(arg); } - if (NET_SetSockOpt(fdval(env, fdo), klevel, kopt, parg, arglen) < 0) { + if (mayNeedConversion) { + n = NET_SetSockOpt(fdval(env, fdo), level, opt, parg, arglen); + } else { + n = setsockopt(fdval(env, fdo), level, opt, parg, arglen); + } + if (n < 0) { JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "sun.nio.ch.Net.setIntOption"); } } +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_joinOrDrop4(JNIEnv *env, jobject this, jboolean join, jobject fdo, + jint group, jint interf, jint source) +{ + struct ip_mreq mreq; + struct my_ip_mreq_source mreq_source; + int opt, n, optlen; + void* optval; + if (source == 0) { + mreq.imr_multiaddr.s_addr = htonl(group); + mreq.imr_interface.s_addr = htonl(interf); + opt = (join) ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; + optval = (void*)&mreq; + optlen = sizeof(mreq); + } else { + mreq_source.imr_multiaddr.s_addr = htonl(group); + mreq_source.imr_sourceaddr.s_addr = htonl(source); + mreq_source.imr_interface.s_addr = htonl(interf); + opt = (join) ? IP_ADD_SOURCE_MEMBERSHIP : IP_DROP_SOURCE_MEMBERSHIP; + optval = (void*)&mreq_source; + optlen = sizeof(mreq_source); + } + + n = setsockopt(fdval(env,fdo), IPPROTO_IP, opt, optval, optlen); + if (n < 0) { + if (join && (errno == ENOPROTOOPT)) + return IOS_UNAVAILABLE; + handleSocketError(env, errno); + } + return 0; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_blockOrUnblock4(JNIEnv *env, jobject this, jboolean block, jobject fdo, + jint group, jint interf, jint source) +{ + struct my_ip_mreq_source mreq_source; + int n; + int opt = (block) ? IP_BLOCK_SOURCE : IP_UNBLOCK_SOURCE; + + mreq_source.imr_multiaddr.s_addr = htonl(group); + mreq_source.imr_sourceaddr.s_addr = htonl(source); + mreq_source.imr_interface.s_addr = htonl(interf); + + n = setsockopt(fdval(env,fdo), IPPROTO_IP, opt, + (void*)&mreq_source, sizeof(mreq_source)); + if (n < 0) { + if (block && (errno == ENOPROTOOPT)) + return IOS_UNAVAILABLE; + handleSocketError(env, errno); + } + return 0; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_joinOrDrop6(JNIEnv *env, jobject this, jboolean join, jobject fdo, + jbyteArray group, jint index, jbyteArray source) +{ + struct ipv6_mreq mreq6; + struct my_group_source_req req; + int opt, n, optlen; + void* optval; + + if (source == NULL) { + COPY_INET6_ADDRESS(env, group, (jbyte*)&(mreq6.ipv6mr_multiaddr)); + mreq6.ipv6mr_interface = (int)index; + opt = (join) ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP; + optval = (void*)&mreq6; + optlen = sizeof(mreq6); + } else { +#ifdef __linux__ + /* Include-mode filtering broken on Linux at least to 2.6.24 */ + return IOS_UNAVAILABLE; +#else + initGroupSourceReq(env, group, index, source, &req); + opt = (join) ? MCAST_JOIN_SOURCE_GROUP : MCAST_LEAVE_SOURCE_GROUP; + optval = (void*)&req; + optlen = sizeof(req); +#endif + } + + n = setsockopt(fdval(env,fdo), IPPROTO_IPV6, opt, optval, optlen); + if (n < 0) { + if (join && (errno == ENOPROTOOPT)) + return IOS_UNAVAILABLE; + handleSocketError(env, errno); + } + return 0; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_blockOrUnblock6(JNIEnv *env, jobject this, jboolean block, jobject fdo, + jbyteArray group, jint index, jbyteArray source) +{ + struct my_group_source_req req; + int n; + int opt = (block) ? MCAST_BLOCK_SOURCE : MCAST_UNBLOCK_SOURCE; + + initGroupSourceReq(env, group, index, source, &req); + + n = setsockopt(fdval(env,fdo), IPPROTO_IPV6, opt, + (void*)&req, sizeof(req)); + if (n < 0) { + if (block && (errno == ENOPROTOOPT)) + return IOS_UNAVAILABLE; + handleSocketError(env, errno); + } + return 0; +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_Net_setInterface4(JNIEnv* env, jobject this, jobject fdo, jint interf) +{ + struct in_addr in; + int arglen = sizeof(struct in_addr); + int n; + + in.s_addr = htonl(interf); + + n = setsockopt(fdval(env, fdo), IPPROTO_IP, IP_MULTICAST_IF, + (void*)&(in.s_addr), arglen); + if (n < 0) { + handleSocketError(env, errno); + } +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_getInterface4(JNIEnv* env, jobject this, jobject fdo) +{ + struct in_addr in; + int arglen = sizeof(struct in_addr); + int n; + + n = getsockopt(fdval(env, fdo), IPPROTO_IP, IP_MULTICAST_IF, (void*)&in, &arglen); + if (n < 0) { + handleSocketError(env, errno); + return -1; + } + return ntohl(in.s_addr); +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_Net_setInterface6(JNIEnv* env, jobject this, jobject fdo, jint index) +{ + int value = (jint)index; + int arglen = sizeof(value); + int n; + + n = setsockopt(fdval(env, fdo), IPPROTO_IPV6, IPV6_MULTICAST_IF, + (void*)&(index), arglen); + if (n < 0) { + handleSocketError(env, errno); + } +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_getInterface6(JNIEnv* env, jobject this, jobject fdo) +{ + int index; + int arglen = sizeof(index); + int n; + + n = getsockopt(fdval(env, fdo), IPPROTO_IPV6, IPV6_MULTICAST_IF, (void*)&index, &arglen); + if (n < 0) { + handleSocketError(env, errno); + return -1; + } + return (jint)index; +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_Net_shutdown(JNIEnv *env, jclass cl, jobject fdo, jint jhow) +{ + int how = (jhow == sun_nio_ch_Net_SHUT_RD) ? SHUT_RD : + (jhow == sun_nio_ch_Net_SHUT_WR) ? SHUT_WR : SHUT_RDWR; + if (shutdown(fdval(env, fdo), how) < 0) + handleSocketError(env, errno); +} /* Declared in nio_util.h */ diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/solaris/native/sun/nio/ch/ServerSocketChannelImpl.c --- a/jdk/src/solaris/native/sun/nio/ch/ServerSocketChannelImpl.c Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/solaris/native/sun/nio/ch/ServerSocketChannelImpl.c Sun Aug 31 18:39:01 2008 +0100 @@ -65,14 +65,6 @@ "(Ljava/net/InetAddress;I)V"); } -JNIEXPORT void JNICALL -Java_sun_nio_ch_ServerSocketChannelImpl_listen(JNIEnv *env, jclass cl, - jobject fdo, jint backlog) -{ - if (listen(fdval(env, fdo), backlog) < 0) - handleSocketError(env, errno); -} - JNIEXPORT jint JNICALL Java_sun_nio_ch_ServerSocketChannelImpl_accept0(JNIEnv *env, jobject this, jobject ssfdo, jobject newfdo, diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/solaris/native/sun/nio/ch/SocketChannelImpl.c --- a/jdk/src/solaris/native/sun/nio/ch/SocketChannelImpl.c Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/solaris/native/sun/nio/ch/SocketChannelImpl.c Sun Aug 31 18:39:01 2008 +0100 @@ -35,10 +35,6 @@ #include #endif -#if defined(__solaris__) && !defined(_SOCKLEN_T) -typedef size_t socklen_t; /* New in SunOS 5.7, so need this for 5.6 */ -#endif - #include "jni.h" #include "jni_util.h" #include "net_util.h" @@ -88,12 +84,3 @@ } return 0; } - - -JNIEXPORT void JNICALL -Java_sun_nio_ch_SocketChannelImpl_shutdown(JNIEnv *env, jclass cl, - jobject fdo, jint how) -{ - if (shutdown(fdval(env, fdo), how) < 0) - handleSocketError(env, errno); -} diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/solaris/native/sun/nio/ch/nio_util.h --- a/jdk/src/solaris/native/sun/nio/ch/nio_util.h Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/solaris/native/sun/nio/ch/nio_util.h Sun Aug 31 18:39:01 2008 +0100 @@ -27,8 +27,15 @@ #include "jni_util.h" #include "jvm.h" #include "jlong.h" +#include #include +#define RESTARTABLE(_cmd, _result) do { \ + do { \ + _result = _cmd; \ + } while((_result == -1) && (errno == EINTR)); \ +} while(0) + /* NIO utility procedures */ diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/windows/native/java/net/net_util_md.c --- a/jdk/src/windows/native/java/net/net_util_md.c Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/windows/native/java/net/net_util_md.c Sun Aug 31 18:39:01 2008 +0100 @@ -889,7 +889,7 @@ return 0; } -jint +JNIEXPORT jint JNICALL NET_GetPortFromSockaddr(struct sockaddr *him) { if (him->sa_family == AF_INET6) { return ntohs(((struct sockaddr_in6*)him)->sin6_port); diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/windows/native/sun/nio/ch/DatagramChannelImpl.c --- a/jdk/src/windows/native/sun/nio/ch/DatagramChannelImpl.c Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/windows/native/sun/nio/ch/DatagramChannelImpl.c Sun Aug 31 18:39:01 2008 +0100 @@ -39,46 +39,9 @@ static jfieldID dci_senderID; /* sender in sun.nio.ch.DatagramChannelImpl */ static jfieldID dci_senderAddrID; /* sender InetAddress in sun.nio.ch.DatagramChannelImpl */ static jfieldID dci_senderPortID; /* sender port in sun.nio.ch.DatagramChannelImpl */ -static jfieldID ia_addrID; -static jfieldID ia_famID; static jclass isa_class; /* java.net.InetSocketAddress */ -static jclass ia_class; -static jmethodID isa_ctorID; /* .InetSocketAddress(InetAddress, int) */ -static jmethodID ia_ctorID; - -/* - * Returns JNI_TRUE if DatagramChannelImpl has already cached an - * InetAddress/port corresponding to the socket address. - */ -static jboolean isSenderCached(JNIEnv *env, jobject this, struct sockaddr_in *sa) { - jobject senderAddr; - - /* shouldn't happen until we have dual IPv4/IPv6 stack (post-XP ?) */ - if (sa->sin_family != AF_INET) { - return JNI_FALSE; - } +static jmethodID isa_ctorID; /* java.net.InetSocketAddress(InetAddress, int) */ - /* - * Compare source address to cached InetAddress - */ - senderAddr = (*env)->GetObjectField(env, this, dci_senderAddrID); - if (senderAddr == NULL) { - return JNI_FALSE; - } - if ((jint)ntohl(sa->sin_addr.s_addr) != - (*env)->GetIntField(env, senderAddr, ia_addrID)) { - return JNI_FALSE; - } - - /* - * Compare source port to cached port - */ - if ((jint)ntohs(sa->sin_port) != - (*env)->GetIntField(env, this, dci_senderPortID)) { - return JNI_FALSE; - } - return JNI_TRUE; -} JNIEXPORT void JNICALL Java_sun_nio_ch_DatagramChannelImpl_initIDs(JNIEnv *env, jclass clazz) @@ -99,32 +62,6 @@ "Ljava/net/InetAddress;"); dci_senderPortID = (*env)->GetFieldID(env, clazz, "cachedSenderPort", "I"); - clazz = (*env)->FindClass(env, "java/net/Inet4Address"); - ia_class = (*env)->NewGlobalRef(env, clazz); - ia_addrID = (*env)->GetFieldID(env, clazz, "address", "I"); - ia_famID = (*env)->GetFieldID(env, clazz, "family", "I"); - ia_ctorID = (*env)->GetMethodID(env, clazz, "", "()V"); -} - -/* - * Return JNI_TRUE if this Windows edition supports ICMP Port Unreachable - */ -__inline static jboolean supportPortUnreachable() { - static jboolean initDone; - static jboolean portUnreachableSupported; - - if (!initDone) { - OSVERSIONINFO ver; - ver.dwOSVersionInfoSize = sizeof(ver); - GetVersionEx(&ver); - if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT && ver.dwMajorVersion >= 5) { - portUnreachableSupported = JNI_TRUE; - } else { - portUnreachableSupported = JNI_FALSE; - } - initDone = JNI_TRUE; - } - return portUnreachableSupported; } /* @@ -140,15 +77,8 @@ char buf[1]; fd_set tbl; struct timeval t = { 0, 0 }; - struct sockaddr_in rmtaddr; - int addrlen = sizeof(rmtaddr); - - /* - * A no-op if this OS doesn't support it. - */ - if (!supportPortUnreachable()) { - return JNI_FALSE; - } + SOCKETADDRESS sa; + int addrlen = sizeof(sa); /* * Peek at the queue to see if there is an ICMP port unreachable. If there @@ -161,7 +91,7 @@ break; } if (recvfrom(fd, buf, 1, MSG_PEEK, - (struct sockaddr *)&rmtaddr, &addrlen) != SOCKET_ERROR) { + (struct sockaddr *)&sa, &addrlen) != SOCKET_ERROR) { break; } if (WSAGetLastError() != WSAECONNRESET) { @@ -169,7 +99,7 @@ break; } - recvfrom(fd, buf, 1, 0, (struct sockaddr *)&rmtaddr, &addrlen); + recvfrom(fd, buf, 1, 0, (struct sockaddr *)&sa, &addrlen); got_icmp = JNI_TRUE; } @@ -182,12 +112,12 @@ { jint fd = fdval(env, fdo); int rv = 0; - struct sockaddr_in psa; - int sa_len = sizeof(psa); + SOCKETADDRESS sa; + int sa_len = sizeof(sa); - memset(&psa, 0, sa_len); + memset(&sa, 0, sa_len); - rv = connect((SOCKET)fd, (struct sockaddr *)&psa, sa_len); + rv = connect((SOCKET)fd, (struct sockaddr *)&sa, sa_len); if (rv == SOCKET_ERROR) { handleSocketError(env, WSAGetLastError()); } @@ -200,10 +130,11 @@ { jint fd = fdval(env, fdo); void *buf = (void *)jlong_to_ptr(address); - struct sockaddr_in psa; - int sa_len = sizeof(psa); + SOCKETADDRESS sa; + int sa_len = sizeof(sa); BOOL retry = FALSE; jint n; + jobject senderAddr; do { retry = FALSE; @@ -211,7 +142,7 @@ (char *)buf, len, 0, - (struct sockaddr *)&psa, + (struct sockaddr *)&sa, &sa_len); if (n == SOCKET_ERROR) { @@ -233,21 +164,30 @@ } } while (retry); - if (!isSenderCached(env, this, &psa)) { - int port = ntohs(psa.sin_port); - jobject ia = (*env)->NewObject(env, ia_class, ia_ctorID); + /* + * If the source address and port match the cached address + * and port in DatagramChannelImpl then we don't need to + * create InetAddress and InetSocketAddress objects. + */ + senderAddr = (*env)->GetObjectField(env, this, dci_senderAddrID); + if (senderAddr != NULL) { + if (!NET_SockaddrEqualsInetAddress(env, (struct sockaddr *)&sa, + senderAddr)) { + senderAddr = NULL; + } else { + jint port = (*env)->GetIntField(env, this, dci_senderPortID); + if (port != NET_GetPortFromSockaddr((struct sockaddr *)&sa)) { + senderAddr = NULL; + } + } + } + if (senderAddr == NULL) { jobject isa = NULL; - - if (psa.sin_family != AF_INET) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", - "Protocol family unavailable"); - } + int port; + jobject ia = NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa, + &port); if (ia != NULL) { - // populate InetAddress (assumes AF_INET) - (*env)->SetIntField(env, ia, ia_addrID, ntohl(psa.sin_addr.s_addr)); - - // create InetSocketAddress isa = (*env)->NewObject(env, isa_class, isa_ctorID, ia, port); } @@ -258,9 +198,8 @@ // update cachedSenderInetAddress/cachedSenderPort (*env)->SetObjectField(env, this, dci_senderAddrID, ia); - (*env)->SetIntField(env, this, dci_senderPortID, port); - - // update sender + (*env)->SetIntField(env, this, dci_senderPortID, + NET_GetPortFromSockaddr((struct sockaddr *)&sa)); (*env)->SetObjectField(env, this, dci_senderID, isa); } return n; @@ -268,21 +207,20 @@ JNIEXPORT jint JNICALL Java_sun_nio_ch_DatagramChannelImpl_send0(JNIEnv *env, jobject this, - jobject fdo, jlong address, - jint len, jobject dest) + jboolean preferIPv6, jobject fdo, + jlong address, jint len, jobject dest) { jint fd = fdval(env, fdo); void *buf = (void *)jlong_to_ptr(address); - SOCKETADDRESS psa; - int sa_len = sizeof(psa); + SOCKETADDRESS sa; + int sa_len; jint rv = 0; jobject destAddress = (*env)->GetObjectField(env, dest, isa_addrID); jint destPort = (*env)->GetIntField(env, dest, isa_portID); - if (NET_InetAddressToSockaddr(env, destAddress, destPort, - (struct sockaddr *)&psa, - &sa_len, JNI_FALSE) != 0) { + (struct sockaddr *)&sa, + &sa_len, preferIPv6) != 0) { return IOS_THROWN; } @@ -290,7 +228,7 @@ buf, len, 0, - (struct sockaddr *)&psa, + (struct sockaddr *)&sa, sa_len); if (rv == SOCKET_ERROR) { int theErr = (jint)WSAGetLastError(); diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/windows/native/sun/nio/ch/Net.c --- a/jdk/src/windows/native/sun/nio/ch/Net.c Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/windows/native/sun/nio/ch/Net.c Sun Aug 31 18:39:01 2008 +0100 @@ -36,51 +36,95 @@ #include "sun_nio_ch_Net.h" +/** + * Definitions to allow for building with older SDK include files. + */ -static jfieldID ia_addrID; -static jclass ia_class; -static jmethodID ia_ctorID; -static jfieldID ia_famID; +#ifndef MCAST_BLOCK_SOURCE + +#define MCAST_BLOCK_SOURCE 43 +#define MCAST_UNBLOCK_SOURCE 44 +#define MCAST_JOIN_SOURCE_GROUP 45 +#define MCAST_LEAVE_SOURCE_GROUP 46 + +#endif /* MCAST_BLOCK_SOURCE */ -/************************************************************** - * static method to store field IDs in initializers +typedef struct my_ip_mreq_source { + IN_ADDR imr_multiaddr; + IN_ADDR imr_sourceaddr; + IN_ADDR imr_interface; +}; + +typedef struct my_group_source_req { + ULONG gsr_interface; + SOCKADDR_STORAGE gsr_group; + SOCKADDR_STORAGE gsr_source; +}; + +/** + * Copy IPv6 address as jbytearray to target */ +#define COPY_INET6_ADDRESS(env, source, target) \ + (*env)->GetByteArrayRegion(env, source, 0, 16, target) + + JNIEXPORT void JNICALL Java_sun_nio_ch_Net_initIDs(JNIEnv *env, jclass clazz) { - clazz = (*env)->FindClass(env, "java/net/Inet4Address"); - ia_class = (*env)->NewGlobalRef(env, clazz); - ia_addrID = (*env)->GetFieldID(env, clazz, "address", "I"); - ia_famID = (*env)->GetFieldID(env, clazz, "family", "I"); - ia_ctorID = (*env)->GetMethodID(env, clazz, "", "()V"); + /* nothing to do */ } +JNIEXPORT jboolean JNICALL +Java_sun_nio_ch_Net_isIPv6Available0(JNIEnv* env, jclass cl) +{ + /* + * Return true if Windows Vista or newer, and IPv6 is configured + */ + OSVERSIONINFO ver; + ver.dwOSVersionInfoSize = sizeof(ver); + GetVersionEx(&ver); + if ((ver.dwPlatformId == VER_PLATFORM_WIN32_NT) && + (ver.dwMajorVersion >= 6) && ipv6_available()) + { + return JNI_TRUE; + } + return JNI_FALSE; +} JNIEXPORT jint JNICALL -Java_sun_nio_ch_Net_socket0(JNIEnv *env, jclass cl, jboolean stream, - jboolean reuse) +Java_sun_nio_ch_Net_socket0(JNIEnv *env, jclass cl, jboolean preferIPv6, + jboolean stream, jboolean reuse) { SOCKET s; + int domain = (preferIPv6) ? AF_INET6 : AF_INET; - s = socket(AF_INET, (stream ? SOCK_STREAM : SOCK_DGRAM), 0); + s = socket(domain, (stream ? SOCK_STREAM : SOCK_DGRAM), 0); if (s != INVALID_SOCKET) { SetHandleInformation((HANDLE)s, HANDLE_FLAG_INHERIT, 0); + + /* IPV6_V6ONLY is true by default */ + if (domain == AF_INET6) { + int opt = 0; + setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, + (const char *)&opt, sizeof(opt)); + } } else { NET_ThrowNew(env, WSAGetLastError(), "socket"); } + return (jint)s; } JNIEXPORT void JNICALL -Java_sun_nio_ch_Net_bind(JNIEnv *env, jclass clazz, - jobject fdo, jobject iao, jint port) +Java_sun_nio_ch_Net_bind0(JNIEnv *env, jclass clazz, jboolean preferIPv6, + jobject fdo, jobject iao, jint port) { SOCKETADDRESS sa; int rv; - int sa_len = sizeof(sa); + int sa_len; - if (NET_InetAddressToSockaddr(env, iao, port, (struct sockaddr *)&sa, &sa_len, JNI_FALSE) != 0) { + if (NET_InetAddressToSockaddr(env, iao, port, (struct sockaddr *)&sa, &sa_len, preferIPv6) != 0) { return; } @@ -89,16 +133,25 @@ NET_ThrowNew(env, WSAGetLastError(), "bind"); } +JNIEXPORT void JNICALL +Java_sun_nio_ch_Net_listen(JNIEnv *env, jclass cl, jobject fdo, jint backlog) +{ + if (listen(fdval(env,fdo), backlog) == SOCKET_ERROR) { + NET_ThrowNew(env, WSAGetLastError(), "listen"); + } +} + + JNIEXPORT jint JNICALL -Java_sun_nio_ch_Net_connect(JNIEnv *env, jclass clazz, jobject fdo, jobject iao, - jint port, jint trafficClass) +Java_sun_nio_ch_Net_connect0(JNIEnv *env, jclass clazz, jboolean preferIPv6, jobject fdo, + jobject iao, jint port) { SOCKETADDRESS sa; int rv; - int sa_len = sizeof(sa); + int sa_len; - if (NET_InetAddressToSockaddr(env, iao, port, (struct sockaddr *)&sa, &sa_len, JNI_FALSE) != 0) { - return IOS_THROWN; + if (NET_InetAddressToSockaddr(env, iao, port, (struct sockaddr *)&sa, &sa_len, preferIPv6) != 0) { + return IOS_THROWN; } rv = connect(fdval(env, fdo), (struct sockaddr *)&sa, sa_len); @@ -116,7 +169,7 @@ JNIEXPORT jint JNICALL Java_sun_nio_ch_Net_localPort(JNIEnv *env, jclass clazz, jobject fdo) { - struct sockaddr_in sa; + SOCKETADDRESS sa; int sa_len = sizeof(sa); if (getsockname(fdval(env, fdo), (struct sockaddr *)&sa, &sa_len) < 0) { @@ -127,50 +180,64 @@ NET_ThrowNew(env, error, "getsockname"); return IOS_THROWN; } - return (jint)ntohs(sa.sin_port); + return NET_GetPortFromSockaddr((struct sockaddr *)&sa); } JNIEXPORT jobject JNICALL Java_sun_nio_ch_Net_localInetAddress(JNIEnv *env, jclass clazz, jobject fdo) { - struct sockaddr_in sa; + SOCKETADDRESS sa; int sa_len = sizeof(sa); - jobject iao; + int port; if (getsockname(fdval(env, fdo), (struct sockaddr *)&sa, &sa_len) < 0) { NET_ThrowNew(env, WSAGetLastError(), "getsockname"); return NULL; } - - iao = (*env)->NewObject(env, ia_class, ia_ctorID); - if (iao == NULL) { - JNU_ThrowOutOfMemoryError(env, "heap allocation failure"); - } else { - (*env)->SetIntField(env, iao, ia_addrID, ntohl(sa.sin_addr.s_addr)); - } - - return iao; + return NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa, &port); } - JNIEXPORT jint JNICALL -Java_sun_nio_ch_Net_getIntOption0(JNIEnv *env, jclass clazz, - jobject fdo, jint opt) +Java_sun_nio_ch_Net_remotePort(JNIEnv *env, jclass clazz, jobject fdo) { - int klevel, kopt; - int result; + SOCKETADDRESS sa; + int sa_len = sizeof(sa); + + if (getpeername(fdval(env, fdo), (struct sockaddr *)&sa, &sa_len) < 0) { + int error = WSAGetLastError(); + if (error == WSAEINVAL) { + return 0; + } + NET_ThrowNew(env, error, "getsockname"); + return IOS_THROWN; + } + return NET_GetPortFromSockaddr((struct sockaddr *)&sa); +} + +JNIEXPORT jobject JNICALL +Java_sun_nio_ch_Net_remoteInetAddress(JNIEnv *env, jclass clazz, jobject fdo) +{ + SOCKETADDRESS sa; + int sa_len = sizeof(sa); + int port; + + if (getpeername(fdval(env, fdo), (struct sockaddr *)&sa, &sa_len) < 0) { + NET_ThrowNew(env, WSAGetLastError(), "getsockname"); + return NULL; + } + return NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa, &port); +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_getIntOption0(JNIEnv *env, jclass clazz, jobject fdo, + jboolean mayNeedConversion, jint level, jint opt) +{ + int result = 0; struct linger linger; char *arg; - int arglen; + int arglen, n; - if (NET_MapSocketOption(opt, &klevel, &kopt) < 0) { - JNU_ThrowByNameWithLastError(env, - JNU_JAVANETPKG "SocketException", - "Unsupported socket option"); - return IOS_THROWN; - } - - if (opt == java_net_SocketOptions_SO_LINGER) { + if (level == SOL_SOCKET && opt == SO_LINGER) { arg = (char *)&linger; arglen = sizeof(linger); } else { @@ -178,34 +245,40 @@ arglen = sizeof(result); } - if (NET_GetSockOpt(fdval(env, fdo), klevel, kopt, arg, &arglen) < 0) { - NET_ThrowNew(env, WSAGetLastError(), "sun.nio.ch.Net.setIntOption"); + /** + * HACK: IP_TOS is deprecated on Windows and querying the option + * returns a protocol error. NET_GetSockOpt handles this and uses + * a fallback mechanism. + */ + if (level == IPPROTO_IP && opt == IP_TOS) { + mayNeedConversion = JNI_TRUE; + } + + if (mayNeedConversion) { + n = NET_GetSockOpt(fdval(env, fdo), level, opt, arg, &arglen); + } else { + n = getsockopt(fdval(env, fdo), level, opt, arg, &arglen); + } + if (n < 0) { + handleSocketError(env, WSAGetLastError()); return IOS_THROWN; } - if (opt == java_net_SocketOptions_SO_LINGER) + if (level == SOL_SOCKET && opt == SO_LINGER) return linger.l_onoff ? linger.l_linger : -1; else return result; } JNIEXPORT void JNICALL -Java_sun_nio_ch_Net_setIntOption0(JNIEnv *env, jclass clazz, - jobject fdo, jint opt, jint arg) +Java_sun_nio_ch_Net_setIntOption0(JNIEnv *env, jclass clazz, jobject fdo, + jboolean mayNeedConversion, jint level, jint opt, jint arg) { - int klevel, kopt; struct linger linger; char *parg; - int arglen; + int arglen, n; - if (NET_MapSocketOption(opt, &klevel, &kopt) < 0) { - JNU_ThrowByNameWithLastError(env, - JNU_JAVANETPKG "SocketException", - "Unsupported socket option"); - return; - } - - if (opt == java_net_SocketOptions_SO_LINGER) { + if (level == SOL_SOCKET && opt == SO_LINGER) { parg = (char *)&linger; arglen = sizeof(linger); if (arg >= 0) { @@ -220,7 +293,200 @@ arglen = sizeof(arg); } - if (NET_SetSockOpt(fdval(env, fdo), klevel, kopt, parg, arglen) < 0) { - NET_ThrowNew(env, WSAGetLastError(), "sun.nio.ch.Net.setIntOption"); + if (mayNeedConversion) { + n = NET_SetSockOpt(fdval(env, fdo), level, opt, parg, arglen); + } else { + n = setsockopt(fdval(env, fdo), level, opt, parg, arglen); + } + if (n < 0) + handleSocketError(env, WSAGetLastError()); +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_joinOrDrop4(JNIEnv *env, jobject this, jboolean join, jobject fdo, + jint group, jint interf, jint source) +{ + struct ip_mreq mreq; + struct my_ip_mreq_source mreq_source; + int opt, n, optlen; + void* optval; + + if (source == 0) { + mreq.imr_multiaddr.s_addr = htonl(group); + mreq.imr_interface.s_addr = htonl(interf); + opt = (join) ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; + optval = (void*)&mreq; + optlen = sizeof(mreq); + } else { + mreq_source.imr_multiaddr.s_addr = htonl(group); + mreq_source.imr_sourceaddr.s_addr = htonl(source); + mreq_source.imr_interface.s_addr = htonl(interf); + opt = (join) ? IP_ADD_SOURCE_MEMBERSHIP : IP_DROP_SOURCE_MEMBERSHIP; + optval = (void*)&mreq_source; + optlen = sizeof(mreq_source); + } + + n = setsockopt(fdval(env,fdo), IPPROTO_IP, opt, optval, optlen); + if (n < 0) { + if (join && (WSAGetLastError() == WSAENOPROTOOPT)) + return IOS_UNAVAILABLE; + handleSocketError(env, WSAGetLastError()); + } + return 0; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_blockOrUnblock4(JNIEnv *env, jobject this, jboolean block, jobject fdo, + jint group, jint interf, jint source) +{ + struct my_ip_mreq_source mreq_source; + int n; + int opt = (block) ? IP_BLOCK_SOURCE : IP_UNBLOCK_SOURCE; + + mreq_source.imr_multiaddr.s_addr = htonl(group); + mreq_source.imr_sourceaddr.s_addr = htonl(source); + mreq_source.imr_interface.s_addr = htonl(interf); + + n = setsockopt(fdval(env,fdo), IPPROTO_IP, opt, + (void*)&mreq_source, sizeof(mreq_source)); + if (n < 0) { + if (block && (WSAGetLastError() == WSAENOPROTOOPT)) + return IOS_UNAVAILABLE; + handleSocketError(env, WSAGetLastError()); + } + return 0; +} + +/** + * Call setsockopt with a IPPROTO_IPV6 level socket option + * and a group_source_req structure as the option value. The + * given IPv6 group, interface index, and IPv6 source address + * are copied into the structure. + */ +static int setGroupSourceReqOption(JNIEnv* env, + jobject fdo, + int opt, + jbyteArray group, + jint index, + jbyteArray source) +{ + struct my_group_source_req req; + struct sockaddr_in6* sin6; + + req.gsr_interface = (ULONG)index; + + sin6 = (struct sockaddr_in6*)&(req.gsr_group); + sin6->sin6_family = AF_INET6; + COPY_INET6_ADDRESS(env, group, (jbyte*)&(sin6->sin6_addr)); + + sin6 = (struct sockaddr_in6*)&(req.gsr_source); + sin6->sin6_family = AF_INET6; + COPY_INET6_ADDRESS(env, source, (jbyte*)&(sin6->sin6_addr)); + + return setsockopt(fdval(env,fdo), IPPROTO_IPV6, opt, (void*)&req, sizeof(req)); +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_joinOrDrop6(JNIEnv *env, jobject this, jboolean join, jobject fdo, + jbyteArray group, jint index, jbyteArray source) +{ + struct ipv6_mreq mreq6; + int n; + + if (source == NULL) { + int opt = (join) ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP; + COPY_INET6_ADDRESS(env, group, (jbyte*)&(mreq6.ipv6mr_multiaddr)); + mreq6.ipv6mr_interface = (int)index; + n = setsockopt(fdval(env,fdo), IPPROTO_IPV6, opt, + (void*)&mreq6, sizeof(mreq6)); + } else { + int opt = (join) ? MCAST_JOIN_SOURCE_GROUP : MCAST_LEAVE_SOURCE_GROUP; + n = setGroupSourceReqOption(env, fdo, opt, group, index, source); + } + + if (n < 0) { + handleSocketError(env, errno); + } + return 0; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_blockOrUnblock6(JNIEnv *env, jobject this, jboolean block, jobject fdo, + jbyteArray group, jint index, jbyteArray source) +{ + int opt = (block) ? MCAST_BLOCK_SOURCE : MCAST_UNBLOCK_SOURCE; + int n = setGroupSourceReqOption(env, fdo, opt, group, index, source); + if (n < 0) { + handleSocketError(env, errno); + } + return 0; +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_Net_setInterface4(JNIEnv* env, jobject this, jobject fdo, jint interf) +{ + struct in_addr in; + int arglen = sizeof(struct in_addr); + int n; + + in.s_addr = htonl(interf); + + n = setsockopt(fdval(env, fdo), IPPROTO_IP, IP_MULTICAST_IF, + (void*)&(in.s_addr), arglen); + if (n < 0) { + handleSocketError(env, WSAGetLastError()); } } + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_getInterface4(JNIEnv* env, jobject this, jobject fdo) +{ + struct in_addr in; + int arglen = sizeof(struct in_addr); + int n; + + n = getsockopt(fdval(env, fdo), IPPROTO_IP, IP_MULTICAST_IF, (void*)&in, &arglen); + if (n < 0) { + handleSocketError(env, WSAGetLastError()); + return IOS_THROWN; + } + return ntohl(in.s_addr); +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_Net_setInterface6(JNIEnv* env, jobject this, jobject fdo, jint index) +{ + int value = (jint)index; + int arglen = sizeof(value); + int n; + + n = setsockopt(fdval(env, fdo), IPPROTO_IPV6, IPV6_MULTICAST_IF, + (void*)&(index), arglen); + if (n < 0) { + handleSocketError(env, errno); + } +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_getInterface6(JNIEnv* env, jobject this, jobject fdo) +{ + int index; + int arglen = sizeof(index); + int n; + + n = getsockopt(fdval(env, fdo), IPPROTO_IPV6, IPV6_MULTICAST_IF, (void*)&index, &arglen); + if (n < 0) { + handleSocketError(env, errno); + return -1; + } + return (jint)index; +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_Net_shutdown(JNIEnv *env, jclass cl, jobject fdo, jint jhow) { + int how = (jhow == sun_nio_ch_Net_SHUT_RD) ? SD_RECEIVE : + (jhow == sun_nio_ch_Net_SHUT_WR) ? SD_SEND : SD_BOTH; + if (shutdown(fdval(env, fdo), how) == SOCKET_ERROR) { + NET_ThrowNew(env, WSAGetLastError(), "shutdown"); + } +} diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/windows/native/sun/nio/ch/ServerSocketChannelImpl.c --- a/jdk/src/windows/native/sun/nio/ch/ServerSocketChannelImpl.c Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/windows/native/sun/nio/ch/ServerSocketChannelImpl.c Sun Aug 31 18:39:01 2008 +0100 @@ -46,10 +46,6 @@ static jfieldID fd_fdID; /* java.io.FileDescriptor.fd */ static jclass isa_class; /* java.net.InetSocketAddress */ static jmethodID isa_ctorID; /* InetSocketAddress(InetAddress, int) */ -static jclass ia_class; /* java.net.InetAddress */ -static jmethodID ia_ctorID; /* InetAddress() */ -static jfieldID ia_addrID; /* java.net.InetAddress.address */ -static jfieldID ia_famID; /* java.net.InetAddress.family */ /************************************************************** @@ -66,12 +62,6 @@ isa_class = (*env)->NewGlobalRef(env, cls); isa_ctorID = (*env)->GetMethodID(env, cls, "", "(Ljava/net/InetAddress;I)V"); - - cls = (*env)->FindClass(env, "java/net/Inet4Address"); - ia_class = (*env)->NewGlobalRef(env, cls); - ia_ctorID = (*env)->GetMethodID(env, cls, "","()V"); - ia_addrID = (*env)->GetFieldID(env, cls, "address", "I"); - ia_famID = (*env)->GetFieldID(env, cls, "family", "I"); } JNIEXPORT void JNICALL @@ -90,8 +80,9 @@ { jint ssfd = (*env)->GetIntField(env, ssfdo, fd_fdID); jint newfd; - struct sockaddr_in sa; - jobject remote_ia = 0; + SOCKETADDRESS sa; + jobject remote_ia; + int remote_port; jobject isa; jobject ia; int addrlen = sizeof(sa); @@ -106,14 +97,13 @@ JNU_ThrowIOExceptionWithLastError(env, "Accept failed"); return IOS_THROWN; } + (*env)->SetIntField(env, newfdo, fd_fdID, newfd); + remote_ia = NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa, (int *)&remote_port); - ia = (*env)->NewObject(env, ia_class, ia_ctorID); - (*env)->SetIntField(env, ia, ia_addrID, ntohl(sa.sin_addr.s_addr)); - (*env)->SetIntField(env, ia, ia_famID, sa.sin_family); + isa = (*env)->NewObject(env, isa_class, isa_ctorID, + remote_ia, remote_port); + (*env)->SetObjectArrayElement(env, isaa, 0, isa); - isa = (*env)->NewObject(env, isa_class, isa_ctorID, ia, - ntohs(sa.sin_port)); - (*env)->SetObjectArrayElement(env, isaa, 0, isa); return 1; } diff -r 4070cecdb99d -r 29d6145d1097 jdk/src/windows/native/sun/nio/ch/SocketChannelImpl.c --- a/jdk/src/windows/native/sun/nio/ch/SocketChannelImpl.c Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/src/windows/native/sun/nio/ch/SocketChannelImpl.c Sun Aug 31 18:39:01 2008 +0100 @@ -139,12 +139,3 @@ return 0; } - -JNIEXPORT void JNICALL -Java_sun_nio_ch_SocketChannelImpl_shutdown(JNIEnv *env, jclass cl, - jobject fdo, jint how) -{ - if (shutdown(fdval(env, fdo), how) == SOCKET_ERROR) { - NET_ThrowNew(env, WSAGetLastError(), "shutdown"); - } -} diff -r 4070cecdb99d -r 29d6145d1097 jdk/test/java/nio/channels/DatagramChannel/BasicMulticastTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/nio/channels/DatagramChannel/BasicMulticastTests.java Sun Aug 31 18:39:01 2008 +0100 @@ -0,0 +1,220 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 4527345 + * @summary Unit test for DatagramChannel's multicast support + * @build BasicMulticastTests NetworkConfiguration + */ + +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.net.*; +import java.util.*; +import java.io.IOException; + +public class BasicMulticastTests { + + /** + * Tests that existing membership key is returned by join methods and that + * membership key methods return the expected results + */ + static void membershipKeyTests(NetworkInterface nif, + InetAddress group, + InetAddress source) + throws IOException + { + System.out.format("MembershipKey test using %s @ %s\n", + group.getHostAddress(), nif.getName()); + + ProtocolFamily family = (group instanceof Inet4Address) ? + StandardProtocolFamily.INET : StandardProtocolFamily.INET6; + + DatagramChannel dc = DatagramChannel.open(family) + .setOption(StandardSocketOption.SO_REUSEADDR, true) + .bind(new InetSocketAddress(source, 0)); + + // check existing key is returned + MembershipKey key = dc.join(group, nif); + MembershipKey other = dc.join(group, nif); + if (other != key) { + throw new RuntimeException("existing key not returned"); + } + + // check key + if (!key.isValid()) + throw new RuntimeException("key is not valid"); + if (!key.getGroup().equals(group)) + throw new RuntimeException("group is incorrect"); + if (!key.getNetworkInterface().equals(nif)) + throw new RuntimeException("network interface is incorrect"); + if (key.getSourceAddress() != null) + throw new RuntimeException("key is source specific"); + + // drop membership + key.drop(); + if (key.isValid()) { + throw new RuntimeException("key is still valid"); + } + + // source-specific + try { + key = dc.join(group, nif, source); + other = dc.join(group, nif, source); + if (other != key) { + throw new RuntimeException("existing key not returned"); + } + if (!key.isValid()) + throw new RuntimeException("key is not valid"); + if (!key.getGroup().equals(group)) + throw new RuntimeException("group is incorrect"); + if (!key.getNetworkInterface().equals(nif)) + throw new RuntimeException("network interface is incorrect"); + if (!key.getSourceAddress().equals(source)) + throw new RuntimeException("key's source address incorrect"); + + // drop membership + key.drop(); + if (key.isValid()) { + throw new RuntimeException("key is still valid"); + } + } catch (UnsupportedOperationException x) { + } + + // done + dc.close(); + } + + /** + * Tests exceptions for invalid arguments or scenarios + */ + static void exceptionTests(NetworkInterface nif) + throws IOException + { + System.out.println("Exception Tests"); + + DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET) + .setOption(StandardSocketOption.SO_REUSEADDR, true) + .bind(new InetSocketAddress(0)); + + InetAddress group = InetAddress.getByName("225.4.5.6"); + InetAddress notGroup = InetAddress.getByName("1.2.3.4"); + InetAddress thisHost = InetAddress.getLocalHost(); + + // IllegalStateException + MembershipKey key; + key = dc.join(group, nif); + try { + dc.join(group, nif, thisHost); + throw new RuntimeException("IllegalStateException not thrown"); + } catch (IllegalStateException x) { + } catch (UnsupportedOperationException x) { + } + key.drop(); + try { + key = dc.join(group, nif, thisHost); + try { + dc.join(group, nif); + throw new RuntimeException("IllegalStateException not thrown"); + } catch (IllegalStateException x) { + } + key.drop(); + } catch (UnsupportedOperationException x) { + } + + // IllegalArgumentException + try { + dc.join(notGroup, nif); + throw new RuntimeException("IllegalArgumentException not thrown"); + } catch (IllegalArgumentException x) { + } + try { + dc.join(notGroup, nif, thisHost); + throw new RuntimeException("IllegalArgumentException not thrown"); + } catch (IllegalArgumentException x) { + } catch (UnsupportedOperationException x) { + } + + // NullPointerException + try { + dc.join(null, nif); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + try { + dc.join(group, null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + try { + dc.join(group, nif, null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } catch (UnsupportedOperationException x) { + } + + dc.close(); + + // ClosedChannelException + try { + dc.join(group, nif); + throw new RuntimeException("ClosedChannelException not thrown"); + } catch (ClosedChannelException x) { + } + try { + dc.join(group, nif, thisHost); + throw new RuntimeException("ClosedChannelException not thrown"); + } catch (ClosedChannelException x) { + } catch (UnsupportedOperationException x) { + } + } + + + /** + * Probe interfaces to get interfaces that support IPv4 or IPv6 multicasting + * and invoke tests. + */ + public static void main(String[] args) throws IOException { + + // multicast groups used for the test + InetAddress ip4Group = InetAddress.getByName("225.4.5.6"); + InetAddress ip6Group = InetAddress.getByName("ff02::a"); + + + NetworkConfiguration config = NetworkConfiguration.probe(); + + NetworkInterface nif = config.ip4Interfaces().iterator().next(); + InetAddress anySource = config.ip4Addresses(nif).iterator().next(); + membershipKeyTests(nif, ip4Group, anySource); + exceptionTests(nif); + + // re-run the membership key tests with IPv6 if available + + Iterator iter = config.ip6Interfaces().iterator(); + if (iter.hasNext()) { + nif = iter.next(); + anySource = config.ip6Addresses(nif).iterator().next(); + membershipKeyTests(nif, ip6Group, anySource); + } + } +} diff -r 4070cecdb99d -r 29d6145d1097 jdk/test/java/nio/channels/DatagramChannel/MulticastSendReceiveTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/nio/channels/DatagramChannel/MulticastSendReceiveTests.java Sun Aug 31 18:39:01 2008 +0100 @@ -0,0 +1,220 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 4527345 + * @summary Unit test for DatagramChannel's multicast support + * @build MulticastSendReceiveTests NetworkConfiguration + */ + +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.net.*; +import java.util.*; +import java.io.IOException; + +public class MulticastSendReceiveTests { + + static Random rand = new Random(); + + /** + * Send datagram from given local address to given multicast + * group. + */ + static int sendDatagram(InetAddress local, + NetworkInterface nif, + InetAddress group, + int port) + throws IOException + { + ProtocolFamily family = (group instanceof Inet6Address) ? + StandardProtocolFamily.INET6 : StandardProtocolFamily.INET; + DatagramChannel dc = DatagramChannel.open(family) + .bind(new InetSocketAddress(local, 0)) + .setOption(StandardSocketOption.IP_MULTICAST_IF, nif); + int id = rand.nextInt(); + byte[] msg = Integer.toString(id).getBytes("UTF-8"); + ByteBuffer buf = ByteBuffer.wrap(msg); + System.out.format("Send message from %s -> group %s (id=0x%x)\n", + local.getHostAddress(), group.getHostAddress(), id); + dc.send(buf, new InetSocketAddress(group, port)); + dc.close(); + return id; + } + + /** + * Wait (with timeout) for datagram. + * + * @param expectedSender - expected sender address, or + * null if no datagram expected + * @param id - expected id of datagram + */ + static void receiveDatagram(DatagramChannel dc, + InetAddress expectedSender, + int id) + throws IOException + { + Selector sel = Selector.open(); + dc.configureBlocking(false); + dc.register(sel, SelectionKey.OP_READ); + ByteBuffer buf = ByteBuffer.allocateDirect(100); + + try { + for (;;) { + System.out.println("Waiting to receive message"); + sel.select(5*1000); + SocketAddress sa = dc.receive(buf); + + // no datagram received + if (sa == null) { + if (expectedSender != null) { + throw new RuntimeException("Expected message not recieved"); + } + System.out.println("No message received (correct)"); + return; + } + + // datagram received + + InetAddress sender = ((InetSocketAddress)sa).getAddress(); + buf.flip(); + byte[] bytes = new byte[buf.remaining()]; + buf.get(bytes); + int receivedId = Integer.parseInt(new String(bytes)); + + System.out.format("Received message from %s (id=0x%x)\n", + sender, receivedId); + + if (expectedSender == null) { + if (receivedId == id) + throw new RuntimeException("Message not expected"); + System.out.println("Message ignored (has wrong id)"); + } else { + if (sender.equals(expectedSender)) { + System.out.println("Message expected"); + return; + } + System.out.println("Message ignored (wrong sender)"); + } + + sel.selectedKeys().clear(); + buf.rewind(); + } + } finally { + sel.close(); + } + } + + + /** + * Exercise multicast send/receive on given group/interface + */ + static void test(NetworkInterface nif, InetAddress group, InetAddress source) + throws IOException + { + ProtocolFamily family = (group instanceof Inet6Address) ? + StandardProtocolFamily.INET6 : StandardProtocolFamily.INET; + System.out.format("create channel to %s socket\n", family.name()); + DatagramChannel dc = DatagramChannel.open(family) + .setOption(StandardSocketOption.SO_REUSEADDR, true) + .bind(new InetSocketAddress(0)); + + // join group + System.out.format("join %s @ %s\n", group.getHostAddress(), + nif.getName()); + MembershipKey key = dc.join(group, nif); + + // send message to group + int port = ((InetSocketAddress)dc.getLocalAddress()).getPort(); + int id = sendDatagram(source, nif, group, port); + + // receive message and check id matches + receiveDatagram(dc, source, id); + + // exclude-mode filtering + + try { + System.out.format("block %s\n", source.getHostAddress()); + + // may throw UOE + key.block(source); + id = sendDatagram(source, nif, group, port); + receiveDatagram(dc, null, id); + + // unblock source, send message, message should be received + System.out.format("unblock %s\n", source.getHostAddress()); + key.unblock(source); + id = sendDatagram(source, nif, group, port); + receiveDatagram(dc, source, id); + } catch (UnsupportedOperationException x) { + System.out.println("Exclude-mode filtering not supported!"); + } + + key.drop(); + + // include-mode filtering + + InetAddress bogus = (group instanceof Inet6Address) ? + InetAddress.getByName("fe80::1234") : + InetAddress.getByName("1.2.3.4"); + System.out.format("join %s @ %s only-source %s\n", group.getHostAddress(), + nif.getName(), bogus.getHostAddress()); + try { + // may throw UOE + key = dc.join(group, nif, bogus); + + id = sendDatagram(source, nif, group, port); + receiveDatagram(dc, null, id); + + System.out.format("join %s @ %s only-source %s\n", group.getHostAddress(), + nif.getName(), source.getHostAddress()); + key = dc.join(group, nif, source); + + id = sendDatagram(source, nif, group, port); + receiveDatagram(dc, source, id); + } catch (UnsupportedOperationException x) { + System.out.println("Include-mode filtering not supported!"); + } + + // done + dc.close(); + } + + public static void main(String[] args) throws IOException { + NetworkConfiguration config = NetworkConfiguration.probe(); + + // multicast groups used for the test + InetAddress ip4Group = InetAddress.getByName("225.4.5.6"); + InetAddress ip6Group = InetAddress.getByName("ff02::a"); + + for (NetworkInterface nif: config.ip4Interfaces()) { + InetAddress source = config.ip4Addresses(nif).iterator().next(); + test(nif, ip4Group, source); + } + + for (NetworkInterface nif: config.ip6Interfaces()) { + InetAddress source = config.ip6Addresses(nif).iterator().next(); + test(nif, ip6Group, source); + } + } +} diff -r 4070cecdb99d -r 29d6145d1097 jdk/test/java/nio/channels/DatagramChannel/NetworkConfiguration.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/nio/channels/DatagramChannel/NetworkConfiguration.java Sun Aug 31 18:39:01 2008 +0100 @@ -0,0 +1,97 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import java.net.*; +import java.util.*; +import java.io.IOException; + +/** + * Helper class for multicasting tests. + */ + +class NetworkConfiguration { + + private Map> ip4Interfaces; + private Map> ip6Interfaces; + + private NetworkConfiguration(Map> ip4Interfaces, + Map> ip6Interfaces) + { + this.ip4Interfaces = ip4Interfaces; + this.ip6Interfaces = ip6Interfaces; + } + + Iterable ip4Interfaces() { + return ip4Interfaces.keySet(); + } + + Iterable ip6Interfaces() { + return ip6Interfaces.keySet(); + } + + Iterable ip4Addresses(NetworkInterface nif) { + return ip4Interfaces.get(nif); + } + + Iterable ip6Addresses(NetworkInterface nif) { + return ip6Interfaces.get(nif); + } + + static NetworkConfiguration probe() throws IOException { + Map> ip4Interfaces = + new HashMap>(); + Map> ip6Interfaces = + new HashMap>(); + + // find the interfaces that support IPv4 and IPv6 + List nifs = Collections + .list(NetworkInterface.getNetworkInterfaces()); + for (NetworkInterface nif: nifs) { + // ignore intertaces that are down or don't support multicast + if (!nif.isUp() || !nif.supportsMulticast() || nif.isLoopback()) + continue; + + List addrs = Collections.list(nif.getInetAddresses()); + for (InetAddress addr: addrs) { + if (addr instanceof Inet4Address) { + List list = ip4Interfaces.get(nif); + if (list == null) { + list = new LinkedList(); + } + list.add(addr); + ip4Interfaces.put(nif, list); + } + if (addr instanceof Inet6Address) { + List list = ip6Interfaces.get(nif); + if (list == null) { + list = new LinkedList(); + } + list.add(addr); + ip6Interfaces.put(nif, list); + + } + } + } + return new NetworkConfiguration(ip4Interfaces, ip6Interfaces); + } +} diff -r 4070cecdb99d -r 29d6145d1097 jdk/test/java/nio/channels/DatagramChannel/SocketOptionTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/nio/channels/DatagramChannel/SocketOptionTests.java Sun Aug 31 18:39:01 2008 +0100 @@ -0,0 +1,115 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 4640544 + * @summary Unit test for setOption/getOption/options methods + */ + +import java.nio.*; +import java.nio.channels.*; +import java.net.*; +import java.io.IOException; +import java.util.*; +import static java.net.StandardSocketOption.*; + +public class SocketOptionTests { + + static void checkOption(DatagramChannel dc, + SocketOption name, + T expectedValue) + throws IOException + { + T value = dc.getOption(name); + if (!value.equals(expectedValue)) + throw new RuntimeException("value not as expected"); + } + + public static void main(String[] args) throws IOException { + DatagramChannel dc = DatagramChannel.open(); + + // check supported options + Set> options = dc.options(); + List> expected = Arrays.asList(SO_SNDBUF, SO_RCVBUF, + SO_REUSEADDR, SO_BROADCAST, IP_TOS, IP_MULTICAST_IF, IP_MULTICAST_TTL, + IP_MULTICAST_LOOP); + for (SocketOption opt: expected) { + if (!options.contains(opt)) + throw new RuntimeException(opt.name() + " should be supported"); + } + + // check specified defaults + checkOption(dc, SO_BROADCAST, false); + checkOption(dc, IP_MULTICAST_TTL, 1); // true on supported platforms + checkOption(dc, IP_MULTICAST_LOOP, true); // true on supported platforms + + // allowed to change when not bound + dc.setOption(SO_BROADCAST, true); + checkOption(dc, SO_BROADCAST, true); + dc.setOption(SO_BROADCAST, false); + checkOption(dc, SO_BROADCAST, false); + dc.setOption(SO_SNDBUF, 16*1024); // can't check + dc.setOption(SO_RCVBUF, 16*1024); // can't check + dc.setOption(SO_REUSEADDR, true); + checkOption(dc, SO_REUSEADDR, true); + dc.setOption(SO_REUSEADDR, false); + checkOption(dc, SO_REUSEADDR, false); + + // bind socket + dc.bind(new InetSocketAddress(0)); + + // allow to change when bound + dc.setOption(SO_BROADCAST, true); + checkOption(dc, SO_BROADCAST, true); + dc.setOption(SO_BROADCAST, false); + checkOption(dc, SO_BROADCAST, false); + dc.setOption(IP_TOS, 0x08); // can't check + dc.setOption(IP_MULTICAST_TTL, 2); + checkOption(dc, IP_MULTICAST_TTL, 2); + dc.setOption(IP_MULTICAST_LOOP, false); + checkOption(dc, IP_MULTICAST_LOOP, false); + dc.setOption(IP_MULTICAST_LOOP, true); + checkOption(dc, IP_MULTICAST_LOOP, true); + + + // NullPointerException + try { + dc.setOption(null, "value"); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + try { + dc.getOption(null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + + // ClosedChannelException + dc.close(); + try { + dc.setOption(IP_MULTICAST_LOOP, true); + throw new RuntimeException("ClosedChannelException not thrown"); + } catch (ClosedChannelException x) { + } + } +} diff -r 4070cecdb99d -r 29d6145d1097 jdk/test/java/nio/channels/ServerSocketChannel/SocketOptionTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/nio/channels/ServerSocketChannel/SocketOptionTests.java Sun Aug 31 18:39:01 2008 +0100 @@ -0,0 +1,84 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 4640544 + * @summary Unit test for ServerSocketChannel setOption/getOption/options + * methods. + */ + +import java.nio.*; +import java.nio.channels.*; +import java.net.*; +import java.io.IOException; +import java.util.*; +import static java.net.StandardSocketOption.*; + +public class SocketOptionTests { + + static void checkOption(ServerSocketChannel ssc, SocketOption name, Object expectedValue) + throws IOException + { + Object value = ssc.getOption(name); + if (!value.equals(expectedValue)) + throw new RuntimeException("value not as expected"); + } + + public static void main(String[] args) throws IOException { + ServerSocketChannel ssc = ServerSocketChannel.open(); + + // check supported options + Set> options = ssc.options(); + if (!options.contains(SO_REUSEADDR)) + throw new RuntimeException("SO_REUSEADDR should be supported"); + if (!options.contains(SO_RCVBUF)) + throw new RuntimeException("SO_RCVBUF should be supported"); + + // allowed to change when not bound + ssc.setOption(SO_RCVBUF, 256*1024); // can't check + ssc.setOption(SO_REUSEADDR, true); + checkOption(ssc, SO_REUSEADDR, true); + ssc.setOption(SO_REUSEADDR, false); + checkOption(ssc, SO_REUSEADDR, false); + + // NullPointerException + try { + ssc.setOption(null, "value"); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + try { + ssc.getOption(null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + + // ClosedChannelException + ssc.close(); + try { + ssc.setOption(SO_REUSEADDR, true); + throw new RuntimeException("ClosedChannelException not thrown"); + } catch (ClosedChannelException x) { + } + } +} diff -r 4070cecdb99d -r 29d6145d1097 jdk/test/java/nio/channels/SocketChannel/SocketOptionTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/nio/channels/SocketChannel/SocketOptionTests.java Sun Aug 31 18:39:01 2008 +0100 @@ -0,0 +1,129 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 4640544 + * @summary Unit test to check SocketChannel setOption/getOption/options + * methods. + */ + +import java.nio.*; +import java.nio.channels.*; +import java.net.*; +import java.io.IOException; +import java.util.*; +import static java.net.StandardSocketOption.*; + +public class SocketOptionTests { + + static void checkOption(SocketChannel sc, SocketOption name, Object expectedValue) + throws IOException + { + Object value = sc.getOption(name); + if (!value.equals(expectedValue)) + throw new RuntimeException("value not as expected"); + } + + public static void main(String[] args) throws IOException { + SocketChannel sc = SocketChannel.open(); + + // check supported options + Set> options = sc.options(); + List expected = Arrays.asList(SO_SNDBUF, SO_RCVBUF, + SO_KEEPALIVE, SO_REUSEADDR, SO_LINGER, TCP_NODELAY); + for (SocketOption opt: expected) { + if (!options.contains(opt)) + throw new RuntimeException(opt.name() + " should be supported"); + } + + // check specified defaults + int linger = sc.getOption(SO_LINGER); + if (linger >= 0) + throw new RuntimeException("initial value of SO_LINGER should be < 0"); + checkOption(sc, SO_KEEPALIVE, false); + checkOption(sc, TCP_NODELAY, false); + + // allowed to change when not bound + sc.setOption(SO_KEEPALIVE, true); + checkOption(sc, SO_KEEPALIVE, true); + sc.setOption(SO_KEEPALIVE, false); + checkOption(sc, SO_KEEPALIVE, false); + sc.setOption(SO_SNDBUF, 128*1024); // can't check + sc.setOption(SO_RCVBUF, 256*1024); // can't check + sc.setOption(SO_REUSEADDR, true); + checkOption(sc, SO_REUSEADDR, true); + sc.setOption(SO_REUSEADDR, false); + checkOption(sc, SO_REUSEADDR, false); + sc.setOption(SO_LINGER, 10); + linger = sc.getOption(SO_LINGER); + if (linger < 1) + throw new RuntimeException("expected linger to be enabled"); + sc.setOption(SO_LINGER, -1); + linger = sc.getOption(SO_LINGER); + if (linger >= 0) + throw new RuntimeException("expected linger to be disabled"); + sc.setOption(TCP_NODELAY, true); + checkOption(sc, TCP_NODELAY, true); + sc.setOption(TCP_NODELAY, false); // can't check + + // bind socket + sc.bind(new InetSocketAddress(0)); + + // allow to change when bound + sc.setOption(SO_KEEPALIVE, true); + checkOption(sc, SO_KEEPALIVE, true); + sc.setOption(SO_KEEPALIVE, false); + checkOption(sc, SO_KEEPALIVE, false); + + sc.setOption(SO_LINGER, 10); + linger = sc.getOption(SO_LINGER); + if (linger < 1) + throw new RuntimeException("expected linger to be enabled"); + sc.setOption(SO_LINGER, -1); + linger = sc.getOption(SO_LINGER); + if (linger >= 0) + throw new RuntimeException("expected linger to be disabled"); + sc.setOption(TCP_NODELAY, true); // can't check + sc.setOption(TCP_NODELAY, false); // can't check + + // NullPointerException + try { + sc.setOption(null, "value"); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + try { + sc.getOption(null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + + // ClosedChannelException + sc.close(); + try { + sc.setOption(TCP_NODELAY, true); + throw new RuntimeException("ClosedChannelException not thrown"); + } catch (ClosedChannelException x) { + } + } +} diff -r 4070cecdb99d -r 29d6145d1097 jdk/test/java/nio/channels/TestUtil.java --- a/jdk/test/java/nio/channels/TestUtil.java Sun Aug 31 18:32:59 2008 +0100 +++ b/jdk/test/java/nio/channels/TestUtil.java Sun Aug 31 18:39:01 2008 +0100 @@ -38,7 +38,7 @@ // executing in a different network. public static final String HOST = "javaweb.sfbay.sun.com"; public static final String REFUSING_HOST = "jano1.sfbay.sun.com"; - public static final String FAR_HOST = "theclub.ireland.sun.com"; + public static final String FAR_HOST = "irejano.ireland.sun.com"; public static final String UNRESOLVABLE_HOST = "blah-blah.blah-blah.blah"; private TestUtil() { } @@ -102,5 +102,4 @@ static boolean onWindows() { return osName.startsWith("Windows"); } - } diff -r 4070cecdb99d -r 29d6145d1097 jdk/test/java/nio/channels/etc/NetworkChannelTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/nio/channels/etc/NetworkChannelTests.java Sun Aug 31 18:39:01 2008 +0100 @@ -0,0 +1,187 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 4640544 + * @summary Unit test for channels that implement NetworkChannel + */ + +import java.nio.*; +import java.nio.channels.*; +import java.net.*; +import java.io.IOException; +import java.util.*; + +public class NetworkChannelTests { + + static interface ChannelFactory { + NetworkChannel open() throws IOException; + } + + static class BogusSocketAddress extends SocketAddress { + } + + /** + * Exercise bind method. + */ + static void bindTests(ChannelFactory factory) throws IOException { + NetworkChannel ch; + + // AlreadyBoundException + ch = factory.open().bind(new InetSocketAddress(0)); + try { + ch.bind(new InetSocketAddress(0)); + throw new RuntimeException("AlreadyBoundException not thrown"); + } catch (AlreadyBoundException x) { + } + ch.close(); + + // bind(null) + ch = factory.open().bind(null); + if (ch.getLocalAddress() == null) + throw new RuntimeException("socket not found"); + ch.close(); + + // UnsupportedAddressTypeException + ch = factory.open(); + try { + ch.bind(new BogusSocketAddress()); + throw new RuntimeException("UnsupportedAddressTypeException not thrown"); + } catch (UnsupportedAddressTypeException x) { + } + ch.close(); + + // ClosedChannelException + try { + ch.bind(new InetSocketAddress(0)); + throw new RuntimeException("ClosedChannelException not thrown"); + } catch (ClosedChannelException x) { + } + } + + /** + * Exercise getLocalAddress method. + */ + static void localAddressTests(ChannelFactory factory) throws IOException { + NetworkChannel ch; + + // not bound + ch = factory.open(); + if (ch.getLocalAddress() != null) { + throw new RuntimeException("Local address returned when not bound"); + } + + // bound + InetSocketAddress local = + (InetSocketAddress)(ch.bind(new InetSocketAddress(0)).getLocalAddress()); + if (!local.getAddress().isAnyLocalAddress()) { + if (NetworkInterface.getByInetAddress(local.getAddress()) == null) + throw new RuntimeException("not bound to local address"); + } + if (local.getPort() <= 0) + throw new RuntimeException("not bound to local port"); + + // closed + ch.close(); + if (ch.getLocalAddress() != null) { + throw new RuntimeException("Local address return when closed"); + } + } + + /** + * Exercise getConnectedAddress method (SocketChannel only) + */ + static void connectedAddressTests() throws IOException { + ServerSocketChannel ssc = ServerSocketChannel.open() + .bind(new InetSocketAddress(0)); + InetSocketAddress local = (InetSocketAddress)(ssc.getLocalAddress()); + int port = local.getPort(); + InetSocketAddress server = new InetSocketAddress(InetAddress.getLocalHost(), port); + + SocketChannel sc = SocketChannel.open(); + + // not connected + if (sc.getConnectedAddress() != null) + throw new RuntimeException("getConnectedAddress returned address when not connected"); + + // connected + sc.connect(server); + SocketAddress remote = sc.getConnectedAddress(); + if (!remote.equals(server)) + throw new RuntimeException("getConnectedAddress returned incorrect address"); + + // closed + sc.close(); + if (sc.getConnectedAddress() != null) + throw new RuntimeException("getConnectedAddress returned address when closed"); + + ssc.close(); + } + + public static void main(String[] args) throws IOException { + ChannelFactory factory; + + // -- SocketChannel -- + + factory = new ChannelFactory() { + public NetworkChannel open() throws IOException { + return SocketChannel.open(); + } + }; + + bindTests(factory); + localAddressTests(factory); + connectedAddressTests(); + + // -- ServerSocketChannel -- + + factory = new ChannelFactory() { + public NetworkChannel open() throws IOException { + return ServerSocketChannel.open(); + } + }; + + bindTests(factory); + localAddressTests(factory); + + // backlog values + ServerSocketChannel.open() + .bind(new InetSocketAddress(0), 100).close(); + ServerSocketChannel.open() + .bind(new InetSocketAddress(0), 0).close(); + ServerSocketChannel.open() + .bind(new InetSocketAddress(0), -1).close(); + + // -- DatagramChannel -- + + factory = new ChannelFactory() { + public NetworkChannel open() throws IOException { + return DatagramChannel.open(); + } + }; + + bindTests(factory); + localAddressTests(factory); + } + +}