4927640: Implementation of the sctp protocol
authorchegar
Thu, 16 Apr 2009 17:42:00 +0100
changeset 2542 d859108aea12
parent 2418 15096652c4d4
child 2543 825f6628794a
4927640: Implementation of the sctp protocol Summary: An implementation-specific API for the Stream Control Transmission Protocol Reviewed-by: alanb, michaelm, jccollet
jdk/make/com/sun/Makefile
jdk/make/com/sun/nio/Makefile
jdk/make/com/sun/nio/sctp/Exportedfiles.gmk
jdk/make/com/sun/nio/sctp/FILES_c.gmk
jdk/make/com/sun/nio/sctp/FILES_java.gmk
jdk/make/com/sun/nio/sctp/Makefile
jdk/make/com/sun/nio/sctp/mapfile-vers
jdk/make/docs/NON_CORE_PKGS.gmk
jdk/make/java/nio/mapfile-linux
jdk/make/java/nio/mapfile-solaris
jdk/src/share/classes/com/sun/nio/sctp/AbstractNotificationHandler.java
jdk/src/share/classes/com/sun/nio/sctp/Association.java
jdk/src/share/classes/com/sun/nio/sctp/AssociationChangeNotification.java
jdk/src/share/classes/com/sun/nio/sctp/HandlerResult.java
jdk/src/share/classes/com/sun/nio/sctp/IllegalReceiveException.java
jdk/src/share/classes/com/sun/nio/sctp/IllegalUnbindException.java
jdk/src/share/classes/com/sun/nio/sctp/InvalidStreamException.java
jdk/src/share/classes/com/sun/nio/sctp/MessageInfo.java
jdk/src/share/classes/com/sun/nio/sctp/Notification.java
jdk/src/share/classes/com/sun/nio/sctp/NotificationHandler.java
jdk/src/share/classes/com/sun/nio/sctp/PeerAddressChangeNotification.java
jdk/src/share/classes/com/sun/nio/sctp/SctpChannel.java
jdk/src/share/classes/com/sun/nio/sctp/SctpMultiChannel.java
jdk/src/share/classes/com/sun/nio/sctp/SctpServerChannel.java
jdk/src/share/classes/com/sun/nio/sctp/SctpSocketOption.java
jdk/src/share/classes/com/sun/nio/sctp/SctpStandardSocketOption.java
jdk/src/share/classes/com/sun/nio/sctp/SendFailedNotification.java
jdk/src/share/classes/com/sun/nio/sctp/ShutdownNotification.java
jdk/src/share/classes/com/sun/nio/sctp/package-info.java
jdk/src/share/classes/sun/nio/ch/SctpMessageInfoImpl.java
jdk/src/share/classes/sun/nio/ch/SctpStdSocketOption.java
jdk/src/solaris/classes/sun/nio/ch/SctpAssocChange.java
jdk/src/solaris/classes/sun/nio/ch/SctpAssociationImpl.java
jdk/src/solaris/classes/sun/nio/ch/SctpChannelImpl.java
jdk/src/solaris/classes/sun/nio/ch/SctpMultiChannelImpl.java
jdk/src/solaris/classes/sun/nio/ch/SctpNet.java
jdk/src/solaris/classes/sun/nio/ch/SctpNotification.java
jdk/src/solaris/classes/sun/nio/ch/SctpPeerAddrChange.java
jdk/src/solaris/classes/sun/nio/ch/SctpResultContainer.java
jdk/src/solaris/classes/sun/nio/ch/SctpSendFailed.java
jdk/src/solaris/classes/sun/nio/ch/SctpServerChannelImpl.java
jdk/src/solaris/classes/sun/nio/ch/SctpShutdown.java
jdk/src/solaris/classes/sun/nio/ch/SctpSocketDispatcher.java
jdk/src/solaris/native/sun/nio/ch/Sctp.h
jdk/src/solaris/native/sun/nio/ch/SctpChannelImpl.c
jdk/src/solaris/native/sun/nio/ch/SctpNet.c
jdk/src/solaris/native/sun/nio/ch/SctpServerChannelImpl.c
jdk/src/windows/classes/sun/nio/ch/SctpChannelImpl.java
jdk/src/windows/classes/sun/nio/ch/SctpMultiChannelImpl.java
jdk/src/windows/classes/sun/nio/ch/SctpServerChannelImpl.java
jdk/test/com/sun/nio/sctp/MessageInfoTests.java
jdk/test/com/sun/nio/sctp/SctpChannel/Bind.java
jdk/test/com/sun/nio/sctp/SctpChannel/Connect.java
jdk/test/com/sun/nio/sctp/SctpChannel/Receive.java
jdk/test/com/sun/nio/sctp/SctpChannel/Send.java
jdk/test/com/sun/nio/sctp/SctpChannel/Shutdown.java
jdk/test/com/sun/nio/sctp/SctpChannel/SocketOptionTests.java
jdk/test/com/sun/nio/sctp/SctpChannel/Util.java
jdk/test/com/sun/nio/sctp/SctpMultiChannel/Send.java
jdk/test/com/sun/nio/sctp/SctpMultiChannel/Util.java
jdk/test/com/sun/nio/sctp/SctpServerChannel/Accept.java
jdk/test/com/sun/nio/sctp/SctpServerChannel/NonBlockingAccept.java
jdk/test/com/sun/nio/sctp/SctpServerChannel/Util.java
--- a/jdk/make/com/sun/Makefile	Wed Jul 05 16:50:36 2017 +0200
+++ b/jdk/make/com/sun/Makefile	Thu Apr 16 17:42:00 2009 +0100
@@ -41,7 +41,7 @@
 # Omit mirror since it's built with the apt tool.
 SUBDIRS = $(SCRIPT_SUBDIR) image security crypto/provider jndi jmx \
     java inputmethods org xml rowset net/httpserver net/ssl demo \
-    tools jarsigner tracing servicetag
+    tools jarsigner tracing servicetag nio
 
 all build clean clobber::
 	$(SUBDIRS-loop)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/make/com/sun/nio/Makefile	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,38 @@
+#
+# Copyright 2009 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 com.sun.nio packages.
+#
+
+BUILDDIR = ../../..
+include $(BUILDDIR)/common/Defs.gmk
+
+SUBDIRS = sctp
+all build clean clobber::
+	$(SUBDIRS-loop)
+
+clean clobber::
+	$(RM) -r $(CLASSDESTDIR)/com/sun/nio
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/make/com/sun/nio/sctp/Exportedfiles.gmk	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,39 @@
+#
+# Copyright 2009 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.
+#
+
+#
+# These are the names of Java classes for which we will make .h files.
+#
+
+ifneq ($(PLATFORM), windows)
+FILES_export = \
+    sun/nio/ch/SctpAssocChange.java \
+    sun/nio/ch/SctpChannelImpl.java \
+    sun/nio/ch/SctpNet.java \
+    sun/nio/ch/SctpPeerAddrChange.java \
+    sun/nio/ch/SctpResultContainer.java \
+    sun/nio/ch/SctpServerChannelImpl.java \
+    sun/nio/ch/SctpStdSocketOption.java
+endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/make/com/sun/nio/sctp/FILES_c.gmk	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,31 @@
+#
+# Copyright 2009 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.
+#
+
+ifneq ($(PLATFORM),windows)
+FILES_c = \
+	SctpNet.c \
+        SctpChannelImpl.c \
+        SctpServerChannelImpl.c
+endif	
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/make/com/sun/nio/sctp/FILES_java.gmk	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,67 @@
+#
+# Copyright 2009 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.
+#
+FILES_java = \
+        com/sun/nio/sctp/AbstractNotificationHandler.java \
+	com/sun/nio/sctp/Association.java \
+	com/sun/nio/sctp/AssociationChangeNotification.java \
+	com/sun/nio/sctp/HandlerResult.java \
+	com/sun/nio/sctp/IllegalReceiveException.java \
+	com/sun/nio/sctp/IllegalUnbindException.java \
+	com/sun/nio/sctp/InvalidStreamException.java \
+	com/sun/nio/sctp/MessageInfo.java \
+	com/sun/nio/sctp/Notification.java \
+	com/sun/nio/sctp/NotificationHandler.java \
+	com/sun/nio/sctp/PeerAddressChangeNotification.java \
+	com/sun/nio/sctp/SctpChannel.java \
+	com/sun/nio/sctp/SctpMultiChannel.java \
+	com/sun/nio/sctp/SctpServerChannel.java \
+	com/sun/nio/sctp/SctpSocketOption.java \
+	com/sun/nio/sctp/SctpStandardSocketOption.java \
+	com/sun/nio/sctp/SendFailedNotification.java \
+	com/sun/nio/sctp/ShutdownNotification.java \
+	\
+	sun/nio/ch/SctpMessageInfoImpl.java \
+	sun/nio/ch/SctpStdSocketOption.java
+
+ifneq ($(PLATFORM), windows)
+FILES_java += \
+	sun/nio/ch/SctpAssocChange.java \
+	sun/nio/ch/SctpAssociationImpl.java \
+	sun/nio/ch/SctpChannelImpl.java \
+	sun/nio/ch/SctpMultiChannelImpl.java \
+	sun/nio/ch/SctpNet.java \
+	sun/nio/ch/SctpNotification.java \
+	sun/nio/ch/SctpPeerAddrChange.java \
+	sun/nio/ch/SctpResultContainer.java \
+	sun/nio/ch/SctpSendFailed.java \
+	sun/nio/ch/SctpServerChannelImpl.java \
+	sun/nio/ch/SctpShutdown.java \
+	sun/nio/ch/SctpSocketDispatcher.java
+else
+FILES_java += \
+	sun/nio/ch/SctpChannelImpl.java \
+	sun/nio/ch/SctpMultiChannelImpl.java \
+	sun/nio/ch/SctpServerChannelImpl.java
+endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/make/com/sun/nio/sctp/Makefile	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,80 @@
+#
+# Copyright 2009 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 com.sun.nio.sctp
+#
+
+BUILDDIR = ../../../..
+PACKAGE = com.sun.nio.sctp
+LIBRARY = sctp
+PRODUCT = sun
+#OTHER_JAVACFLAGS += -Xmaxwarns 1000 -Xlint
+include $(BUILDDIR)/common/Defs.gmk
+
+#
+# Files to compile
+#
+include FILES_c.gmk
+include FILES_java.gmk
+include Exportedfiles.gmk
+
+ifneq ($(PLATFORM), windows)
+include $(BUILDDIR)/common/Mapfile-vers.gmk
+include $(BUILDDIR)/common/Library.gmk
+
+#
+# Find platform-specific C source files
+#
+vpath %.c $(PLATFORM_SRC)/native/sun/nio/ch
+
+#
+# Include nio.h, net_util.h, sun_nio_ch_IOStatus.h, etc
+#
+OTHER_INCLUDES += \
+  -I$(SHARE_SRC)/native/sun/nio/ch \
+  -I$(SHARE_SRC)/native/java/net \
+  -I$(PLATFORM_SRC)/native/java/net \
+  -I$(CLASSHDRDIR)/../../../../java/java.nio/nio/CClassHeaders
+
+ifeq ($(PLATFORM), linux)
+COMPILER_WARNINGS_FATAL=true
+#OTHER_LDLIBS += -L$(LIBDIR)/$(LIBARCH) -ljava -lnet -lpthread -ldl
+OTHER_LDLIBS += -L$(LIBDIR)/$(LIBARCH) -lnio -lnet -lpthread -ldl
+endif
+ifeq ($(PLATFORM), solaris)
+#LIBSCTP = -lsctp
+OTHER_LDLIBS += $(LIBSOCKET) -L$(LIBDIR)/$(LIBARCH) -lnet -lnio
+endif # PLATFORM
+
+else # windows
+include $(BUILDDIR)/common/Classes.gmk
+endif # ifneq windows
+
+
+clean clobber::
+	$(RM) -r $(CLASSDESTDIR)/com/sun/nio/sctp
+	$(RM) -r $(CLASSDESTDIR)/sun/nio/ch
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/make/com/sun/nio/sctp/mapfile-vers	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,49 @@
+#
+# Copyright 2009 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:
+		Java_sun_nio_ch_SctpNet_socket0;
+		Java_sun_nio_ch_SctpNet_bindx;
+		Java_sun_nio_ch_SctpNet_getLocalAddresses0;
+		Java_sun_nio_ch_SctpNet_getRemoteAddresses0;
+		Java_sun_nio_ch_SctpNet_getPrimAddrOption0;
+		Java_sun_nio_ch_SctpNet_setPrimAddrOption0;
+		Java_sun_nio_ch_SctpNet_setPeerPrimAddrOption0;
+		Java_sun_nio_ch_SctpNet_getInitMsgOption0;
+		Java_sun_nio_ch_SctpNet_setInitMsgOption0;
+		Java_sun_nio_ch_SctpNet_getIntOption0;
+		Java_sun_nio_ch_SctpNet_setIntOption0;
+		Java_sun_nio_ch_SctpNet_shutdown0;
+		Java_sun_nio_ch_SctpChannelImpl_initIDs;
+		Java_sun_nio_ch_SctpChannelImpl_checkConnect;
+		Java_sun_nio_ch_SctpChannelImpl_receive0;
+		Java_sun_nio_ch_SctpChannelImpl_send0;
+		Java_sun_nio_ch_SctpServerChannelImpl_initIDs;
+		Java_sun_nio_ch_SctpServerChannelImpl_accept0;
+                JNI_OnLoad;
+	local:
+		*;
+};
--- a/jdk/make/docs/NON_CORE_PKGS.gmk	Wed Jul 05 16:50:36 2017 +0200
+++ b/jdk/make/docs/NON_CORE_PKGS.gmk	Thu Apr 16 17:42:00 2009 +0100
@@ -86,6 +86,8 @@
 
 SMARTCARDIO_PKGS = javax.smartcardio
 
+SCTPAPI_PKGS     = com.sun.nio.sctp
+
 TRACING_PKGS     = com.sun.tracing         \
                    com.sun.tracing.dtrace
 
@@ -98,4 +100,6 @@
                    $(OLD_JSSE_PKGS) \
                    $(HTTPSERVER_PKGS) \
                    $(SMARTCARDIO_PKGS) \
-                   $(TRACING_PKGS)
+                   $(TRACING_PKGS) \
+                   $(SCTPAPI_PKGS)
+
--- a/jdk/make/java/nio/mapfile-linux	Wed Jul 05 16:50:36 2017 +0200
+++ b/jdk/make/java/nio/mapfile-linux	Thu Apr 16 17:42:00 2009 +0100
@@ -189,6 +189,7 @@
 		Java_sun_nio_fs_UnixNativeDispatcher_getgrnam0;
 		Java_sun_nio_fs_UnixNativeDispatcher_getextmntent;
 		Java_sun_nio_fs_UnixCopyFile_transfer;
+		handleSocketError;
 
 	local:
 		*;
--- a/jdk/make/java/nio/mapfile-solaris	Wed Jul 05 16:50:36 2017 +0200
+++ b/jdk/make/java/nio/mapfile-solaris	Thu Apr 16 17:42:00 2009 +0100
@@ -175,6 +175,7 @@
 		Java_sun_nio_fs_SolarisWatchService_portDissociate;
 		Java_sun_nio_fs_SolarisWatchService_portSend;
 		Java_sun_nio_fs_SolarisWatchService_portGetn;
+		handleSocketError;
 
 	local:
 		*;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/AbstractNotificationHandler.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2009 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 com.sun.nio.sctp;
+
+/**
+ * A skeletal handler that consumes notifications and continues.
+ *
+ * <P> This class trivially implements the {@code handleNotification} methods to
+ * return {@link HandlerResult#CONTINUE CONTINUE} so that all notifications are
+ * consumed and the channel continues to try and receive a message.
+ *
+ * <P> It also provides overloaded versions of the {@code handleNotification}
+ * methods, one for each of the required supported notification types, {@link
+ * AssociationChangeNotification}, {@link PeerAddressChangeNotification},
+ * {@link SendFailedNotification}, and {@link ShutdownNotification}. The
+ * appropriate method will be invoked when the notification is received.
+ *
+ * @since 1.7
+ */
+public class AbstractNotificationHandler<T>
+    implements NotificationHandler<T>
+{
+    /**
+     * Initializes a new instance of this class.
+     */
+    protected AbstractNotificationHandler() {}
+
+    /**
+     * Invoked when an implementation specific notification is received from the
+     * SCTP stack.
+     *
+     * @param  notification
+     *         The notification
+     *
+     * @param  attachment
+     *         The object attached to the {@code receive} operation when it was
+     *         initiated.
+     *
+     * @return  The handler result
+     */
+    @Override
+    public HandlerResult handleNotification(Notification notification,
+                                            T attachment) {
+        return HandlerResult.CONTINUE;
+    }
+
+    /**
+     * Invoked when an {@link AssociationChangeNotification} is received from
+     * the SCTP stack.
+     *
+     * @param  notification
+     *         The notification
+     *
+     * @param  attachment
+     *         The object attached to the {@code receive} operation when it was
+     *         initiated.
+     *
+     * @return  The handler result
+     */
+    public HandlerResult handleNotification(AssociationChangeNotification notification,
+                                            T attachment) {
+        return HandlerResult.CONTINUE;
+    }
+
+    /**
+     * Invoked when an {@link PeerAddressChangeNotification} is received from
+     * the SCTP stack.
+     *
+     * @param  notification
+     *         The notification
+     *
+     * @param  attachment
+     *         The object attached to the {@code receive} operation when it was
+     *         initiated.
+     *
+     * @return  The handler result
+     */
+    public HandlerResult handleNotification(PeerAddressChangeNotification notification,
+                                            T attachment) {
+        return HandlerResult.CONTINUE;
+    }
+
+    /**
+     * Invoked when an {@link SendFailedNotification} is received from
+     * the SCTP stack.
+     *
+     * @param  notification
+     *         The notification
+     *
+     * @param  attachment
+     *         The object attached to the {@code receive} operation when it was
+     *         initiated.
+     *
+     * @return  The handler result
+     */
+    public HandlerResult handleNotification(SendFailedNotification notification,
+                                            T attachment) {
+        return HandlerResult.CONTINUE;
+    }
+
+    /**
+     * Invoked when an {@link ShutdownNotification} is received from
+     * the SCTP stack.
+     *
+     * @param  notification
+     *         The notification
+     *
+     * @param  attachment
+     *         The object attached to the {@code receive} operation when it was
+     *         initiated.
+     *
+     * @return  The handler result
+     */
+    public HandlerResult handleNotification(ShutdownNotification notification,
+                                            T attachment) {
+        return HandlerResult.CONTINUE;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/Association.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2009 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 com.sun.nio.sctp;
+
+/**
+ * A class that represents an SCTP association.
+ *
+ * <P> An association exists between two SCTP endpoints. Each endpoint is
+ * represented by a list of transport addresses through which that endpoint can
+ * be reached and from which it will originate SCTP messages. The association
+ * spans over all of the possible source/destination combinations which may be
+ * generated from each endpoint's lists of addresses.
+ *
+ * <P> Associations are identified by their Association ID.
+ * Association ID's are guaranteed to be unique for the lifetime of the
+ * association. An association ID may be reused after the association has been
+ * shutdown. An association ID is not unique across multiple SCTP channels.
+ * An Association's local and remote addresses may change if the SCTP
+ * implementation supports <I>Dynamic Address Reconfiguration</I> as defined by
+ * <A HREF="http://tools.ietf.org/html/rfc5061">RFC5061</A>, see the
+ * {@code bindAddress} and {@code unbindAddress} methods of {@link SctpChannel},
+ * {@link SctpServerChannel}, and {@link SctpMultiChannel}.
+ *
+ * <P> An {@code Association} is returned from an {@link
+ * SctpChannel#association SctpChannel} or an {@link
+ * SctpMultiChannel#associations SctpMultiChannel}, as well
+ * as being given as a parameter to {@link NotificationHandler
+ * NotificationHandler} methods.
+ *
+ * @since 1.7
+ */
+public class Association {
+    private final int associationID;
+    private final int maxInStreams;
+    private final int maxOutStreams;
+
+    /**
+     * Initializes a new instance of this class.
+     */
+    protected Association(int associationID,
+                          int maxInStreams,
+                          int maxOutStreams) {
+        this.associationID = associationID;
+        this.maxInStreams = maxInStreams;
+        this.maxOutStreams = maxOutStreams;
+    }
+
+    /**
+     * Returns the associationID.
+     *
+     * @return  The association ID
+     */
+    public final int associationID() {
+        return associationID;
+    };
+
+    /**
+     * Returns the maximum number of inbound streams that this association
+     * supports.
+     *
+     * <P> Data received on this association will be on stream number
+     * {@code s}, where {@code 0 <= s < maxInboundStreams()}.
+     *
+     * @return  The maximum number of inbound streams
+     */
+    public final int maxInboundStreams() {
+        return maxInStreams;
+    };
+
+    /**
+     * Returns the maximum number of outbound streams that this association
+     * supports.
+     *
+     * <P> Data sent on this association must be on stream number
+     * {@code s}, where {@code 0 <= s < maxOutboundStreams()}.
+     *
+     * @return  The maximum number of outbound streams
+     */
+    public final int maxOutboundStreams() {
+        return maxOutStreams;
+    };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/AssociationChangeNotification.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2009 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 com.sun.nio.sctp;
+
+/**
+ * Notification emitted when an association has either opened or closed.
+ *
+ * @since 1.7
+ */
+public abstract class AssociationChangeNotification
+    implements Notification
+{
+    /**
+     * Defines the type of change event that happened to the association.
+     *
+     * @since 1.7
+     */
+    public enum AssocChangeEvent
+    {
+        /**
+         * A new association is now ready and data may be exchanged with this peer.
+         */
+        COMM_UP,
+
+        /**
+         * The association has failed. A series of SCTP send failed notifications
+         * will follow this notification, one for each outstanding message.
+         */
+       COMM_LOST,
+
+        /**
+         * SCTP has detected that the peer has restarted.
+         */
+       RESTART,
+
+        /**
+         * The association has gracefully closed.
+         */
+       SHUTDOWN,
+
+        /**
+         * The association failed to setup. If a message was sent on a {@link
+         * SctpMultiChannel} in non-blocking mode, an
+         * SCTP send failed notification will follow this notification for the
+         * outstanding message.
+         */
+       CANT_START
+    }
+
+    /**
+     * Initializes a new instance of this class.
+     */
+    protected AssociationChangeNotification() {}
+
+    /**
+     * Returns the association that this notification is applicable to.
+     *
+     * @return  The association whose state has changed, or {@code null} if
+     *          there is no association, that is {@linkplain
+     *          AssocChangeEvent#CANT_START CANT_START}
+     */
+    public abstract Association association();
+
+    /**
+     * Returns the type of change event.
+     *
+     * @return  The event
+     */
+    public abstract AssocChangeEvent event();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/HandlerResult.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2009 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 com.sun.nio.sctp;
+
+/**
+ * Defines notification handler results.
+ *
+ * <P> The {@code HandlerResult} is used to determine the behavior of the
+ * channel after it handles a notification from the SCTP stack. Essentially its
+ * value determines if the channel should try to receive another notificaiton or
+ * a message before returning.
+ *
+ * @since 1.7
+ */
+public enum HandlerResult {
+    /**
+     * Try to receieve another message or notification.
+     */
+    CONTINUE,
+
+    /**
+     * Return without trying to receive any more data.
+     */
+    RETURN;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/IllegalReceiveException.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2009 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 com.sun.nio.sctp;
+
+/**
+ * Unchecked exception thrown when an attempt is made to invoke the
+ * {@code receive} method of {@link SctpChannel} or {@link SctpMultiChannel}
+ * from a notification handler.
+ *
+ * @since 1.7
+ */
+public class IllegalReceiveException extends IllegalStateException {
+    private static final long serialVersionUID = 2296619040988576224L;
+
+    /**
+     * Constructs an instance of this class.
+     */
+    public IllegalReceiveException() { }
+
+    /**
+     * Constructs an instance of this class with the specified message.
+     */
+    public IllegalReceiveException(String msg) {
+        super(msg);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/IllegalUnbindException.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2009 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 com.sun.nio.sctp;
+
+/**
+ * Unchecked exception thrown when an attempt is made to remove an
+ * address that is not bound to the channel, or remove an address from a
+ * channel that has only one address bound to it.
+ *
+ * @since 1.7
+ */
+public class IllegalUnbindException extends IllegalStateException {
+    private static final long serialVersionUID = -310540883995532224L;
+
+    /**
+     * Constructs an instance of this class.
+     */
+    public IllegalUnbindException() { }
+
+    /**
+     * Constructs an instance of this class with the specified detailed message.
+     */
+    public IllegalUnbindException(String msg) {
+        super(msg);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/InvalidStreamException.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2009 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 com.sun.nio.sctp;
+
+/**
+ * Unchecked exception thrown when an attempt is made to send a
+ * message to an invalid stream.
+ *
+ * @since 1.7
+ */
+public class InvalidStreamException extends IllegalArgumentException {
+    private static final long serialVersionUID = -9172703378046665558L;
+
+    /**
+     * Constructs an instance of this class.
+     */
+    public InvalidStreamException() { }
+
+    /**
+     * Constructs an instance of this class with the specified detailed message.
+     */
+    public InvalidStreamException(String msg) {
+        super(msg);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/MessageInfo.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,303 @@
+/*
+ * Copyright 2009 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 com.sun.nio.sctp;
+
+import java.net.SocketAddress;
+
+/**
+ * The {@code MessageInfo} class provides additional ancillary information about
+ * messages.
+ *
+ * <P> Received SCTP messages, returned by
+ * {@link SctpChannel#receive SctpChannel.receive} and {@link
+ * SctpMultiChannel#receive SctpMultiChannel.receive},
+ * return a {@code MessageInfo} instance that can be queried to determine
+ * ancillary information about the received message. Messages being sent should
+ * use one of the {@link #createOutgoing(java.net.SocketAddress,int)
+ * createOutgoing} methods to provide ancillary data for the message being
+ * sent, and may use the appropriate setter methods to override the default
+ * values provided for {@link #isUnordered() unordered}, {@link #timeToLive()
+ * timeToLive}, {@link #isComplete() complete} and {@link #payloadProtocolID()
+ * payloadProtocolID}, before sending the message.
+ *
+ * <P> For out going messages the {@code timeToLive} parameter is a time period
+ * that the sending side SCTP stack may expire the message if it has not been
+ * sent. This time period is an indication to the stack that the message is no
+ * longer required to be sent after the time period expires. It is not a hard
+ * timeout and may be influenced by whether the association supports the partial
+ * reliability extension, <a href=http://www.ietf.org/rfc/rfc3758.txt>RFC 3758
+ * <a>
+ *
+ * <P> {@code MessageInfo} instances are not safe for use by multiple concurrent
+ * threads. If a MessageInfo is to be used by more than one thread then access
+ * to the MessageInfo should be controlled by appropriate synchronization.
+ *
+ * @since 1.7
+ */
+public abstract class MessageInfo {
+    /**
+     * Initializes a new instance of this class.
+     */
+    protected MessageInfo() {}
+
+    /**
+     * Creates a {@code MessageInfo} instance suitable for use when
+     * sending a message.
+     *
+     * <P> The returned instance will have its {@link #isUnordered() unordered}
+     * value set to {@code false}, its {@link #timeToLive() timeToLive} value
+     * set to {@code 0}, its {@link #isComplete() complete} value set
+     * to {@code true}, and its {@link #payloadProtocolID() payloadProtocolID}
+     * value set to {@code 0}. These values, if required, can be set through
+     * the appropriate setter method before sending the message.
+     *
+     * @param  address
+     *         For a connected {@code SctpChannel} the address is the
+     *         preferred peer address of the association to send the message
+     *         to, or {@code null} to use the peer primary address. For an
+     *         {@code SctpMultiChannel} the address is used to determine
+     *         the association, or if no association exists with a peer of that
+     *         address then one is setup.
+     *
+     * @param  streamNumber
+     *         The stream number that the message will be sent on
+     *
+     * @return  The outgoing message info
+     *
+     * @throws  IllegalArgumentException
+     *          If the streamNumber is negative or greater than {@code 65536}
+     */
+    public static MessageInfo createOutgoing(SocketAddress address,
+                                             int streamNumber) {
+        if (streamNumber < 0 || streamNumber > 65536)
+            throw new IllegalArgumentException("Invalid stream number");
+
+        return new sun.nio.ch.SctpMessageInfoImpl(null, address, streamNumber);
+    }
+    /**
+     * Creates a {@code MessageInfo} instance suitable for use when
+     * sending a message to a given association. Typically used for
+     * {@code SctpMultiChannel} when an association has already been setup.
+     *
+     * <P> The returned instance will have its {@link #isUnordered() unordered}
+     * value set to {@code false}, its {@link #timeToLive() timeToLive} value
+     * set to {@code 0}, its {@link #isComplete() complete} value set
+     * to {@code true}, and its {@link #payloadProtocolID() payloadProtocolID}
+     * value set to {@code 0}. These values, if required, can be set through
+     * the appropriate setter method before sending the message.
+     *
+     * @param  association
+     *         The association to send the message on
+     *
+     * @param  address
+     *         The preferred peer address of the association to send the message
+     *         to, or {@code null} to use the peer primary address
+     *
+     * @param  streamNumber
+     *         The stream number that the message will be sent on.
+     *
+     * @return  The outgoing message info
+     *
+     * @throws  IllegalArgumentException
+     *          If {@code association} is {@code null}, or the streamNumber is
+     *          negative or greater than {@code 65536}
+     */
+    public static MessageInfo createOutgoing(Association association,
+                                             SocketAddress address,
+                                             int streamNumber) {
+        if (association == null)
+            throw new IllegalArgumentException("association cannot be null");
+
+        if (streamNumber < 0 || streamNumber > 65536)
+            throw new IllegalArgumentException("Invalid stream number");
+
+        return new sun.nio.ch.SctpMessageInfoImpl(association, address,
+                streamNumber);
+    }
+
+    /**
+     * Returns the source socket address if the message has been received,
+     * otherwise the preferred destination of the message to be sent.
+     *
+     * @return  The socket address, or {@code null} if this instance is to be
+     *          used for sending a message and has been construced without
+     *          specifying a preferred destination address
+     *
+     */
+    public abstract SocketAddress address();
+
+    /**
+     * Returns the association that the message was received on, if the message
+     * has been received, otherwise the association that the message is to be
+     * sent on.
+     *
+     * @return The association, or {@code null} if this instance is to be
+     *         used for sending a message and has been construced using the
+     *         the {@link #createOutgoing(SocketAddress,int)
+     *         createOutgoing(SocketAddress,int)} static factory method
+     */
+    public abstract Association association();
+
+    /**
+     * Returns the number of bytes read for the received message.
+     *
+     * <P> This method is only appicable for received messages, it has no
+     * meaning for messages being sent.
+     *
+     * @return  The number of bytes read, {@code -1} if the channel is an {@link
+     *          SctpChannel} that has reached end-of-stream, otherwise
+     *          {@code 0}
+     */
+    public abstract int bytes();
+
+    /**
+     * Tells whether or not the message is complete.
+     *
+     * <P> For received messages {@code true} indicates that the message was
+     * completely received. For messages being sent {@code true} indicates that
+     * the message is complete, {@code false} indicates that the message is not
+     * complete. How the send channel interprets this value depends on the value
+     * of its {@link SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE
+     * SCTP_EXPLICIT_COMPLETE} socket option.
+     *
+     * @return  {@code true} if, and only if, the message is complete
+     */
+    public abstract boolean isComplete();
+
+    /**
+     * Sets whether or not the message is complete.
+     *
+     * <P> For messages being sent {@code true} indicates that
+     * the message is complete, {@code false} indicates that the message is not
+     * complete. How the send channel interprets this value depends on the value
+     * of its {@link SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE
+     * SCTP_EXPLICIT_COMPLETE} socket option.
+     *
+     * @param  complete
+     *         {@code true} if, and only if, the message is complete
+     *
+     * @return  This MessageInfo
+     *
+     * @see  MessageInfo#isComplete()
+     */
+    public abstract MessageInfo complete(boolean complete);
+
+    /**
+     * Tells whether or not the message is unordered. For received messages
+     * {@code true} indicates that the message was sent non-ordered. For
+     * messages being sent {@code true} requests the un-ordered delivery of the
+     * message, {@code false} indicates that the message is ordered.
+     *
+     * @return  {@code true} if the message is unordered, otherwise
+     *          {@code false}
+     */
+    public abstract boolean isUnordered();
+
+    /**
+     * Sets whether or not the message is unordered.
+     *
+     * @param  unordered
+     *         {@code true} requests the un-ordered delivery of the message,
+     *         {@code false} indicates that the message is ordered.
+     *
+     * @return  This MessageInfo
+     *
+     * @see  MessageInfo#isUnordered()
+     */
+    public abstract MessageInfo unordered(boolean unordered);
+
+    /**
+     * Returns the payload protocol Identifier.
+     *
+     * <P> A value indicating the type of payload protocol data being
+     * transmitted/received. This value is passed as opaque data by SCTP.
+     * {@code 0} indicates an unspecified payload protocol identifier.
+     *
+     * @return  The Payload Protocol Identifier
+     */
+    public abstract int payloadProtocolID();
+
+    /**
+     * Sets the payload protocol Identifier.
+     *
+     * <P> A value indicating the type of payload protocol data being
+     * transmitted. This value is passed as opaque data by SCTP.
+     *
+     * @param  ppid
+     *         The Payload Protocol Identifier, or {@code 0} indicate an
+     *         unspecified payload protocol identifier.
+     *
+     * @return  This MessageInfo
+     *
+     * @see  MessageInfo#payloadProtocolID()
+     */
+    public abstract MessageInfo payloadProtocolID(int ppid);
+
+    /**
+     * Returns the stream number that the message was received on, if the
+     * message has been received, otherwise the stream number that the message
+     * is to be sent on.
+     *
+     * @return  The stream number
+     */
+    public abstract int streamNumber();
+
+    /**
+     * Sets the stream number that the message is to be sent on.
+     *
+     * @param  streamNumber
+     *         The stream number
+     *
+     * @throws  IllegalArgumentException
+     *          If the streamNumber is negative or greater than {@code 65536}
+     *
+     * @return  This MessageInfo
+     */
+    public abstract MessageInfo streamNumber(int streamNumber);
+
+    /**
+     * The time period that the sending side may expire the message if it has
+     * not been sent, or {@code 0} to indicate that no timeout should occur. This
+     * value is only applicable for messages being sent, it has no meaning for
+     * received messages.
+     *
+     * @return  The time period in milliseconds, or {@code 0}
+     */
+    public abstract long timeToLive();
+
+    /**
+     * Sets the time period that the sending side may expire the message if it
+     * has not been sent.
+     *
+     * @param  millis
+     *         The time period in milliseconds, or {@code 0} to indicate that no
+     *         timeout should occur
+     *
+     * @return  This MessageInfo
+     *
+     * @see MessageInfo#timeToLive()
+     */
+    public abstract MessageInfo timeToLive(long millis);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/Notification.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2009 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 com.sun.nio.sctp;
+
+/**
+ * A notification from the SCTP stack.
+ *
+ * <P> Objects of this type are passed to the {@link NotificationHandler} when
+ * a notification is received.
+ *
+ * <P> An SCTP channel supports the following notifications: {@link
+ * AssociationChangeNotification}, {@link PeerAddressChangeNotification},
+ * {@link SendFailedNotification}, {@link ShutdownNotification}, and may support
+ * additional implementation specific notifications.
+ *
+ * @since 1.7
+ */
+public interface Notification {
+    /**
+     * Returns the association that this notification is applicable to.
+     */
+    public Association association();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/NotificationHandler.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2009 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 com.sun.nio.sctp;
+
+/**
+ * A handler for consuming notifications from the SCTP stack.
+ *
+ * <P> The SCTP channels defined in this package allow a notification handler to
+ * be specified to consume notifications from the SCTP stack. When a
+ * notification is received the {@linkplain #handleNotification
+ * handleNotification} method of the handler is invoked to handle that
+ * notification.
+ *
+ * <P> Additionally, an attachment object can be attached to the {@code receive}
+ * operation to provide context when consuming the notification. The
+ * attachment is important for cases where a <i>state-less</i> {@code
+ * NotificationHandler} is used to consume the result of many {@code receive}
+ * operations.
+ *
+ * <P> Handler implementations are encouraged to extend the {@link
+ * AbstractNotificationHandler} class which implements this interface and
+ * provide notification specific methods. However, an API should generally use
+ * this handler interface as the type for parameters, return type, etc. rather
+ * than the abstract class.
+ *
+ * @param  T  The type of the object attached to the receive operation
+ *
+ * @since 1.7
+ */
+public interface NotificationHandler<T> {
+    /**
+     * Invoked when a notification is received from the SCTP stack.
+     *
+     * @param  notification
+     *         The notification
+     *
+     * @param  attachment
+     *         The object attached to the receive operation when it was initiated.
+     *
+     * @return  The handler result
+     */
+    HandlerResult handleNotification(Notification notification, T attachment);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/PeerAddressChangeNotification.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2009 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 com.sun.nio.sctp;
+
+import java.net.SocketAddress;
+
+/**
+ * Notification emitted when a destination address on a multi-homed peer
+ * encounters a change.
+ *
+ * @since 1.7
+ */
+public abstract class PeerAddressChangeNotification
+    implements Notification
+{
+    /**
+     * Defines the type of address change event that occurred to the destination
+     * address on a multi-homed peer when it encounters a change of interface
+     * details.
+     *
+     * <P> Some of these events types are only generated when the association
+     * supports dynamic address reconfiguration, e.g. {@code SCTP_ADDR_ADDED},
+     * {@code SCTP_ADDR_REMOVED}, etc.
+     *
+     * @since 1.7
+     */
+    public enum AddressChangeEvent {
+        /**
+         * This address is now reachable.
+         */
+       ADDR_AVAILABLE,
+
+       /**
+        * The address specified can no longer be reached. Any data sent to this
+        * address is rerouted to an alternate until this address becomes reachable.
+        */
+       ADDR_UNREACHABLE,
+
+       /**
+        * The address is no longer part of the association.
+        */
+       ADDR_REMOVED,
+
+       /**
+        * The address is now part of the association.
+        */
+       ADDR_ADDED,
+
+       /**
+        * This address has now been made to be the primary destination address.
+        */
+       ADDR_MADE_PRIMARY,
+
+       /**
+        * This address has now been confirmed as a valid address.
+        */
+       ADDR_CONFIRMED;
+    }
+
+    /**
+     * Initializes a new instance of this class.
+     */
+    protected PeerAddressChangeNotification() {}
+
+    /**
+     * Returns the peer address.
+     *
+     * @return  The peer address
+     */
+    public abstract SocketAddress address();
+
+    /**
+     * Returns the association that this notification is applicable to.
+     *
+     * @return  The association whose peer address changed
+     */
+    public abstract Association association();
+
+    /**
+     * Returns the type of change event.
+     *
+     * @return  The event
+     */
+    public abstract AddressChangeEvent event();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/SctpChannel.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,859 @@
+/*
+ * Copyright 2009 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 com.sun.nio.sctp;
+
+import java.net.SocketAddress;
+import java.net.InetAddress;
+import java.io.IOException;
+import java.util.Set;
+import java.nio.ByteBuffer;
+import java.nio.channels.spi.AbstractSelectableChannel;
+import java.nio.channels.spi.SelectorProvider;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.SelectionKey;
+
+/**
+ * A selectable channel for message-oriented connected SCTP sockets.
+ *
+ * <P> An SCTP channel can only control one SCTP association.
+ * An {@code SCTPChannel} is created by invoking one of the
+ * {@link #open open} methods of this class. A newly-created channel is open but
+ * not yet connected, that is, there is no association setup with a remote peer.
+ * An attempt to invoke an I/O operation upon an unconnected
+ * channel will cause a {@link java.nio.channels.NotYetConnectedException} to be
+ * thrown. An association can be setup by connecting the channel using one of
+ * its {@link #connect connect} methods. Once connected, the channel remains
+ * connected until it is closed. Whether or not a channel is connected may be
+ * determined by invoking {@link #getRemoteAddresses getRemoteAddresses}.
+ *
+ * <p> SCTP channels support <i>non-blocking connection:</i>&nbsp;A
+ * channel may be created and the process of establishing the link to
+ * the remote socket may be initiated via the {@link #connect connect} method
+ * for later completion by the {@link #finishConnect finishConnect} method.
+ * Whether or not a connection operation is in progress may be determined by
+ * invoking the {@link #isConnectionPending isConnectionPending} method.
+ *
+ * <p> Socket options are configured using the
+ * {@link #setOption(SctpSocketOption,Object) setOption} method. An SCTP
+ * channel support the following options:
+ * <blockquote>
+ * <table border>
+ *   <tr>
+ *     <th>Option Name</th>
+ *     <th>Description</th>
+ *   </tr>
+ *   <tr>
+ *     <td> {@link SctpStandardSocketOption#SCTP_DISABLE_FRAGMENTS
+ *                                          SCTP_DISABLE_FRAGMENTS} </td>
+ *     <td> Enables or disables message fragmentation </td>
+ *   </tr>
+ *   <tr>
+ *     <td> {@link SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE
+ *                                          SCTP_EXPLICIT_COMPLETE} </td>
+ *     <td> Enables or disables explicit message completion </td>
+ *   </tr>
+ *    <tr>
+ *     <td> {@link SctpStandardSocketOption#SCTP_FRAGMENT_INTERLEAVE
+ *                                          SCTP_FRAGMENT_INTERLEAVE} </td>
+ *     <td> Controls how the presentation of messages occur for the message
+ *          receiver </td>
+ *   </tr>
+ *   <tr>
+ *     <td> {@link SctpStandardSocketOption#SCTP_INIT_MAXSTREAMS
+ *                                          SCTP_INIT_MAXSTREAMS} </td>
+ *     <td> The maximum number of streams requested by the local endpoint during
+ *          association initialization </td>
+ *   </tr>
+ *   <tr>
+ *     <td> {@link SctpStandardSocketOption#SCTP_NODELAY SCTP_NODELAY} </td>
+ *     <td> Enables or disable a Nagle-like algorithm </td>
+ *   </tr>
+ *   <tr>
+ *     <td> {@link SctpStandardSocketOption#SCTP_PRIMARY_ADDR
+ *                                          SCTP_PRIMARY_ADDR} </td>
+ *     <td> Requests that the local SCTP stack use the given peer address as the
+ *          association primary </td>
+ *   </tr>
+ *   <tr>
+ *     <td> {@link SctpStandardSocketOption#SCTP_SET_PEER_PRIMARY_ADDR
+ *                                          SCTP_SET_PEER_PRIMARY_ADDR} </td>
+ *     <td> Requests that the peer mark the enclosed address as the association
+ *          primary </td>
+ *   </tr>
+ *   <tr>
+ *     <td> {@link SctpStandardSocketOption#SO_SNDBUF
+ *                                          SO_SNDBUF} </td>
+ *     <td> The size of the socket send buffer </td>
+ *   </tr>
+ *   <tr>
+ *     <td> {@link SctpStandardSocketOption#SO_RCVBUF
+ *                                          SO_RCVBUF} </td>
+ *     <td> The size of the socket receive buffer </td>
+ *   </tr>
+ *   <tr>
+ *     <td> {@link SctpStandardSocketOption#SO_LINGER
+ *                                          SO_LINGER} </td>
+ *     <td> Linger on close if data is present (when configured in blocking mode
+ *          only) </td>
+ *   </tr>
+ * </table>
+ * </blockquote>
+ * Additional (implementation specific) options may also be supported. The list
+ * of options supported is obtained by invoking the {@link #supportedOptions()
+ * supportedOptions}  method.
+ *
+ * <p> SCTP 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 #connect connect} and {@link #finishConnect
+ * finishConnect} methods are mutually synchronized against each other, and
+ * an attempt to initiate a send or receive operation while an invocation of one
+ * of these methods is in progress will block until that invocation is complete.
+ *
+ * @since 1.7
+ */
+public abstract class SctpChannel
+    extends AbstractSelectableChannel
+{
+    /**
+     * Initializes a new instance of this class.
+     *
+     * @param  provider
+     *         The selector provider for this channel
+     */
+    protected SctpChannel(SelectorProvider provider) {
+        super(provider);
+    }
+
+    /**
+     * Opens an SCTP channel.
+     *
+     * <P> The new channel is unbound and unconnected.
+     *
+     * @return  A new SCTP channel
+     *
+     * @throws  UnsupportedOperationException
+     *          If the SCTP protocol is not supported
+     *
+     * @throws  IOException
+     *          If an I/O error occurs
+     */
+    public static SctpChannel open() throws
+        IOException {
+        return new sun.nio.ch.SctpChannelImpl((SelectorProvider)null);
+    }
+
+    /**
+     * Opens an SCTP channel and connects it to a remote address.
+     *
+     * <P> This is a convenience method and is equivalent to evaluating the
+     * following expression:
+     * <blockquote><pre>
+     * open().connect(remote, maxOutStreams, maxInStreams);
+     * </pre></blockquote>
+     *
+     * @param  remote
+     *         The remote address to which the new channel is to be connected
+     *
+     * @param  maxOutStreams
+     *         The number of streams that the application wishes to be able
+     *         to send to. Must be non negative and no larger than {@code 65536}.
+     *         {@code 0} to use the endpoints default value.
+     *
+     * @param  maxInStreams
+     *         The maximum number of inbound streams the application is prepared
+     *         to support. Must be non negative and no larger than {@code 65536}.
+     *         {@code 0} to use the endpoints default value.
+     *
+     * @return  A new SCTP channel connected to the given address
+     *
+     * @throws  java.nio.channels.AsynchronousCloseException
+     *          If another thread closes this channel
+     *          while the connect operation is in progress
+     *
+     * @throws  java.nio.channels.ClosedByInterruptException
+     *          If another thread interrupts the current thread
+     *          while the connect operation is in progress, thereby
+     *          closing the channel and setting the current thread's
+     *          interrupt status
+     *
+     * @throws  java.nio.channels.UnresolvedAddressException
+     *          If the given remote address is not fully resolved
+     *
+     * @throws  java.nio.channels.UnsupportedAddressTypeException
+     *          If the type of the given remote address is not supported
+     *
+     * @throws  SecurityException
+     *          If a security manager has been installed
+     *          and it does not permit access to the given remote peer
+     *
+     * @throws  UnsupportedOperationException
+     *          If the SCTP protocol is not supported
+     *
+     * @throws  IOException
+     *          If some other I/O error occurs
+     */
+    public static SctpChannel open(SocketAddress remote, int maxOutStreams,
+                   int maxInStreams) throws IOException {
+        SctpChannel ssc = SctpChannel.open();
+        ssc.connect(remote, maxOutStreams, maxInStreams);
+        return ssc;
+    }
+
+    /**
+     * Returns the association on this channel's socket.
+     *
+     * @return  the association, or {@code null} if the channel's socket is not
+     *          connected.
+     *
+     * @throws  ClosedChannelException
+     *          If the channel is closed
+     *
+     * @throws  IOException
+     *          If some other I/O error occurs
+     */
+    public abstract Association association() throws IOException;
+
+    /**
+     * Binds the channel's socket to a local address.
+     *
+     * <P> This method is used to establish a relationship between the socket
+     * and the local addresses. Once a relationship is established then
+     * the socket remains bound until the channel is closed. This relationship
+     * may not necesssarily be with the address {@code local} as it may be removed
+     * by {@link #unbindAddress unbindAddress}, but there will always be at least
+     * one local address bound to the channel's socket once an invocation of
+     * this method successfully completes.
+     *
+     * <P> Once the channel's socket has been successfully bound to a specific
+     * address, that is not automatically assigned, more addresses
+     * may be bound to it using {@link #bindAddress bindAddress}, or removed
+     * using {@link #unbindAddress unbindAddress}.
+     *
+     * @param  local
+     *         The local address to bind the socket, or {@code null} to
+     *         bind the socket to an automatically assigned socket address
+     *
+     * @return  This channel
+     *
+     * @throws  java.nio.channels.AlreadyConnectedException
+     *          If this channel is already connected
+     *
+     * @throws  java.nio.channels.ClosedChannelException
+     *          If this channel is closed
+     *
+     * @throws  java.nio.channels.ConnectionPendingException
+     *          If a non-blocking connection operation is already in progress on this channel
+     *
+     * @throws  java.nio.channels.AlreadyBoundException
+     *          If this channel is already bound
+     *
+     * @throws  java.nio.channels.UnsupportedAddressTypeException
+     *          If the type of the given address is not supported
+     *
+     * @throws  IOException
+     *          If some other I/O error occurs
+     */
+    public abstract SctpChannel bind(SocketAddress local)
+        throws IOException;
+
+    /**
+     * Adds the given address to the bound addresses for the channel's
+     * socket.
+     *
+     * <P> The given address must not be the {@link
+     * java.net.InetAddress#isAnyLocalAddress wildcard} address.
+     * The channel must be first bound using {@link #bind bind} before
+     * invoking this method, otherwise {@link
+     * java.nio.channels.NotYetBoundException} is thrown. The {@link #bind bind}
+     * method takes a {@code SocketAddress} as its argument which typically
+     * contains a port number as well as an address. Addresses subquently bound
+     * using this method are simply addresses as the SCTP port number remains
+     * the same for the lifetime of the channel.
+     *
+     * <P> Adding addresses to a connected association is optional functionality.
+     * If the endpoint supports dynamic address reconfiguration then it may
+     * send the appropriate message to the peer to change the peers address
+     * lists.
+     *
+     * @param  address
+     *         The address to add to the bound addresses for the socket
+     *
+     * @return  This channel
+     *
+     * @throws  java.nio.channels.ClosedChannelException
+     *          If this channel is closed
+     *
+     * @throws  java.nio.channels.ConnectionPendingException
+     *          If a non-blocking connection operation is already in progress on
+     *          this channel
+     *
+     * @throws  java.nio.channels.NotYetBoundException
+     *          If this channel is not yet bound
+     *
+     * @throws  java.nio.channels.AlreadyBoundException
+     *          If this channel is already bound to the given address
+     *
+     * @throws  IllegalArgumentException
+     *          If address is {@code null} or the {@link
+     *          java.net.InetAddress#isAnyLocalAddress wildcard} address
+     *
+     * @throws  IOException
+     *          If some other I/O error occurs
+     */
+    public abstract SctpChannel bindAddress(InetAddress address)
+         throws IOException;
+
+    /**
+     * Removes the given address from the bound addresses for the channel's
+     * socket.
+     *
+     * <P> The given address must not be the {@link
+     * java.net.InetAddress#isAnyLocalAddress wildcard} address.
+     * The channel must be first bound using {@link #bind bind} before
+     * invoking this method, otherwise {@link java.nio.channels.NotYetBoundException}
+     * is thrown. If this method is invoked on a channel that does not have
+     * {@code address} as one of its bound addresses or that has only one
+     * local address bound to it, then this method throws
+     * {@link IllegalUnbindException}.
+     * The initial address that the channel's socket is bound to using {@link
+     * #bind bind} may be removed from the bound addresses for the channel's socket.
+     *
+     * <P> Removing addresses from a connected association is optional
+     * functionality. If the endpoint supports dynamic address reconfiguration
+     * then it may send the appropriate message to the peer to change the peers
+     * address lists.
+     *
+     * @param  address
+     *         The address to remove from the bound addresses for the socket
+     *
+     * @return  This channel
+     *
+     * @throws  java.nio.channels.ClosedChannelException
+     *          If this channel is closed
+     *
+     * @throws  java.nio.channels.ConnectionPendingException
+     *          If a non-blocking connection operation is already in progress on
+     *          this channel
+     *
+     * @throws  java.nio.channels.NotYetBoundException
+     *          If this channel is not yet bound
+     *
+     * @throws  IllegalArgumentException
+     *          If address is {@code null} or the {@link
+     *          java.net.InetAddress#isAnyLocalAddress wildcard} address
+     *
+     * @throws  IllegalUnbindException
+     *          If {@code address} is not bound to the channel's socket. or
+     *          the channel has only one address bound to it
+     *
+     * @throws  IOException
+     *          If some other I/O error occurs
+     */
+    public abstract SctpChannel unbindAddress(InetAddress address)
+         throws IOException;
+
+    /**
+     * Connects this channel's socket.
+     *
+     * <P> If this channel is in non-blocking mode then an invocation of this
+     * method initiates a non-blocking connection operation.  If the connection
+     * is established immediately, as can happen with a local connection, then
+     * this method returns {@code true}.  Otherwise this method returns
+     * {@code false} and the connection operation must later be completed by
+     * invoking the {@link #finishConnect finishConnect} method.
+     *
+     * <P> If this channel is in blocking mode then an invocation of this
+     * method will block until the connection is established or an I/O error
+     * occurs.
+     *
+     * <P> If a security manager has been installed then this method verifies
+     * that its {@link java.lang.SecurityManager#checkConnect checkConnect}
+     * method permits connecting to the address and port number of the given
+     * remote peer.
+     *
+     * <p> This method may be invoked at any time. If a {@link #send send} or
+     * {@link #receive receive} operation upon this channel is invoked while an
+     * invocation of this method is in progress then that operation will first
+     * block until this invocation is complete.  If a connection attempt is
+     * initiated but fails, that is, if an invocation of this method throws a
+     * checked exception, then the channel will be closed.
+     *
+     * @param  remote
+     *         The remote peer to which this channel is to be connected
+     *
+     * @return  {@code true} if a connection was established, {@code false} if
+     *          this channel is in non-blocking mode and the connection
+     *          operation is in progress
+     *
+     * @throws  java.nio.channels.AlreadyConnectedException
+     *          If this channel is already connected
+     *
+     * @throws  java.nio.channels.ConnectionPendingException
+     *          If a non-blocking connection operation is already in progress on
+     *          this channel
+     *
+     * @throws  java.nio.channels.ClosedChannelException
+     *          If this channel is closed
+     *
+     * @throws  java.nio.channels.AsynchronousCloseException
+     *          If another thread closes this channel
+     *          while the connect operation is in progress
+     *
+     * @throws  java.nio.channels.ClosedByInterruptException
+     *          If another thread interrupts the current thread
+     *          while the connect operation is in progress, thereby
+     *          closing the channel and setting the current thread's
+     *          interrupt status
+     *
+     * @throws  java.nio.channels.UnresolvedAddressException
+     *          If the given remote address is not fully resolved
+     *
+     * @throws  java.nio.channels.UnsupportedAddressTypeException
+     *          If the type of the given remote address is not supported
+     *
+     * @throws  SecurityException
+     *          If a security manager has been installed
+     *          and it does not permit access to the given remote peer
+     *
+     * @throws  IOException
+     *          If some other I/O error occurs
+     */
+    public abstract boolean connect(SocketAddress remote) throws IOException;
+
+    /**
+     * Connects this channel's socket.
+     *
+     * <P> This is a convience method and is equivalent to evaluating the
+     * following expression:
+     * <blockquote><pre>
+     * setOption(SctpStandardSocketOption.SCTP_INIT_MAXSTREAMS, SctpStandardSocketOption.InitMaxStreams.create(maxInStreams, maxOutStreams))
+     *  .connect(remote);
+     * </pre></blockquote>
+     *
+     * <P> The {@code maxOutStreams} and {@code maxInStreams} parameters
+     * represent the maximum number of streams that the application wishes to be
+     * able to send to and receive from. They are negotiated with the remote
+     * peer and may be limited by the operating system.
+     *
+     * @param  remote
+     *         The remote peer to which this channel is to be connected
+     *
+     * @param  maxOutStreams
+     *         Must be non negative and no larger than {@code 65536}.
+     *         {@code 0} to use the endpoints default value.
+     *
+     * @param  maxInStreams
+     *         Must be non negative and no larger than {@code 65536}.
+     *         {@code 0} to use the endpoints default value.
+     *
+     * @return  {@code true} if a connection was established, {@code false} if
+     *          this channel is in non-blocking mode and the connection operation
+     *          is in progress
+     *
+     * @throws  java.nio.channels.AlreadyConnectedException
+     *          If this channel is already connected
+     *
+     * @throws  java.nio.channels.ConnectionPendingException
+     *          If a non-blocking connection operation is already in progress on
+     *          this channel
+     *
+     * @throws  java.nio.channels.ClosedChannelException
+     *          If this channel is closed
+     *
+     * @throws  java.nio.channels.AsynchronousCloseException
+     *          If another thread closes this channel
+     *          while the connect operation is in progress
+     *
+     * @throws  java.nio.channels.ClosedByInterruptException
+     *          If another thread interrupts the current thread
+     *          while the connect operation is in progress, thereby
+     *          closing the channel and setting the current thread's
+     *          interrupt status
+     *
+     * @throws  java.nio.channels.UnresolvedAddressException
+     *          If the given remote address is not fully resolved
+     *
+     * @throws  java.nio.channels.UnsupportedAddressTypeException
+     *          If the type of the given remote address is not supported
+     *
+     * @throws  SecurityException
+     *          If a security manager has been installed
+     *          and it does not permit access to the given remote peer
+     *
+     * @throws  IOException
+     *          If some other I/O error occurs
+     */
+    public abstract boolean connect(SocketAddress remote,
+                                    int maxOutStreams,
+                                    int maxInStreams)
+        throws IOException;
+
+    /**
+     * Tells whether or not a connection operation is in progress on this channel.
+     *
+     * @return  {@code true} if, and only if, a connection operation has been initiated
+     *          on this channel but not yet completed by invoking the
+     *          {@link #finishConnect} method
+     */
+    public abstract boolean isConnectionPending();
+
+    /**
+     * Finishes the process of connecting an SCTP channel.
+     *
+     * <P> A non-blocking connection operation is initiated by placing a socket
+     * channel in non-blocking mode and then invoking one of its {@link #connect
+     * connect} methods.  Once the connection is established, or the attempt has
+     * failed, the channel will become connectable and this method may
+     * be invoked to complete the connection sequence.  If the connection
+     * operation failed then invoking this method will cause an appropriate
+     * {@link java.io.IOException} to be thrown.
+     *
+     * <P> If this channel is already connected then this method will not block
+     * and will immediately return <tt>true</tt>.  If this channel is in
+     * non-blocking mode then this method will return <tt>false</tt> if the
+     * connection process is not yet complete.  If this channel is in blocking
+     * mode then this method will block until the connection either completes
+     * or fails, and will always either return <tt>true</tt> or throw a checked
+     * exception describing the failure.
+     *
+     * <P> This method may be invoked at any time. If a {@link #send send} or {@link #receive receive}
+     * operation upon this channel is invoked while an invocation of this
+     * method is in progress then that operation will first block until this
+     * invocation is complete.  If a connection attempt fails, that is, if an
+     * invocation of this method throws a checked exception, then the channel
+     * will be closed.
+     *
+     * @return  {@code true} if, and only if, this channel's socket is now
+     *          connected
+     *
+     * @throws  java.nio.channels.NoConnectionPendingException
+     *          If this channel is not connected and a connection operation
+     *          has not been initiated
+     *
+     * @throws  java.nio.channels.ClosedChannelException
+     *          If this channel is closed
+     *
+     * @throws  java.nio.channels.AsynchronousCloseException
+     *          If another thread closes this channel
+     *          while the connect operation is in progress
+     *
+     * @throws  java.nio.channels.ClosedByInterruptException
+     *          If another thread interrupts the current thread
+     *          while the connect operation is in progress, thereby
+     *          closing the channel and setting the current thread's
+     *          interrupt status
+     *
+     * @throws  IOException
+     *          If some other I/O error occurs
+     */
+    public abstract boolean finishConnect() throws IOException;
+
+    /**
+     * Returns all of the socket addresses to which this channel's socket is
+     * bound.
+     *
+     * @return  All the socket addresses that this channel's socket is
+     *          bound to, or an empty {@code Set} if the channel's socket is not
+     *          bound
+     *
+     * @throws  ClosedChannelException
+     *          If the channel is closed
+     *
+     * @throws  IOException
+     *          If an I/O error occurs
+     */
+    public abstract Set<SocketAddress> getAllLocalAddresses()
+        throws IOException;
+
+    /**
+     * Returns all of the remote addresses to which this channel's socket
+     * is connected.
+     *
+     * <P> If the channel is connected to a remote peer that is bound to
+     * multiple addresses then it is these addresses that the channel's socket
+     * is connected.
+     *
+     * @return  All of the remote addresses to which this channel's socket
+     *          is connected, or an empty {@code Set} if the channel's socket is
+     *          not connected
+     *
+     * @throws  ClosedChannelException
+     *          If the channel is closed
+     *
+     * @throws  IOException
+     *          If an I/O error occurs
+     */
+    public abstract Set<SocketAddress> getRemoteAddresses()
+        throws IOException;
+
+    /**
+     * Shutdown a connection without closing the channel.
+     *
+     * <P> Sends a shutdown command to the remote peer, effectively preventing
+     * any new data from being written to the socket by either peer. Further
+     * sends will throw {@link java.nio.channels.ClosedChannelException}. The
+     * channel remains open to allow the for any data (and notifications) to be
+     * received that may have been sent by the peer before it received the
+     * shutdown command. If the channel is already shutdown then invoking this
+     * method has no effect.
+     *
+     * @return  This channel
+     *
+     * @throws  java.nio.channels.NotYetConnectedException
+     *          If this channel is not yet connected
+     *
+     * @throws  java.nio.channels.ClosedChannelException
+     *          If this channel is closed
+     *
+     * @throws  IOException
+     *          If some other I/O error occurs
+     */
+    public abstract SctpChannel shutdown() 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  UnsupportedOperationException
+     *          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 SctpStandardSocketOption
+     */
+    public abstract <T> T getOption(SctpSocketOption<T> name)
+        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  UnsupportedOperationException
+     *          If the socket option is not supported by this channel
+     *
+     * @throws  IllegalArgumentException
+     *          If 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 SctpStandardSocketOption
+     */
+    public abstract <T> SctpChannel setOption(SctpSocketOption<T> name, T value)
+        throws IOException;
+
+    /**
+     * Returns a set of the socket options supported by this channel.
+     *
+     * <P> 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
+     */
+    public abstract Set<SctpSocketOption<?>> supportedOptions();
+
+    /**
+     * Returns an operation set identifying this channel's supported operations.
+     *
+     * <P> SCTP channels support connecting, reading, and writing, so this
+     * method returns <tt>(</tt>{@link SelectionKey#OP_CONNECT}
+     * <tt>|</tt>&nbsp;{@link SelectionKey#OP_READ} <tt>|</tt>&nbsp;{@link
+     * SelectionKey#OP_WRITE}<tt>)</tt>.  </p>
+     *
+     * @return  The valid-operation set
+     */
+    @Override
+    public final int validOps() {
+        return (SelectionKey.OP_READ |
+                SelectionKey.OP_WRITE |
+                SelectionKey.OP_CONNECT);
+    }
+
+    /**
+     * Receives a message into the given buffer and/or handles a notification.
+     *
+     * <P> If a message or notification is immediately available, or if this
+     * channel is in blocking mode and one eventually becomes available, then
+     * the message or notification is returned or handled, respectively. If this
+     * channel is in non-blocking mode and a message or notification is not
+     * immediately available then this method immediately returns {@code null}.
+     *
+     * <P> If this method receives a message it is copied into the given byte
+     * buffer. The message is transferred into the given byte buffer starting at
+     * its current position and the buffers position is incremented by the
+     * number of bytes read. If there are fewer bytes remaining in the buffer
+     * than are required to hold the message, or the underlying input buffer
+     * does not contain the complete message, then an invocation of {@link
+     * MessageInfo#isComplete isComplete} on the returned {@code
+     * MessageInfo} will return {@code false}, and more invocations of this
+     * method will be necessary to completely consume the messgae. Only
+     * one message at a time will be partially delivered in any stream. The
+     * socket option {@link SctpStandardSocketOption#SCTP_FRAGMENT_INTERLEAVE
+     * SCTP_FRAGMENT_INTERLEAVE} controls various aspects of what interlacing of
+     * messages occurs.
+     *
+     * <P> If this method receives a notification then the appropriate method of
+     * the given handler, if there is one, is invoked. If the handler returns
+     * {@link HandlerResult#CONTINUE CONTINUE} then this method will try to
+     * receive another message/notification, otherwise, if {@link
+     * HandlerResult#RETURN RETURN} is returned this method will return {@code
+     * null}. If an uncaught exception is thrown by the handler it will be
+     * propagated up the stack through this method.
+     *
+     * <P> This method may be invoked at any time. If another thread has
+     * already initiated a receive operation upon this channel, then an
+     * invocation of this method will block until the first operation is
+     * complete. The given handler is invoked without holding any locks used
+     * to enforce the above synchronization policy, that way handlers
+     * will not stall other threads from receiving. A handler should not invoke
+     * the {@code receive} method of this channel, if it does an
+     * {@link IllegalReceiveException} will be thrown.
+     *
+     * @param  dst
+     *         The buffer into which message bytes are to be transferred
+     *
+     * @param  attachment
+     *         The object to attach to the receive operation; can be
+     *         {@code null}
+     *
+     * @param  handler
+     *         A handler to handle notifications from the SCTP stack, or {@code
+     *         null} to ignore any notifications.
+     *
+     * @return  The {@code MessageInfo}, {@code null} if this channel is in
+     *          non-blocking mode and no messages are immediately available or
+     *          the notification handler returns {@link HandlerResult#RETURN
+     *          RETURN} after handling a notification
+     *
+     * @throws  java.nio.channels.ClosedChannelException
+     *          If this channel is closed
+     *
+     * @throws  java.nio.channels.AsynchronousCloseException
+     *          If another thread closes this channel
+     *          while the read operation is in progress
+     *
+     * @throws  java.nio.channels.ClosedByInterruptException
+     *          If another thread interrupts the current thread
+     *          while the read operation is in progress, thereby
+     *          closing the channel and setting the current thread's
+     *          interrupt status
+     *
+     * @throws  java.nio.channels.NotYetConnectedException
+     *          If this channel is not yet connected
+     *
+     * @throws  IllegalReceiveException
+     *          If the given handler invokes the {@code receive} method of this
+     *          channel
+     *
+     * @throws  IOException
+     *          If some other I/O error occurs
+     */
+    public abstract <T> MessageInfo receive(ByteBuffer dst,
+                                            T attachment,
+                                            NotificationHandler<T> handler)
+        throws IOException;
+
+    /**
+     * Sends a message via this channel.
+     *
+     * <P> If this channel is in non-blocking mode and there is sufficient room
+     * in the underlying output buffer, or if this channel is in blocking mode
+     * and sufficient room becomes available, then the remaining bytes in the
+     * given byte buffer are transmitted as a single message. Sending a message
+     * is atomic unless explicit message completion {@link
+     * SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE SCTP_EXPLICIT_COMPLETE}
+     * socket option is enabled on this channel's socket.
+     *
+     * <P> The message is transferred from the byte buffer as if by a regular
+     * {@link java.nio.channels.WritableByteChannel#write(java.nio.ByteBuffer)
+     * write} operation.
+     *
+     * <P> The bytes will be written to the stream number that is specified by
+     * {@link MessageInfo#streamNumber streamNumber} in the given {@code
+     * messageInfo}.
+     *
+     * <P> This method may be invoked at any time. If another thread has already
+     * initiated a send operation upon this channel, then an invocation of
+     * this method will block until the first operation is complete.
+     *
+     * @param  src
+     *         The buffer containing the message to be sent
+     *
+     * @param  messageInfo
+     *         Ancillary data about the message to be sent
+     *
+     * @return  The number of bytes sent, which will be either the number of
+     *          bytes that were remaining in the messages buffer when this method
+     *          was invoked or, if this channel is non-blocking, may be zero if
+     *          there was insufficient room for the message in the underlying
+     *          output buffer
+     *
+     * @throws  InvalidStreamExcepton
+     *          If {@code streamNumner} is negative or greater than or equal to
+     *          the maximum number of outgoing streams
+     *
+     * @throws  java.nio.channels.ClosedChannelException
+     *          If this channel is closed
+     *
+     * @throws  java.nio.channels.AsynchronousCloseException
+     *          If another thread closes this channel
+     *          while the read operation is in progress
+     *
+     * @throws  java.nio.channels.ClosedByInterruptException
+     *          If another thread interrupts the current thread
+     *          while the read operation is in progress, thereby
+     *          closing the channel and setting the current thread's
+     *          interrupt status
+     *
+     * @throws  java.nio.channels.NotYetConnectedException
+     *          If this channel is not yet connected
+     *
+     * @throws  IOException
+     *          If some other I/O error occurs
+     */
+    public abstract int send(ByteBuffer src, MessageInfo messageInfo)
+        throws IOException;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/SctpMultiChannel.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,731 @@
+/*
+ * Copyright 2009 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 com.sun.nio.sctp;
+
+import java.net.SocketAddress;
+import java.net.InetAddress;
+import java.io.IOException;
+import java.util.Set;
+import java.nio.ByteBuffer;
+import java.nio.channels.spi.AbstractSelectableChannel;
+import java.nio.channels.spi.SelectorProvider;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.NotYetBoundException;
+import java.nio.channels.SelectionKey;
+
+/**
+ * A selectable channel for message-oriented SCTP sockets.
+ *
+ * <P> An SCTP multi channel supports many associations on a single socket.
+ * An {@code SctpMultiChannel} is created by invoking the
+ * {@link #open open} method of this class. A newly-created channel is open but
+ * not yet bound. An attempt to invoke the {@link #receive receive} method of an
+ * unbound channel will cause the {@link NotYetBoundException}
+ * to be thrown. An attempt to invoke the {@link #send send} method of an
+ * unbound channel will cause it to first invoke the {@link #bind bind} method.
+ * The address(es) that the channel's socket is bound to can be retrieved by
+ * calling {@link #getAllLocalAddresses getAllLocalAddresses}.
+ *
+ * <P> Messages may be sent and received without explicitly setting up an
+ * association with the remote peer. The channel will implicitly setup
+ * a new association whenever it sends or receives a message from a remote
+ * peer if there is not already an association with that peer. Upon successful
+ * association setup, an {@link AssociationChangeNotification
+ * association changed} notification will be put to the SCTP stack with its
+ * {@code event} parameter set to {@link
+ * AssociationChangeNotification.AssocChangeEvent#COMM_UP
+ * COMM_UP}. This notification can be received by invoking {@link #receive
+ * receive}.
+ *
+ * <P> Socket options are configured using the
+ * {@link #setOption(SctpSocketOption,Object,Association) setOption} method. An
+ * {@code SctpMultiChannel} supports the following options:
+ * <blockquote>
+ * <table border>
+ *   <tr>
+ *     <th>Option Name</th>
+ *     <th>Description</th>
+ *   </tr>
+ *   <tr>
+ *     <td> {@link SctpStandardSocketOption#SCTP_DISABLE_FRAGMENTS
+ *                                          SCTP_DISABLE_FRAGMENTS} </td>
+ *     <td> Enables or disables message fragmentation </td>
+ *   </tr>
+ *   <tr>
+ *     <td> {@link SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE
+ *                                          SCTP_EXPLICIT_COMPLETE} </td>
+ *     <td> Enables or disables explicit message completion </td>
+ *   </tr>
+ *    <tr>
+ *     <td> {@link SctpStandardSocketOption#SCTP_FRAGMENT_INTERLEAVE
+ *                                          SCTP_FRAGMENT_INTERLEAVE} </td>
+ *     <td> Controls how the presentation of messages occur for the message
+ *          receiver </td>
+ *   </tr>
+ *   <tr>
+ *     <td> {@link SctpStandardSocketOption#SCTP_INIT_MAXSTREAMS
+ *                                          SCTP_INIT_MAXSTREAMS} </td>
+ *     <td> The maximum number of streams requested by the local endpoint during
+ *          association initialization </td>
+ *   </tr>
+ *   <tr>
+ *     <td> {@link SctpStandardSocketOption#SCTP_NODELAY SCTP_NODELAY} </td>
+ *     <td> Enables or disable a Nagle-like algorithm </td>
+ *   </tr>
+ *   <tr>
+ *     <td> {@link SctpStandardSocketOption#SCTP_PRIMARY_ADDR
+ *                                          SCTP_PRIMARY_ADDR} </td>
+ *     <td> Requests that the local SCTP stack use the given peer address as the
+ *          association primary </td>
+ *   </tr>
+ *   <tr>
+ *     <td> {@link SctpStandardSocketOption#SCTP_SET_PEER_PRIMARY_ADDR
+ *                                          SCTP_SET_PEER_PRIMARY_ADDR} </td>
+ *     <td> Requests that the peer mark the enclosed address as the association
+ *          primary </td>
+ *   </tr>
+ *   <tr>
+ *     <td> {@link SctpStandardSocketOption#SO_SNDBUF
+ *                                          SO_SNDBUF} </td>
+ *     <td> The size of the socket send buffer </td>
+ *   </tr>
+ *   <tr>
+ *     <td> {@link SctpStandardSocketOption#SO_RCVBUF
+ *                                          SO_RCVBUF} </td>
+ *     <td> The size of the socket receive buffer </td>
+ *   </tr>
+ *   <tr>
+ *     <td> {@link SctpStandardSocketOption#SO_LINGER
+ *                                          SO_LINGER} </td>
+ *     <td> Linger on close if data is present (when configured in blocking mode
+ *          only) </td>
+ *   </tr>
+ * </table>
+ * </blockquote>
+ * Additional (implementation specific) options may also be supported. The list
+ * of options supported is obtained by invoking the {@link #supportedOptions()
+ * supportedOptions} method.
+ *
+ * <p> SCTP multi channels are safe for use by multiple concurrent threads.
+ * They support concurrent sending and receiving, though at most one thread may be
+ * sending and at most one thread may be receiving at any given time.
+ *
+ * @since 1.7
+ */
+public abstract class SctpMultiChannel
+    extends AbstractSelectableChannel
+{
+    /**
+     * Initializes a new instance of this class.
+     *
+     * @param  provider
+     *         The selector provider for this channel
+     */
+    protected SctpMultiChannel(SelectorProvider provider) {
+        super(provider);
+    }
+
+    /**
+     * Opens an SCTP multi channel.
+     *
+     * <P> The new channel is unbound.
+     *
+     * @return  A new SCTP multi channel
+     *
+     * @throws UnsupportedOperationException
+     *         If the SCTP protocol is not supported
+     *
+     * @throws  IOException
+     *          If an I/O error occurs
+     */
+    public static SctpMultiChannel open() throws
+        IOException {
+        return new sun.nio.ch.SctpMultiChannelImpl((SelectorProvider)null);
+    }
+
+    /**
+     * Returns the open associations on this channel's socket.
+     *
+     * <P> Only associations whose {@link AssociationChangeNotification.AssocChangeEvent#COMM_UP
+     * COMM_UP} association change event has been received are included
+     * in the returned set of associations. Associations for which a
+     * {@link AssociationChangeNotification.AssocChangeEvent#COMM_LOST COMM_LOST} or {@link
+     * AssociationChangeNotification.AssocChangeEvent#SHUTDOWN SHUTDOWN} association change
+     * event have been receive are removed from the set of associations.
+     *
+     * <P> The returned set of associations is a snapshot of the open
+     * associations at the time that this method is invoked.
+     *
+     * @return  A {@code Set} containing the open associations, or an empty
+     *          {@code Set} if there are none.
+     *
+     * @throws  ClosedChannelException
+     *          If this channel is closed
+     *
+     * @throws  IOException
+     *          If some other I/O error occurs
+     */
+    public abstract Set<Association> associations()
+        throws IOException;
+
+    /**
+     * Binds the channel's socket to a local address and configures the socket
+     * to listen for connections.
+     *
+     * <P> This method is used to establish a relationship between the socket
+     * and the local address. Once a relationship is established then
+     * the socket remains bound until the channel is closed. This relationship
+     * may not necesssarily be with the address {@code local} as it may be removed
+     * by {@link #unbindAddress unbindAddress}, but there will always be at least one local
+     * address bound to the channel's socket once an invocation of this method
+     * successfully completes.
+     *
+     * <P> Once the channel's socket has been successfully bound to a specific
+     * address, that is not automatically assigned, more addresses
+     * may be bound to it using {@link #bindAddress bindAddress}, or removed
+     * using {@link #unbindAddress unbindAddress}.
+     *
+     * <P> The backlog parameter is the maximum number of pending connections on
+     * the socket. Its exact semantics are implementation specific. An implementation
+     * may impose an implementation specific maximum length or may choose to ignore
+     * the parameter. If the backlog parameter has the value {@code 0}, or a negative
+     * value, then an implementation specific default is used.
+     *
+     * @param  local
+     *         The local address to bind the socket, or {@code null} to
+     *         bind the socket to an automatically assigned socket address
+     *
+     * @param  backlog
+     *         The maximum number number of pending connections
+     *
+     * @return  This channel
+     *
+     * @throws  ClosedChannelException
+     *          If this channel is closed
+     *
+     * @throws  java.nio.channels.AlreadyBoundException
+     *          If this channel is already bound
+     *
+     * @throws  java.nio.channels.UnsupportedAddressTypeException
+     *          If the type of the given address is not supported
+     *
+     * @throws  SecurityException
+     *          If a security manager has been installed and its {@link
+     *          java.lang.SecurityManager#checkListen(int) checkListen} method
+     *          denies the operation
+     *
+     * @throws  IOException
+     *          If some other I/O error occurs
+     */
+    public abstract SctpMultiChannel bind(SocketAddress local,
+                                          int backlog)
+        throws IOException;
+
+    /**
+     * Binds the channel's socket to a local address and configures the socket
+     * to listen for connections.
+     *
+     * <P> This method works as if invoking it were equivalent to evaluating the
+     * expression:
+     * <blockquote><pre>
+     * bind(local, 0);
+     * </pre></blockquote>
+     *
+     * @param  local
+     *         The local address to bind the socket, or {@code null} to
+     *         bind the socket to an automatically assigned socket address
+     *
+     * @return  This channel
+     *
+     * @throws  ClosedChannelException
+     *          If this channel is closed
+     *
+     * @throws  java.nio.channels.AlreadyBoundException
+     *          If this channel is already bound
+     *
+     * @throws  java.nio.channels.UnsupportedAddressTypeException
+     *          If the type of the given address is not supported
+     *
+     * @throws  SecurityException
+     *          If a security manager has been installed and its {@link
+     *          java.lang.SecurityManager#checkListen(int) checkListen} method
+     *          denies the operation
+     *
+     * @throws  IOException
+     *          If some other I/O error occurs
+     */
+    public final SctpMultiChannel bind(SocketAddress local)
+        throws IOException {
+        return bind(local, 0);
+    }
+
+    /**
+     * Adds the given address to the bound addresses for the channel's
+     * socket.
+     *
+     * <P> The given address must not be the {@link
+     * java.net.InetAddress#isAnyLocalAddress wildcard} address.
+     * The channel must be first bound using {@link #bind bind} before
+     * invoking this method, otherwise {@link NotYetBoundException} is thrown.
+     * The {@link #bind bind} method takes a {@code SocketAddress} as its
+     * argument which typically contains a port number as well as an address.
+     * Addresses subquently bound using this method are simply addresses as the
+     * SCTP port number remains the same for the lifetime of the channel.
+     *
+     * <P> New associations setup after this method successfully completes
+     * will be associated with the given address. Adding addresses to existing
+     * associations is optional functionality. If the endpoint supports
+     * dynamic address reconfiguration then it may send the appropriate message
+     * to the peer to change the peers address lists.
+     *
+     * @param  address
+     *         The address to add to the bound addresses for the socket
+     *
+     * @return  This channel
+     *
+     * @throws  ClosedChannelException
+     *          If this channel is closed
+     *
+     * @throws  NotYetBoundException
+     *          If this channel is not yet bound
+     *
+     * @throws  java.nio.channels.AlreadyBoundException
+     *          If this channel is already bound to the given address
+     *
+     * @throws  IllegalArgumentException
+     *          If address is {@code null} or the {@link
+     *          java.net.InetAddress#isAnyLocalAddress wildcard} address
+     *
+     * @throws  IOException
+     *          If some other I/O error occurs
+     */
+    public abstract SctpMultiChannel bindAddress(InetAddress address)
+         throws IOException;
+
+    /**
+     * Removes the given address from the bound addresses for the channel's
+     * socket.
+     *
+     * <P> The given address must not be the {@link
+     * java.net.InetAddress#isAnyLocalAddress wildcard} address.
+     * The channel must be first bound using {@link #bind bind} before
+     * invoking this method, otherwise {@link NotYetBoundException} is thrown.
+     *
+     * <P> If this method is invoked on a channel that does
+     * not have {@code address} as one of its bound addresses, or that has only
+     * one local address bound to it, then this method throws
+     * {@link IllegalUnbindException}.
+     *
+     * <P> The initial address that the channel's socket is bound to using
+     * {@link #bind bind} may be removed from the bound addresses for the
+     * channel's socket.
+     *
+     * <P> New associations setup after this method successfully completes
+     * will not be associated with the given address. Removing addresses from
+     * existing associations is optional functionality. If the endpoint supports
+     * dynamic address reconfiguration then it may send the appropriate message
+     * to the peer to change the peers address lists.
+     *
+     * @param  address
+     *         The address to remove from the bound addresses for the socket
+     *
+     * @return  This channel
+     *
+     * @throws  ClosedChannelException
+     *          If this channel is closed
+     *
+     * @throws  NotYetBoundException
+     *          If this channel is not yet bound
+     *
+     * @throws  IllegalUnbindException
+     *          {@code address} is not bound to the channel's socket, or the
+     *          channel has only one address  bound to it
+     *
+     * @throws  IllegalArgumentException
+     *          If address is {@code null} or the {@link
+     *          java.net.InetAddress#isAnyLocalAddress wildcard} address
+     *
+     * @throws  IOException
+     *          If some other I/O error occurs
+     */
+    public abstract SctpMultiChannel unbindAddress(InetAddress address)
+         throws IOException;
+
+    /**
+     * Returns all of the socket addresses to which this channel's socket is
+     * bound.
+     *
+     * @return  All the socket addresses that this channel's socket is
+     *          bound to, or an empty {@code Set} if the channel's socket is not
+     *          bound
+     *
+     * @throws  ClosedChannelException
+     *          If the channel is closed
+     *
+     * @throws  IOException
+     *          If an I/O error occurs
+     */
+    public abstract Set<SocketAddress> getAllLocalAddresses()
+        throws IOException;
+
+    /**
+     * Returns all of the remote addresses to which the given association on
+     * this channel's socket is connected.
+     *
+     * @return  All of the remote addresses for the given association, or
+     *          an empty {@code Set} if the association has been shutdown
+     *
+     * @throws  ClosedChannelException
+     *          If the channel is closed
+     *
+     * @throws  IOException
+     *          If an I/O error occurs
+     */
+    public abstract Set<SocketAddress> getRemoteAddresses(Association association)
+        throws IOException;
+
+    /**
+     * Shutdown an association without closing the channel.
+     *
+     * @param  association
+     *         The association to shutdown
+     *
+     * @return  This channel
+     *
+     * @throws  ClosedChannelException
+     *          If this channel is closed
+     *
+     * @throws  IOException
+     *          If some other I/O error occurs
+     */
+    public abstract SctpMultiChannel shutdown(Association association)
+            throws IOException;
+
+    /**
+     * Returns the value of a socket option.
+     *
+     * <P> Note that some options are retrieved on the channel's socket,
+     * therefore the {@code association} parameter is not applicable and will be
+     * ignored if given. However, if the option is association specific then the
+     * association must be given.
+     *
+     * @param  name
+     *         The socket option
+     *
+     * @param  association
+     *         The association whose option should be retrieved, or {@code null}
+     *         if this option should be retrieved at the channel's socket level.
+     *
+     * @return  The value of the socket option. A value of {@code null} may be
+     *          a valid value for some socket options.
+     *
+     * @throws  UnsupportedOperationException
+     *          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 SctpStandardSocketOption
+     */
+    public abstract <T> T getOption(SctpSocketOption<T> name,
+                                    Association association)
+        throws IOException;
+
+    /**
+     * Sets the value of a socket option.
+     *
+     * <P> Note that some options are retrieved on the channel's socket,
+     * therefore the {@code association} parameter is not applicable and will be
+     * ignored if given. However, if the option is association specific then the
+     * association must be given.
+     *
+     * @param   name
+     *          The socket option
+     *
+     * @param  association
+     *         The association whose option should be set, or {@code null}
+     *         if this option should be set at the channel's socket level.
+     *
+     * @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  UnsupportedOperationException
+     *          If the socket option is not supported by this channel
+     *
+     * @throws  IllegalArgumentException
+     *          If 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 SctpStandardSocketOption
+     */
+    public abstract <T> SctpMultiChannel setOption(SctpSocketOption<T> name,
+                                                   T value,
+                                                   Association association)
+         throws IOException;
+
+     /**
+     * Returns a set of the socket options supported by this channel.
+     *
+     * <P> 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
+     */
+    public abstract Set<SctpSocketOption<?>> supportedOptions();
+
+    /**
+     * Returns an operation set identifying this channel's supported operations.
+     *
+     * <P> SCTP multi channels support reading, and writing, so this
+     * method returns
+     * {@code (}{@link SelectionKey#OP_READ} {@code |}&nbsp;{@link
+     * SelectionKey#OP_WRITE}{@code )}.  </p>
+     *
+     * @return  The valid-operation set
+     */
+    @Override
+    public final int validOps() {
+        return (SelectionKey.OP_READ |
+                SelectionKey.OP_WRITE );
+    }
+
+    /**
+     * Receives a message and/or handles a notification via this channel.
+     *
+     * <P> If a message or notification is immediately available, or if this
+     * channel is in blocking mode and one eventually becomes available, then
+     * the message or notification is returned or handled, respectively. If this
+     * channel is in non-blocking mode and a message or notification is not
+     * immediately available then this method immediately returns {@code null}.
+     *
+     * <P> If this method receives a message it is copied into the given byte
+     * buffer and an {@link MessageInfo} is returned.
+     * The message is transferred into the given byte buffer starting at its
+     * current position and the buffers position is incremented by the number of
+     * bytes read. If there are fewer bytes remaining in the buffer than are
+     * required to hold the message, or the underlying input buffer does not
+     * contain the complete message, then an invocation of {@link
+     * MessageInfo#isComplete isComplete} on the returned {@code
+     * MessageInfo} will return {@code false}, and more invocations of this
+     * method will be necessary to completely consume the messgae. Only
+     * one message at a time will be partially delivered in any stream. The
+     * socket option {@link SctpStandardSocketOption#SCTP_FRAGMENT_INTERLEAVE
+     * SCTP_FRAGMENT_INTERLEAVE} controls various aspects of what interlacing of
+     * messages occurs.
+     *
+     * <P> If this method receives a notification then the appropriate method of
+     * the given handler, if there is one, is invoked. If the handler returns {@link
+     * HandlerResult#CONTINUE CONTINUE} then this method will try to receive another
+     * message/notification, otherwise, if {@link HandlerResult#RETURN RETURN} is returned
+     * this method will return {@code null}. If an uncaught exception is thrown by the
+     * handler it will be propagated up the stack through this method.
+     *
+     * <P> If a security manager has been installed then for each new association
+     * setup this method verifies that the associations source address and port
+     * number are permitted by the security manager's {@link
+     * java.lang.SecurityManager#checkAccept(String,int) checkAccept} method.
+     *
+     * <P> This method may be invoked at any time. If another thread has
+     * already initiated a receive operation upon this channel, then an
+     * invocation of this method will block until the first operation is
+     * complete. The given handler is invoked without holding any locks used
+     * to enforce the above synchronization policy, that way handlers
+     * will not stall other threads from receiving. A handler should not invoke
+     * the {@code receive} method of this channel, if it does an
+     * {@link IllegalReceiveException} will be thrown.
+     *
+     * @param  buffer
+     *         The buffer into which bytes are to be transferred
+     *
+     * @param  attachment
+     *         The object to attach to the receive operation; can be
+     *         {@code null}
+     *
+     * @param  handler
+     *         A handler to handle notifications from the SCTP stack, or
+     *         {@code null} to ignore any notifications.
+     *
+     * @return  The {@code MessageInfo}, {@code null} if this channel is in
+     *          non-blocking mode and no messages are immediately available or
+     *          the notification handler returns {@code RETURN} after handling
+     *          a notification
+     *
+     * @throws  java.nio.channels.ClosedChannelException
+     *          If this channel is closed
+     *
+     * @throws  java.nio.channels.AsynchronousCloseException
+     *          If another thread closes this channel
+     *          while the read operation is in progress
+     *
+     * @throws  java.nio.channels.ClosedByInterruptException
+     *          If another thread interrupts the current thread
+     *          while the read operation is in progress, thereby
+     *          closing the channel and setting the current thread's
+     *          interrupt status
+     *
+     * @throws  NotYetBoundException
+     *          If this channel is not yet bound
+     *
+     * @throws  IllegalReceiveException
+     *          If the given handler invokes the {@code receive} method of this
+     *          channel
+     *
+     * @throws  SecurityException
+     *          If a security manager has been installed and it does not permit
+     *          new associations to be accepted from the message's sender
+     *
+     * @throws  IOException
+     *          If some other I/O error occurs
+     */
+    public abstract <T> MessageInfo receive(ByteBuffer buffer,
+                                            T attachment,
+                                            NotificationHandler<T> handler)
+        throws IOException;
+
+    /**
+     * Sends a message via this channel.
+     *
+     * <P> If this channel is unbound then this method will invoke {@link
+     * #bind(SocketAddress, int) bind(null, 0)} before sending any data.
+     *
+     * <P> If there is no association existing between this channel's socket
+     * and the intended receiver, identified by the address in the given messageInfo, then one
+     * will be automatically setup to the intended receiver. This is considered
+     * to be Implicit Association Setup. Upon successful association setup, an
+     * {@link AssociationChangeNotification association changed}
+     * notification will be put to the SCTP stack with its {@code event} parameter set
+     * to {@link AssociationChangeNotification.AssocChangeEvent#COMM_UP COMM_UP}
+     * . This notification can be received by invoking {@link #receive
+     * receive}.
+     *
+     * <P> If this channel is in blocking mode, there is sufficient room in the
+     * underlying output buffer, then the remaining bytes in the given byte
+     * buffer are transmitted as a single message. Sending a message
+     * is atomic unless explicit message completion {@link
+     * SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE SCTP_EXPLICIT_COMPLETE}
+     * socket option is enabled on this channel's socket.
+     *
+     * <P> If this channel is in non-blocking mode, there is sufficient room
+     * in the underlying output buffer, and an implicit association setup is
+     * required, then the remaining bytes in the given byte buffer are
+     * transmitted as a single message, subject to {@link
+     * SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE SCTP_EXPLICIT_COMPLETE}.
+     * If for any reason the message cannot
+     * be delivered an {@link AssociationChangeNotification association
+     * changed} notification is put on the SCTP stack with its {@code event} parameter set
+     * to {@link AssociationChangeNotification.AssocChangeEvent#CANT_START CANT_START}.
+     *
+     * <P> The message is transferred from the byte buffer as if by a regular
+     * {@link java.nio.channels.WritableByteChannel#write(java.nio.ByteBuffer)
+     * write} operation.
+     *
+     * <P> If a security manager has been installed then for each new association
+     * setup this method verifies that the given remote peers address and port
+     * number are permitted by the security manager's {@link
+     * java.lang.SecurityManager#checkConnect(String,int) checkConnect} method.
+     *
+     * <P> This method may be invoked at any time. If another thread has already
+     * initiated a send operation upon this channel, then an invocation of
+     * this method will block until the first operation is complete.
+     *
+     * @param  buffer
+     *         The buffer containing the message to be sent
+     *
+     * @param  messageInfo
+     *         Ancillary data about the message to be sent
+     *
+     * @return  The number of bytes sent, which will be either the number of
+     *          bytes that were remaining in the messages buffer when this method
+     *          was invoked or, if this channel is non-blocking, may be zero if
+     *          there was insufficient room for the message in the underlying
+     *          output buffer
+     *
+     * @throws  InvalidStreamExcepton
+     *          If {@code streamNumber} is negative, or if an association already
+     *          exists and {@code streamNumber} is greater than the maximum number
+     *          of outgoing streams
+     *
+     * @throws  java.nio.channels.ClosedChannelException
+     *          If this channel is closed
+     *
+     * @throws  java.nio.channels.AsynchronousCloseException
+     *          If another thread closes this channel
+     *          while the read operation is in progress
+     *
+     * @throws  java.nio.channels.ClosedByInterruptException
+     *          If another thread interrupts the current thread
+     *          while the read operation is in progress, thereby
+     *          closing the channel and setting the current thread's
+     *          interrupt status
+     *
+     * @throws  SecurityException
+     *          If a security manager has been installed and it does not permit
+     *          new associations to be setup with the the messages's address
+     *
+     * @throws  IOException
+     *          If some other I/O error occurs
+     */
+    public abstract int send(ByteBuffer buffer, MessageInfo messageInfo)
+        throws IOException;
+
+    /**
+     * Branches off an association.
+     *
+     * <P> An application can invoke this method to branch off an association
+     * into a separate channel. The new bound and connected {@link SctpChannel}
+     * will be created for the association. The branched off association will no
+     * longer be part of this channel.
+     *
+     * <P> This is particularly useful when, for instance, the application
+     * wishes to have a number of sporadic message senders/receivers remain
+     * under the original SCTP multi channel but branch off those
+     * associations carrying high volume data traffic into their own
+     * separate SCTP channels.
+     *
+     * @param  association
+     *         The association to branch off
+     *
+     * @return  The {@code SctpChannel}
+     *
+     * @throws  java.nio.channels.ClosedChannelException
+     *          If this channel is closed
+     *
+     * @throws  IOException
+     *          If some other I/O error occurs
+     */
+    public abstract SctpChannel branch(Association association)
+        throws IOException;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/SctpServerChannel.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,421 @@
+/*
+ * Copyright 2009 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 com.sun.nio.sctp;
+
+import java.net.SocketAddress;
+import java.net.InetAddress;
+import java.io.IOException;
+import java.util.Set;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.spi.SelectorProvider;
+import java.nio.channels.spi.AbstractSelectableChannel;
+
+/**
+ * A selectable channel for message-oriented listening SCTP sockets.
+ *
+ * <p> An {@code SCTPServerChannel} is created by invoking the
+ * {@link #open open} method of this class. A newly-created SCTP server
+ * channel is open but not yet bound. An attempt to invoke the
+ * {@link #accept accept} method of an unbound channel will cause the
+ * {@link java.nio.channels.NotYetBoundException} to be thrown. An SCTP server
+ * channel can be bound by invoking one of the
+ * {@link #bind(java.net.SocketAddress,int) bind} methods defined by this class.
+ *
+ * <p> Socket options are configured using the
+ * {@link #setOption(SctpSocketOption,Object) setOption} method. SCTP server socket
+ * channels support the following options:
+ * <blockquote>
+ * <table border>
+ *   <tr>
+ *     <th>Option Name</th>
+ *     <th>Description</th>
+ *   </tr>
+ *   <tr>
+ *     <td> {@link SctpStandardSocketOption#SCTP_INIT_MAXSTREAMS
+ *                                          SCTP_INIT_MAXSTREAMS} </td>
+ *     <td> The maximum number of streams requested by the local endpoint during
+ *          association initialization </td>
+ *   </tr>
+ * </table>
+ * </blockquote>
+ * Additional (implementation specific) options may also be supported. The list
+ * of options supported is obtained by invoking the {@link #supportedOptions()
+ * supportedOptions} method.
+ *
+ * <p>SCTP server channels are safe for use by multiple concurrent threads.
+ *
+ * @since 1.7
+ */
+public abstract class SctpServerChannel
+    extends AbstractSelectableChannel
+{
+    /**
+     * Initializes a new instance of this class.
+     *
+     * @param  provider
+     *         The selector provider for this channel
+     */
+    protected SctpServerChannel(SelectorProvider provider) {
+        super(provider);
+    }
+
+    /**
+     * Opens an SCTP server channel.
+     *
+     * <P> The new channel's socket is initially unbound; it must be bound
+     * to a specific address via one of its socket's {@link #bind bind}
+     * methods before associations can be accepted.
+     *
+     * @return  A new SCTP server channel
+     *
+     * @throws  UnsupportedOperationException
+     *          If the SCTP protocol is not supported
+     *
+     * @throws  IOException
+     *          If an I/O error occurs
+     */
+    public static SctpServerChannel open() throws
+        IOException {
+        return new sun.nio.ch.SctpServerChannelImpl((SelectorProvider)null);
+    }
+
+    /**
+     * Accepts an association on this channel's socket.
+     *
+     * <P> If this channel is in non-blocking mode then this method will
+     * immediately return {@code null} if there are no pending associations.
+     * Otherwise it will block indefinitely until a new association is
+     * available or an I/O error occurs.
+     *
+     * <P> The {@code SCTPChannel} returned by this method, if any, will be in
+     *  blocking mode regardless of the blocking mode of this channel.
+     *
+     * <P> If a security manager has been installed then for each new
+     * association this method verifies that the address and port number of the
+     * assocaitions's remote peer are permitted by the security manager's {@link
+     * java.lang.SecurityManager#checkAccept(String,int) checkAccept} method.
+     *
+     * @return  The SCTP channel for the new association, or {@code null}
+     *          if this channel is in non-blocking mode and no association is
+     *          available to be accepted
+     *
+     * @throws  java.nio.channels.ClosedChannelException
+     *          If this channel is closed
+     *
+     * @throws  java.nio.channels.AsynchronousCloseException
+     *          If another thread closes this channel
+     *          while the accept operation is in progress
+     *
+     * @throws  java.nio.channels.ClosedByInterruptException
+     *          If another thread interrupts the current thread
+     *          while the accept operation is in progress, thereby
+     *          closing the channel and setting the current thread's
+     *          interrupt status
+     *
+     * @throws  java.nio.channels.NotYetBoundException
+     *          If this channel's socket has not yet been bound
+     *
+     * @throws  SecurityException
+     *          If a security manager has been installed and it does not permit
+     *          access to the remote peer of the new association
+     *
+     * @throws  IOException
+     *          If some other I/O error occurs
+     */
+    public abstract SctpChannel accept() throws IOException;
+
+    /**
+     * Binds the channel's socket to a local address and configures the socket
+     * to listen for associations.
+     *
+     * <P> This method works as if invoking it were equivalent to evaluating the
+     * expression:
+     * <blockquote><pre>
+     * bind(local, 0);
+     * </pre></blockquote>
+     *
+     * @param  local
+     *         The local address to bind the socket, or {@code null} to
+     *         bind the socket to an automatically assigned socket address
+     *
+     * @return  This channel
+     *
+     * @throws  java.nio.channels.ClosedChannelException
+     *          If this channel is closed
+     *
+     * @throws  java.nio.channels.AlreadyBoundException
+     *          If this channel is already bound
+     *
+     * @throws  java.nio.channels.UnsupportedAddressTypeException
+     *          If the type of the given address is not supported
+     *
+     * @throws  SecurityException
+     *          If a security manager has been installed and its {@link
+     *          java.lang.SecurityManager#checkListen(int) checkListen} method
+     *          denies the operation
+     *
+     * @throws  IOException
+     *          If some other I/O error occurs
+     */
+    public final SctpServerChannel 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 associations.
+     *
+     * <P> This method is used to establish a relationship between the socket
+     * and the local address. Once a relationship is established then
+     * the socket remains bound until the channel is closed. This relationship
+     * may not necesssarily be with the address {@code local} as it may be
+     * removed by {@link #unbindAddress unbindAddress}, but there will always be
+     * at least one local address bound to the channel's socket once an
+     * invocation of this method successfully completes.
+     *
+     * <P> Once the channel's socket has been successfully bound to a specific
+     * address, that is not automatically assigned, more addresses
+     * may be bound to it using {@link #bindAddress bindAddress}, or removed
+     * using {@link #unbindAddress unbindAddress}.
+     *
+     * <P> The backlog parameter is the maximum number of pending associations
+     * on the socket. Its exact semantics are implementation specific. An
+     * implementation may impose an implementation specific maximum length or
+     * may choose to ignore the parameter. If the backlog parameter has the
+     * value {@code 0}, or a negative value, then an implementation specific
+     * default is used.
+     *
+     * @param  local
+     *         The local address to bind the socket, or {@code null} to
+     *         bind the socket to an automatically assigned socket address
+     *
+     * @param  backlog
+     *         The maximum number number of pending associations
+     *
+     * @return  This channel
+     *
+     * @throws  java.nio.channels.ClosedChannelException
+     *          If this channel is closed
+     *
+     * @throws  java.nio.channels.AlreadyBoundException
+     *          If this channel is already bound
+     *
+     * @throws  java.nio.channels.UnsupportedAddressTypeException
+     *          If the type of the given address is not supported
+     *
+     * @throws  SecurityException
+     *          If a security manager has been installed and its {@link
+     *          java.lang.SecurityManager#checkListen(int) checkListen} method
+     *          denies the operation
+     *
+     * @throws  IOException
+     *          If some other I/O error occurs
+     */
+    public abstract SctpServerChannel bind(SocketAddress local,
+                                           int backlog)
+        throws IOException;
+
+    /**
+     * Adds the given address to the bound addresses for the channel's
+     * socket.
+     *
+     * <P> The given address must not be the {@link
+     * java.net.InetAddress#isAnyLocalAddress wildcard} address.
+     * The channel must be first bound using {@link #bind bind} before
+     * invoking this method, otherwise {@link
+     * java.nio.channels.NotYetBoundException} is thrown. The {@link #bind bind}
+     * method takes a {@code SocketAddress} as its argument which typically
+     * contains a port number as well as an address. Addresses subquently bound
+     * using this method are simply addresses as the SCTP port number remains
+     * the same for the lifetime of the channel.
+     *
+     * <P> New associations accepted after this method successfully completes
+     * will be associated with the given address.
+     *
+     * @param  address
+     *         The address to add to the bound addresses for the socket
+     *
+     * @return  This channel
+     *
+     * @throws  java.nio.channels.ClosedChannelException
+     *          If this channel is closed
+     *
+     * @throws  java.nio.channels.NotYetBoundException
+     *          If this channel is not yet bound
+     *
+     * @throws  java.nio.channels.AlreadyBoundException
+     *          If this channel is already bound to the given address
+     *
+     * @throws  IllegalArgumentException
+     *          If address is {@code null} or the {@link
+     *          java.net.InetAddress#isAnyLocalAddress wildcard} address
+     *
+     * @throws  IOException
+     *          If some other I/O error occurs
+     */
+    public abstract SctpServerChannel bindAddress(InetAddress address)
+         throws IOException;
+
+    /**
+     * Removes the given address from the bound addresses for the channel's
+     * socket.
+     *
+     * <P> The given address must not be the {@link
+     * java.net.InetAddress#isAnyLocalAddress wildcard} address.
+     * The channel must be first bound using {@link #bind bind} before
+     * invoking this method, otherwise
+     * {@link java.nio.channels.NotYetBoundException} is thrown.
+     * If this method is invoked on a channel that does not have
+     * {@code address} as one of its bound addresses, or that has only one
+     * local address bound to it, then this method throws {@link
+     * IllegalUnbindException}.
+     * The initial address that the channel's socket is bound to using
+     * {@link #bind bind} may be removed from the bound addresses for the
+     * channel's socket.
+     *
+     * <P> New associations accepted after this method successfully completes
+     * will not be associated with the given address.
+     *
+     * @param  address
+     *         The address to remove from the bound addresses for the socket
+     *
+     * @return  This channel
+     *
+     * @throws  java.nio.channels.ClosedChannelException
+     *          If this channel is closed
+     *
+     * @throws  java.nio.channels.NotYetBoundException
+     *          If this channel is not yet bound
+     *
+     * @throws  IllegalArgumentException
+     *          If address is {@code null} or the {@link
+     *          java.net.InetAddress#isAnyLocalAddress wildcard} address
+     *
+     * @throws  IllegalUnbindException
+     *          If the implementation does not support removing addresses from a
+     *          listening socket, {@code address} is not bound to the channel's
+     *          socket, or the channel has only one address bound to it
+     *
+     * @throws  IOException
+     *          If some other I/O error occurs
+     */
+    public abstract SctpServerChannel unbindAddress(InetAddress address)
+         throws IOException;
+
+    /**
+     * Returns all of the socket addresses to which this channel's socket is
+     * bound.
+     *
+     * @return  All the socket addresses that this channel's socket is
+     *          bound to, or an empty {@code Set} if the channel's socket is not
+     *          bound
+     *
+     * @throws  java.nio.channels.ClosedChannelException
+     *          If the channel is closed
+     *
+     * @throws  IOException
+     *          If an I/O error occurs
+     */
+    public abstract Set<SocketAddress> getAllLocalAddresses()
+        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  UnsupportedOperationException
+     *          If the socket option is not supported by this channel
+     *
+     * @throws  java.nio.channels.ClosedChannelException
+     *          If this channel is closed
+     *
+     * @throws  IOException
+     *          If an I/O error occurs
+     *
+     * @see SctpStandardSocketOption
+     */
+    public abstract <T> T getOption(SctpSocketOption<T> name) 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  UnsupportedOperationException
+     *          If the socket option is not supported by this channel
+     *
+     * @throws  IllegalArgumentException
+     *          If the value is not a valid value for this socket option
+     *
+     * @throws  java.nio.channels.ClosedChannelException
+     *          If this channel is closed
+     *
+     * @throws  IOException
+     *          If an I/O error occurs
+     *
+     * @see SctpStandardSocketOption
+     */
+    public abstract <T> SctpServerChannel setOption(SctpSocketOption<T> name,
+                                                    T value)
+        throws IOException;
+
+    /**
+     * Returns a set of the socket options supported by this channel.
+     *
+     * <P> 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
+     */
+    public abstract Set<SctpSocketOption<?>> supportedOptions();
+
+    /**
+     * Returns an operation set identifying this channel's supported
+     * operations.
+     *
+     * <P> SCTP server channels only support the accepting of new
+     * associations, so this method returns
+     * {@link java.nio.channels.SelectionKey#OP_ACCEPT}.
+     *
+     * @return  The valid-operation set
+     */
+    @Override
+    public final int validOps() {
+        return SelectionKey.OP_ACCEPT;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/SctpSocketOption.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2009 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 com.sun.nio.sctp;
+
+import java.net.SocketOption;
+
+/**
+ * A socket option associated with an SCTP channel.
+ *
+ * @param   <T>     The type of the socket option value.
+ *
+ * @since 1.7
+ *
+ * @see SctpStandardSocketOption
+ */
+public interface SctpSocketOption<T> extends SocketOption<T> { }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/SctpStandardSocketOption.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,419 @@
+/*
+ * Copyright 2009 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 com.sun.nio.sctp;
+
+import java.net.SocketAddress;
+import sun.nio.ch.SctpStdSocketOption;
+
+/**
+ * SCTP channels supports the socket options defined by this class
+ * (as well as those listed in the particular channel class) and may support
+ * additional Implementation specific socket options.
+ *
+ * @since 1.7
+ */
+public class SctpStandardSocketOption {
+    private SctpStandardSocketOption() {}
+    /**
+     * Enables or disables message fragmentation.
+     *
+     * <P> The value of this socket option is a {@code Boolean} that represents
+     * whether the option is enabled or disabled. If enabled no SCTP message
+     * fragmentation will be performed. Instead if a message being sent
+     * exceeds the current PMTU size, the message will NOT be sent and
+     * an error will be indicated to the user.
+     *
+     * <P> It is implementation specific whether or not this option is
+     * supported.
+     */
+    public static final SctpSocketOption<Boolean> SCTP_DISABLE_FRAGMENTS = new
+        SctpStdSocketOption<Boolean>("SCTP_DISABLE_FRAGMENTS", Boolean.class,
+        sun.nio.ch.SctpStdSocketOption.SCTP_DISABLE_FRAGMENTS);
+
+    /**
+     * Enables or disables explicit message completion.
+     *
+     * <p> The value of this socket option is a {@code Boolean} that represents
+     * whether the option is enabled or disabled. When this option is enabled,
+     * the {@code send} method may be invoked multiple times to a send message.
+     * The {@code isComplete} parameter of the {@link MessageInfo} must only
+     * be set to {@code true} for the final send to indicate that the message is
+     * complete. If this option is disabled then each individual {@code send}
+     * invocation is considered complete.
+     *
+     * <P> The default value of the option is {@code false} indicating that the
+     * option is disabled. It is implementation specific whether or not this
+     * option is supported.
+     */
+    public static final SctpSocketOption<Boolean> SCTP_EXPLICIT_COMPLETE = new
+        SctpStdSocketOption<Boolean>("SCTP_EXPLICIT_COMPLETE", Boolean.class,
+        sun.nio.ch.SctpStdSocketOption.SCTP_EXPLICIT_COMPLETE);
+
+    /**
+     * Fragmented interleave controls how the presentation of messages occur
+     * for the message receiver. There are three levels of fragment interleave
+     * defined. Two of the levels effect {@link SctpChannel}, while
+     * {@link SctpMultiChannel} is effected by all three levels.
+     *
+     * <P> This option takes an {@code Integer} value. It can be set to a value
+     * of {@code 0}, {@code 1} or {@code 2}.
+     *
+     * <P> Setting the three levels provides the following receiver
+     * interactions:
+     *
+     * <P> {@code level 0} - Prevents the interleaving of any messages. This
+     * means that when a partial delivery begins, no other messages will be
+     * received except the message being partially delivered. If another message
+     * arrives on a different stream (or association) that could be delivered,
+     * it will be blocked waiting for the user to read all of the partially
+     * delivered message.
+     *
+     * <P> {@code level 1} - Allows interleaving of messages that are from
+     * different associations. For {@code SctpChannel}, level 0 and
+     * level 1 have the same meaning since an {@code SctpChannel} always
+     * receives messages from the same association. Note that setting an {@code
+     * SctpMultiChannel} to this level may cause multiple partial
+     * delivers from different associations but for any given association, only
+     * one message will be delivered until all parts of a message have been
+     * delivered. This means that one large message, being read with an
+     * association identification of "X", will block other messages from
+     * association "X" from being delivered.
+     *
+     * <P> {@code level 2} - Allows complete interleaving of messages. This
+     * level requires that the sender carefully observe not only the peer
+     * {@code Association} but also must pay careful attention to the stream
+     * number. With this option enabled a partially delivered message may begin
+     * being delivered for association "X" stream "Y" and the next subsequent
+     * receive may return a message from association "X" stream "Z". Note that
+     * no other messages would be delivered for association "X" stream "Y"
+     * until all of stream "Y"'s partially delivered message was read.
+     * Note that this option effects both channel types.  Also note that
+     * for an {@code SctpMultiChannel} not only may another streams
+     * message from the same association be delivered from the next receive,
+     * some other associations message may be delivered upon the next receive.
+     *
+     * <P> It is implementation specific whether or not this option is
+     * supported.
+     */
+    public static final SctpSocketOption<Integer> SCTP_FRAGMENT_INTERLEAVE =
+            new SctpStdSocketOption<Integer>("SCTP_FRAGMENT_INTERLEAVE",
+                  Integer.class,
+                  sun.nio.ch.SctpStdSocketOption.SCTP_FRAGMENT_INTERLEAVE);
+
+    /**
+     * The maximum number of streams requested by the local endpoint during
+     * association initialization.
+     *
+     * <P> The value of this socket option is an {@link
+     * SctpStandardSocketOption.InitMaxStreams InitMaxStreams}, that represents
+     * the maximum number of inbound and outbound streams that an association
+     * on the channel is prepared to support.
+     *
+     * <P> For an {@link SctpChannel} this option may only be used to
+     * change the number of inbound/outbound streams prior to connecting.
+     *
+     * <P> For an {@link SctpMultiChannel} this option determines
+     * the maximum number of inbound/outbound streams new associations setup
+     * on the channel will be prepared to support.
+     *
+     * <P> For an {@link SctpServerChannel} this option determines the
+     * maximum number of inbound/outbound streams accepted sockets will
+     * negotiate with their connecting peer.
+     *
+     * <P> In all cases the value set by this option is used in the negotiation
+     * of new associations setup on the channel's socket and the actual
+     * maximum number of inbound/outbound streams that have been negotiated
+     * with the peer can be retrieved from the appropriate {@link
+     * Association}. The {@code Association} can be retrieved from the
+     * {@link AssociationChangeNotification.AssocChangeEvent#COMM_UP COMM_UP}
+     * {@link AssociationChangeNotification} belonging to that association.
+     *
+     * <p> This value is bounded by the actual implementation. In other
+     * words the user may be able to support more streams than the Operating
+     * System. In such a case, the Operating System limit may override the
+     * value requested by the user. The default value of 0 indicates to use
+     * the endpoints default value.
+     */
+    public static final SctpSocketOption
+        <SctpStandardSocketOption.InitMaxStreams> SCTP_INIT_MAXSTREAMS =
+        new SctpStdSocketOption<SctpStandardSocketOption.InitMaxStreams>(
+        "SCTP_INIT_MAXSTREAMS", SctpStandardSocketOption.InitMaxStreams.class);
+
+    /**
+     * Enables or disables a Nagle-like algorithm.
+     *
+     * <P> The value of this socket option is a {@code Boolean} that represents
+     * whether the option is enabled or disabled. SCTP uses an algorithm like
+     * <em>The Nagle Algorithm</em> to coalesce short segments and
+     * improve network efficiency.
+     */
+    public static final SctpSocketOption<Boolean> SCTP_NODELAY =
+        new SctpStdSocketOption<Boolean>("SCTP_NODELAY", Boolean.class,
+        sun.nio.ch.SctpStdSocketOption.SCTP_NODELAY);
+
+    /**
+     * Requests that the local SCTP stack use the given peer address as
+     * the association primary.
+     *
+     * <P> The value of this socket option is a {@code SocketAddress}
+     * that represents the peer address that the local SCTP stack should use as
+     * the association primary. The address must be one of the association
+     * peer's addresses.
+     *
+     * <P> An {@code SctpMultiChannel} can control more than one
+     * association, the association parameter must be given when setting or
+     * retrieving this option.
+     *
+     * <P> Since {@code SctpChannel} only controls one association,
+     * the association parameter is not required and this option can be
+     * set or queried directly.
+     */
+     public static final SctpSocketOption<SocketAddress> SCTP_PRIMARY_ADDR =
+             new SctpStdSocketOption<SocketAddress>
+             ("SCTP_PRIMARY_ADDR", SocketAddress.class);
+
+     /**
+     * Requests that the peer mark the enclosed address as the association
+     * primary.
+     *
+     * <P> The value of this socket option is a {@code SocketAddress}
+     * that represents the local address that the peer should use as its
+     * primary address. The given address must be one of the association's
+     * locally bound addresses.
+     *
+     * <P> An {@code SctpMultiChannel} can control more than one
+     * association, the association parameter must be given when setting or
+     * retrieving this option.
+     *
+     * <P> Since {@code SctpChannel} only controls one association,
+     * the association parameter is not required and this option can be
+     * queried directly.
+     *
+     * <P> Note, this is a set only option and cannot be retrieved by {@code
+     * getOption}. It is implementation specific whether or not this
+     * option is supported.
+     */
+    public static final SctpSocketOption<SocketAddress> SCTP_SET_PEER_PRIMARY_ADDR =
+            new SctpStdSocketOption<SocketAddress>
+            ("SCTP_SET_PEER_PRIMARY_ADDR", SocketAddress.class);
+
+    /**
+     * The size of the socket send buffer.
+     *
+     * <p> 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 <em>hint</em> to the implementation to size the buffer and the actual
+     * size may differ. The socket option can be queried to retrieve the actual
+     * size.
+     *
+     * <p> For {@code SctpChannel}, this controls the amount of data
+     * the SCTP stack may have waiting in internal buffers to be sent. This
+     * option therefore bounds the maximum size of data that can be sent in a
+     * single send call.
+     *
+     * <P> For {@code SctpMultiChannel}, the effect is the same as for {@code
+     * SctpChannel}, except that it applies to all associations. The option
+     * applies to each association's window size separately.
+     *
+     * <p> 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 SctpSocketOption<Integer> SO_SNDBUF =
+        new SctpStdSocketOption<Integer>("SO_SNDBUF", Integer.class,
+        sun.nio.ch.SctpStdSocketOption.SO_SNDBUF);
+
+    /**
+     * The size of the socket receive buffer.
+     *
+     * <P> 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
+     * <em>hint</em> to the implementation to size the buffer and the actual
+     * size may differ.
+     *
+     * <P> For {@code SctpChannel}, this controls the receiver window size.
+     *
+     * <P> For {@code SctpMultiChannel}, the meaning is implementation
+     * dependent. It might control the receive buffer for each association bound
+     * to the socket descriptor or it might control the receive buffer for the
+     * whole socket.
+     *
+     * <p> 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.
+     */
+    public static final SctpSocketOption<Integer> SO_RCVBUF =
+        new SctpStdSocketOption<Integer>("SO_RCVBUF", Integer.class,
+        sun.nio.ch.SctpStdSocketOption.SO_RCVBUF);
+
+    /**
+     * Linger on close if data is present.
+     *
+     * <p> 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
+     * <em>linger interval</em>. 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.
+     *
+     * <p> 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.
+     *
+     * <p> 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 SctpSocketOption<Integer> SO_LINGER =
+        new SctpStdSocketOption<Integer>("SO_LINGER", Integer.class,
+        sun.nio.ch.SctpStdSocketOption.SO_LINGER);
+
+    /**
+     * This class is used to set the maximum number of inbound/outbound streams
+     * used by the local endpoint during association initialization. An
+     * instance of this class is used to set the {@link
+     * SctpStandardSocketOption#SCTP_INIT_MAXSTREAMS SCTP_INIT_MAXSTREAMS}
+     * socket option.
+     *
+     * @since 1.7
+     */
+    public static class InitMaxStreams {
+        private int maxInStreams;
+        private int maxOutStreams;
+
+        private InitMaxStreams(int maxInStreams, int maxOutStreams) {
+           this.maxInStreams = maxInStreams;
+           this.maxOutStreams = maxOutStreams;
+        }
+
+        /**
+         * Creates an InitMaxStreams instance.
+         *
+         * @param  maxInStreams
+         *         The maximum number of inbound streams, where
+         *         {@code 0 <= maxInStreams <= 65536}
+         *
+         * @param  maxOutStreams
+         *         The maximum number of outbound streams, where
+         *         {@code 0 <= maxOutStreams <= 65536}
+         *
+         * @return  An {@code InitMaxStreams} instance
+         *
+         * @throws  IllegalArgumentException
+         *          If an argument is outside of specified bounds
+         */
+        public static InitMaxStreams create
+              (int maxInStreams, int maxOutStreams) {
+            if (maxOutStreams < 0 || maxOutStreams > 65535)
+                throw new IllegalArgumentException(
+                      "Invalid maxOutStreams value");
+            if (maxInStreams < 0 || maxInStreams > 65535)
+                throw new IllegalArgumentException(
+                      "Invalid maxInStreams value");
+
+            return new InitMaxStreams(maxInStreams, maxOutStreams);
+        }
+
+        /**
+         * Returns the maximum number of inbound streams.
+         *
+         * @return  Maximum inbound streams
+         */
+        public int maxInStreams() {
+            return maxInStreams;
+        }
+
+        /**
+         * Returns the maximum number of outbound streams.
+         *
+         * @return  Maximum outbound streams
+         */
+        public int maxOutStreams() {
+            return maxOutStreams;
+        }
+
+        /**
+         * Returns a string representation of this init max streams, including
+         * the maximum in and out bound streams.
+         *
+         * @return  A string representation of this init max streams
+         */
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append(super.toString()).append(" [");
+            sb.append("maxInStreams:").append(maxInStreams);
+            sb.append("maxOutStreams:").append(maxOutStreams).append("]");
+            return sb.toString();
+        }
+
+        /**
+         * Returns true if the specified object is another {@code InitMaxStreams}
+         * instance with the same number of in and out bound streams.
+         *
+         * @param  obj
+         *         The object to be compared with this init max streams
+         *
+         * @return  true if the specified object is another
+         *          {@code InitMaxStreams} instance with the same number of in
+         *          and out bound streams
+         */
+        @Override
+        public boolean equals(Object obj) {
+            if (obj != null && obj instanceof InitMaxStreams) {
+                InitMaxStreams that = (InitMaxStreams) obj;
+                if (this.maxInStreams == that.maxInStreams &&
+                    this.maxOutStreams == that.maxOutStreams)
+                    return true;
+            }
+            return false;
+        }
+
+        /**
+         * Returns a hash code value for this init max streams.
+         */
+        @Override
+        public int hashCode() {
+            int hash = 7 ^ maxInStreams ^ maxOutStreams;
+            return hash;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/SendFailedNotification.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2009 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 com.sun.nio.sctp;
+
+import java.nio.ByteBuffer;
+import java.net.SocketAddress;
+
+/**
+ * Notification emitted when a send failed notification has been received.
+ *
+ * <P> A send failed notification indicates that a message cannot be delivered.
+ * Typically this is because the association has been shutdown with unsent data
+ * in the socket output buffer, or in the case of a {@link SctpMultiChannel}
+ * the association failed to setup.
+ *
+ * @since 1.7
+ */
+public abstract class SendFailedNotification implements Notification {
+    /**
+     * Initializes a new instance of this class.
+     */
+    protected SendFailedNotification() {}
+
+    /**
+     * Returns the association that this notification is applicable to.
+     *
+     * @return  The association that failed to send, or {@code null} if
+     *          there is no association, that is, the notification follows a
+     *          {@linkplain
+     *          com.sun.nio.sctp.AssociationChangeNotification.AssocChangeEvent#CANT_START}
+     */
+    @Override
+    public abstract Association association();
+
+    /**
+     * Returns the address.
+     *
+     * @return  The peer primary address of the association or the address that
+     *          the message was sent to
+     */
+    public abstract SocketAddress address();
+
+    /**
+     * Returns the data that was to be sent.
+     *
+     * @return  The user data. The buffers position will be {@code 0} and its
+     *          limit will be set to the end of the data.
+     */
+    public abstract ByteBuffer buffer();
+
+    /**
+     * Returns the error code.
+     *
+     * <P> The errorCode gives the reason why the send failed, and if set, will
+     * be a SCTP protocol error code as defined in RFC2960 section 3.3.10
+     *
+     * @return  The error code
+     */
+    public abstract int errorCode();
+
+    /**
+     * Returns the stream number that the messge was to be sent on.
+     *
+     * @return  The stream number
+     */
+    public abstract int streamNumber();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/ShutdownNotification.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2009 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 com.sun.nio.sctp;
+
+/**
+ * Notification emitted when a peers shutdowns an the association.
+ *
+ * <P> When a peer sends a <i>SHUTDOWN</i>, the SCTP stack delivers this
+ * notification to inform the application that it should cease sending data.
+ *
+ * @since 1.7
+ */
+public abstract class ShutdownNotification implements Notification {
+    /**
+     * Initializes a new instance of this class.
+     */
+    protected ShutdownNotification() {}
+
+    /**
+     * Returns the association that this notification is applicable to.
+     *
+     * @return  The association that received the shutdown
+     */
+    public abstract Association association();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/nio/sctp/package-info.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2009 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.
+ */
+
+/**
+ * A Java API for Stream Control Transport Protocol.
+ *
+ * <P> The Stream Control Transport Protocol (SCTP) is a reliable,
+ * message-oriented, transport protocol existing at an equivalent level with UDP
+ * (User Datagram Protocol) and TCP (Transmission Control Protocol). SCTP is
+ * session oriented and an association between the endpoints must be established
+ * before any data can be transmitted.
+ *
+ * <P> SCTP has direct support for multi-homing, meaning than an endpoint may be
+ * represented by more than one address and each address may be used for sending
+ * and receiving data, thus providing network redundancy. The connection between
+ * two endpoints is referred to as an association between those endpoints.
+ * Endpoints can exchange a list of addresses during association setup. One
+ * address is designated as the primary address, this is the default address that
+ * the peer will use for sending data. A single port number is used across the
+ * entire address list at an endpoint for a specific session.
+ *
+ * <P> SCTP is message based. I/O operations operate upon messages and message
+ * boundaries are preserved. Each association may support multiple independant
+ * logical streams. Each stream represents a sequence of messages within a single
+ * association and streams are independant of one another, meaning that stream
+ * identifiers and sequence numbers are included in the data packet to allow
+ * sequencing of messages on a per-stream basis.
+ *
+ * <P> This package provides two programming model styles. The one-to-one style
+ * supported by {@link com.sun.nio.sctp.SctpChannel} and {@link
+ * com.sun.nio.sctp.SctpServerChannel}, and the one-to-many
+ * style supported by {@link com.sun.nio.sctp.SctpMultiChannel}.
+ * The semantics of the one-to-one style interface are very similar to TCP.
+ * An {@code SctpChannel} can only control one SCTP association. The
+ * semantics of the one-to-many style interface are very similar to UDP. An
+ * {@code SctpMutliChannel} can control multiple SCTP associations.
+ *
+ * <P> Applications can send and receive per-message ancillary information through
+ * {@link com.sun.nio.sctp.MessageInfo}. For example, the stream number that
+ * the message it is to be sent or received from. The SCTP stack is event driven
+ * and applications can receive notifications of certain SCTP events by invoking
+ * the {@code receive} method of the SCTP channel with an appropriate {@link
+ * com.sun.nio.sctp.NotificationHandler notification handler}.
+ *
+ * <P> The SCTP protocol is defined by
+ * <A HREF="http://tools.ietf.org/html/rfc4960">RFC4960</A>, and the optional
+ * extension for <I>Dynamic Address Reconfiguration</I> is defined by
+ * <A HREF="http://tools.ietf.org/html/rfc5061">RFC5061</A>.
+ *
+ * @since 1.7
+ */
+
+package com.sun.nio.sctp;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/nio/ch/SctpMessageInfoImpl.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2009 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.SocketAddress;
+import com.sun.nio.sctp.MessageInfo;
+import com.sun.nio.sctp.Association;
+
+/**
+ * An implementation of a MessageInfo.
+ */
+public class SctpMessageInfoImpl extends MessageInfo {
+    private final SocketAddress address;
+    private final int bytes;          /* 0 */
+
+    private Association association;
+    private int assocId;
+    private int streamNumber;
+    private boolean complete = true;
+    private boolean unordered;  /* false */
+    private long timeToLive;    /* 0L */
+    private int ppid;           /* 0 */
+
+    public SctpMessageInfoImpl(Association association,
+                               SocketAddress address,
+                               int streamNumber) {
+        this.association = association;
+        this.address = address;
+        this.streamNumber = streamNumber;
+        bytes = 0;
+    }
+
+    /* Invoked from native */
+    private SctpMessageInfoImpl(int assocId,
+                                SocketAddress address,
+                                int bytes,
+                                int streamNumber,
+                                boolean complete,
+                                boolean unordered,
+                                int ppid) {
+        this.assocId = assocId;
+        this.address = address;
+        this.bytes = bytes;
+        this.streamNumber = streamNumber;
+        this.complete = complete;
+        this.unordered = unordered;
+        this.ppid = ppid;
+    }
+
+    @Override
+    public Association association() {
+        return association;
+    }
+
+    /**
+     * SctpMessageInfoImpl instances created from native will need to have their
+     * association set from the channel.
+     */
+    void setAssociation(Association association) {
+        this.association = association;
+    }
+
+    int associationID() {
+        return assocId;
+    }
+
+    @Override
+    public SocketAddress address() {
+        return address;
+    }
+
+    @Override
+    public int bytes() {
+        return bytes;
+    }
+
+    @Override
+    public int streamNumber() {
+        return streamNumber;
+    }
+
+    @Override
+    public MessageInfo streamNumber(int streamNumber) {
+        if (streamNumber < 0 || streamNumber > 65536)
+            throw new IllegalArgumentException("Invalid stream number");
+
+        this.streamNumber = streamNumber;
+        return this;
+    }
+
+    @Override
+    public int payloadProtocolID() {
+        return ppid;
+    }
+
+    @Override
+    public MessageInfo payloadProtocolID(int ppid) {
+        this.ppid = ppid;
+        return this;
+    }
+
+    @Override
+    public boolean isComplete() {
+        return complete;
+    }
+
+    @Override
+    public MessageInfo complete(boolean complete) {
+        this.complete = complete;
+        return this;
+    }
+
+    @Override
+    public boolean isUnordered() {
+        return unordered;
+    }
+
+    @Override
+    public MessageInfo unordered(boolean unordered) {
+        this.unordered = unordered;
+        return this;
+    }
+
+    @Override
+    public long timeToLive() {
+        return timeToLive;
+    }
+
+    @Override
+    public MessageInfo timeToLive(long millis) {
+        timeToLive = millis;
+        return this;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder(super.toString());
+        sb.append( "[Address: ").append(address)
+          .append(", Association: ").append(association)
+          .append(", Assoc ID: ").append(assocId)
+          .append(", Bytes: ").append(bytes)
+          .append(", Stream Number: ").append(streamNumber)
+          .append(", Complete: ").append(complete)
+          .append(", isUnordered: ").append(unordered)
+          .append("]");
+        return sb.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/nio/ch/SctpStdSocketOption.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2009 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 com.sun.nio.sctp.SctpSocketOption;
+
+public class SctpStdSocketOption<T>
+    implements SctpSocketOption<T>
+{
+    /* for native mapping of int options */
+    public static final int SCTP_DISABLE_FRAGMENTS = 1;
+    public static final int SCTP_EXPLICIT_COMPLETE = 2;
+    public static final int SCTP_FRAGMENT_INTERLEAVE = 3;
+    public static final int SCTP_NODELAY = 4;
+    public static final int SO_SNDBUF = 5;
+    public static final int SO_RCVBUF = 6;
+    public static final int SO_LINGER = 7;
+
+    private final String name;
+    private final Class<T> type;
+
+    /* for native mapping of int options */
+    private int constValue;
+
+    public SctpStdSocketOption(String name, Class<T> type) {
+        this.name = name;
+        this.type = type;
+    }
+
+    public SctpStdSocketOption(String name, Class<T> type, int constValue) {
+        this.name = name;
+        this.type = type;
+        this.constValue = constValue;
+    }
+
+    @Override
+    public String name() {
+        return name;
+    }
+
+    @Override
+    public Class<T> type() {
+        return type;
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+
+    int constValue() {
+        return constValue;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpAssocChange.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2009 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 com.sun.nio.sctp.Association;
+import com.sun.nio.sctp.AssociationChangeNotification;
+
+/**
+ * An implementation of AssociationChangeNotification
+ */
+public class SctpAssocChange extends AssociationChangeNotification
+    implements SctpNotification
+{
+    /* static final ints so that they can be referenced from native */
+    private final static int SCTP_COMM_UP = 1;
+    private final static int SCTP_COMM_LOST = 2;
+    private final static int SCTP_RESTART = 3;
+    private final static int SCTP_SHUTDOWN = 4;
+    private final static int SCTP_CANT_START = 5;
+
+    private Association association;
+
+    /* assocId is used to lookup the association before the notification is
+     * returned to user code */
+    private int assocId;
+    private AssocChangeEvent event;
+    private int maxOutStreams;
+    private int maxInStreams;
+
+    /* Invoked from native */
+    private SctpAssocChange(int assocId,
+                            int intEvent,
+                            int maxOutStreams,
+                            int maxInStreams) {
+        switch (intEvent) {
+            case SCTP_COMM_UP :
+                this.event = AssocChangeEvent.COMM_UP;
+                break;
+            case SCTP_COMM_LOST :
+                this.event = AssocChangeEvent.COMM_LOST;
+                break;
+            case SCTP_RESTART :
+                this.event = AssocChangeEvent.RESTART;
+                break;
+            case SCTP_SHUTDOWN :
+                this.event = AssocChangeEvent.SHUTDOWN;
+                break;
+            case SCTP_CANT_START :
+                this.event = AssocChangeEvent.CANT_START;
+                break;
+            default :
+                throw new AssertionError(
+                      "Unknown Association Change Event type: " + intEvent);
+        }
+
+        this.assocId = assocId;
+        this.maxOutStreams = maxOutStreams;
+        this.maxInStreams = maxInStreams;
+    }
+
+    @Override
+    public int assocId() {
+        return assocId;
+    }
+
+    @Override
+    public void setAssociation(Association association) {
+        this.association = association;
+    }
+
+    @Override
+    public Association association() {
+        assert association != null;
+        return association;
+    }
+
+    @Override
+    public AssocChangeEvent event() {
+        return event;
+    }
+
+    int maxOutStreams() {
+        return maxOutStreams;
+    }
+
+    int maxInStreams() {
+        return maxInStreams;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(super.toString()).append(" [");
+        sb.append("Association:").append(association);
+        sb.append(", Event: ").append(event).append("]");
+        return sb.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpAssociationImpl.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2009 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 com.sun.nio.sctp.Association;
+
+/**
+ * An implementation of Association
+ */
+public class SctpAssociationImpl extends Association {
+    public SctpAssociationImpl(int associationID,
+                               int maxInStreams,
+                               int maxOutStreams) {
+        super(associationID, maxInStreams, maxOutStreams);
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer(super.toString());
+        return sb.append("[associationID:")
+                 .append(associationID())
+                 .append(", maxIn:")
+                 .append(maxInboundStreams())
+                 .append(", maxOut:")
+                 .append(maxOutboundStreams())
+                 .append("]")
+                 .toString();
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpChannelImpl.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,1073 @@
+/*
+ * Copyright 2009 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.InetAddress;
+import java.net.SocketAddress;
+import java.net.InetSocketAddress;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Set;
+import java.util.HashSet;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.ConnectionPendingException;
+import java.nio.channels.NoConnectionPendingException;
+import java.nio.channels.AlreadyBoundException;
+import java.nio.channels.AlreadyConnectedException;
+import java.nio.channels.NotYetBoundException;
+import java.nio.channels.NotYetConnectedException;
+import java.nio.channels.spi.SelectorProvider;
+import com.sun.nio.sctp.AbstractNotificationHandler;
+import com.sun.nio.sctp.Association;
+import com.sun.nio.sctp.AssociationChangeNotification;
+import com.sun.nio.sctp.HandlerResult;
+import com.sun.nio.sctp.IllegalReceiveException;
+import com.sun.nio.sctp.InvalidStreamException;
+import com.sun.nio.sctp.IllegalUnbindException;
+import com.sun.nio.sctp.MessageInfo;
+import com.sun.nio.sctp.NotificationHandler;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpSocketOption;
+import sun.nio.ch.NativeDispatcher;
+import sun.nio.ch.PollArrayWrapper;
+import sun.nio.ch.SelChImpl;
+import static com.sun.nio.sctp.SctpStandardSocketOption.*;
+import static sun.nio.ch.SctpResultContainer.SEND_FAILED;
+import static sun.nio.ch.SctpResultContainer.ASSOCIATION_CHANGED;
+import static sun.nio.ch.SctpResultContainer.PEER_ADDRESS_CHANGED;
+import static sun.nio.ch.SctpResultContainer.SHUTDOWN;
+
+/**
+ * An implementation of an SctpChannel
+ */
+public class SctpChannelImpl extends SctpChannel
+    implements SelChImpl
+{
+    /* Used to make native close and preClose calls */
+    private static NativeDispatcher nd;
+
+    private final FileDescriptor fd;
+
+    private final int fdVal;
+
+    /* IDs of native threads doing send and receivess, for signalling */
+    private volatile long receiverThread = 0;
+    private volatile long senderThread = 0;
+
+    /* Lock held by current receiving or connecting thread */
+    private final Object receiveLock = new Object();
+
+    /* Lock held by current sending or connecting thread */
+    private final Object sendLock = new Object();
+
+    private final ThreadLocal<Boolean> receiveInvoked =
+        new ThreadLocal<Boolean>() {
+             @Override protected Boolean initialValue() {
+                 return Boolean.FALSE;
+            }
+    };
+
+    /* Lock held by any thread that modifies the state fields declared below
+       DO NOT invoke a blocking I/O operation while holding this lock! */
+    private final Object stateLock = new Object();
+
+    private enum ChannelState {
+        UNINITIALIZED,
+        UNCONNECTED,
+        PENDING,
+        CONNECTED,
+        KILLPENDING,
+        KILLED,
+    }
+    /* -- The following fields are protected by stateLock -- */
+    private ChannelState state = ChannelState.UNINITIALIZED;
+
+    /* Binding; Once bound the port will remain constant. */
+    int port = -1;
+    private HashSet<InetSocketAddress> localAddresses = new HashSet<InetSocketAddress>();
+    /* Has the channel been bound to the wildcard address */
+    private boolean wildcard; /* false */
+    //private InetSocketAddress remoteAddress = null;
+
+    /* Input/Output open */
+    private boolean readyToConnect;
+
+    /* Shutdown */
+    private boolean isShutdown;
+
+    private Association association;
+
+    /* -- End of fields protected by stateLock -- */
+
+    private SctpResultContainer commUpResultContainer;  /* null */
+
+    /**
+     * Constructor for normal connecting sockets
+     */
+    public SctpChannelImpl(SelectorProvider provider) throws IOException {
+        //TODO: update provider remove public modifier
+        super(provider);
+        this.fd = SctpNet.socket(true);
+        this.fdVal = IOUtil.fdVal(fd);
+        this.state = ChannelState.UNCONNECTED;
+    }
+
+    /**
+     * Constructor for sockets obtained from server sockets
+     */
+    public SctpChannelImpl(SelectorProvider provider, FileDescriptor fd)
+         throws IOException {
+        super(provider);
+        this.fd = fd;
+        this.fdVal = IOUtil.fdVal(fd);
+        this.state = ChannelState.CONNECTED;
+        port = (Net.localAddress(fd)).getPort();
+
+        /* Receive COMM_UP */
+        ByteBuffer buf = Util.getTemporaryDirectBuffer(50);
+        try {
+            receive(buf, null, null, true);
+        } finally {
+            Util.releaseTemporaryDirectBuffer(buf);
+        }
+    }
+
+    /**
+     * Binds the channel's socket to a local address.
+     */
+    @Override
+    public SctpChannel bind(SocketAddress local) throws IOException {
+        synchronized (receiveLock) {
+            synchronized (sendLock) {
+                synchronized (stateLock) {
+                    ensureOpenAndUnconnected();
+                    if (isBound())
+                        throw new AlreadyBoundException();
+                    InetSocketAddress isa = (local == null) ?
+                        new InetSocketAddress(0) : Net.checkAddress(local);
+                    Net.bind(fd, isa.getAddress(), isa.getPort());
+                    InetSocketAddress boundIsa = Net.localAddress(fd);
+                    port = boundIsa.getPort();
+                    localAddresses.add(isa);
+                    if (isa.getAddress().isAnyLocalAddress())
+                        wildcard = true;
+                }
+            }
+        }
+        return this;
+    }
+
+    @Override
+    public SctpChannel bindAddress(InetAddress address)
+            throws IOException {
+        bindUnbindAddress(address, true);
+        localAddresses.add(new InetSocketAddress(address, port));
+        return this;
+    }
+
+    @Override
+    public SctpChannel unbindAddress(InetAddress address)
+            throws IOException {
+        bindUnbindAddress(address, false);
+        localAddresses.remove(new InetSocketAddress(address, port));
+        return this;
+    }
+
+    private SctpChannel bindUnbindAddress(InetAddress address, boolean add)
+            throws IOException {
+        if (address == null)
+            throw new IllegalArgumentException();
+
+        synchronized (receiveLock) {
+            synchronized (sendLock) {
+                synchronized (stateLock) {
+                    if (!isOpen())
+                        throw new ClosedChannelException();
+                    if (!isBound())
+                        throw new NotYetBoundException();
+                    if (wildcard)
+                        throw new IllegalStateException(
+                                "Cannot add or remove addresses from a channel that is bound to the wildcard address");
+                    if (address.isAnyLocalAddress())
+                        throw new IllegalArgumentException(
+                                "Cannot add or remove the wildcard address");
+                    if (add) {
+                        for (InetSocketAddress addr : localAddresses) {
+                            if (addr.getAddress().equals(address)) {
+                                throw new AlreadyBoundException();
+                            }
+                        }
+                    } else { /*removing */
+                        /* Verify that there is more than one address
+                         * and that address is already bound */
+                        if (localAddresses.size() <= 1)
+                            throw new IllegalUnbindException("Cannot remove address from a channel with only one address bound");
+                        boolean foundAddress = false;
+                        for (InetSocketAddress addr : localAddresses) {
+                            if (addr.getAddress().equals(address)) {
+                                foundAddress = true;
+                                break;
+                            }
+                        }
+                        if (!foundAddress )
+                            throw new IllegalUnbindException("Cannot remove address from a channel that is not bound to that address");
+                    }
+
+                    SctpNet.bindx(fdVal, new InetAddress[]{address}, port, add);
+
+                    /* Update our internal Set to reflect the addition/removal */
+                    if (add)
+                        localAddresses.add(new InetSocketAddress(address, port));
+                    else {
+                        for (InetSocketAddress addr : localAddresses) {
+                            if (addr.getAddress().equals(address)) {
+                                localAddresses.remove(addr);
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return this;
+    }
+
+    private boolean isBound() {
+        synchronized (stateLock) {
+            return port == -1 ? false : true;
+        }
+    }
+
+    private boolean isConnected() {
+        synchronized (stateLock) {
+            return (state == ChannelState.CONNECTED);
+        }
+    }
+
+    private void ensureOpenAndUnconnected() throws IOException {
+        synchronized (stateLock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+            if (isConnected())
+                throw new AlreadyConnectedException();
+            if (state == ChannelState.PENDING)
+                throw new ConnectionPendingException();
+        }
+    }
+
+    private boolean ensureReceiveOpen() throws ClosedChannelException {
+        synchronized (stateLock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+            if (!isConnected())
+                throw new NotYetConnectedException();
+            else
+                return true;
+        }
+    }
+
+    private void ensureSendOpen() throws ClosedChannelException {
+        synchronized (stateLock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+            if (isShutdown)
+                throw new ClosedChannelException();
+            if (!isConnected())
+                throw new NotYetConnectedException();
+        }
+    }
+
+    private void receiverCleanup() throws IOException {
+        synchronized (stateLock) {
+            receiverThread = 0;
+            if (state == ChannelState.KILLPENDING)
+                kill();
+        }
+    }
+
+    private void senderCleanup() throws IOException {
+        synchronized (stateLock) {
+            senderThread = 0;
+            if (state == ChannelState.KILLPENDING)
+                kill();
+        }
+    }
+
+    @Override
+    public Association association() throws ClosedChannelException {
+        synchronized (stateLock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+            if (!isConnected())
+                return null;
+
+            return association;
+        }
+    }
+
+    @Override
+    public boolean connect(SocketAddress endpoint) throws IOException {
+        synchronized (receiveLock) {
+            synchronized (sendLock) {
+                ensureOpenAndUnconnected();
+                InetSocketAddress isa = Net.checkAddress(endpoint);
+                SecurityManager sm = System.getSecurityManager();
+                if (sm != null)
+                    sm.checkConnect(isa.getAddress().getHostAddress(),
+                                    isa.getPort());
+                synchronized (blockingLock()) {
+                    int n = 0;
+                    try {
+                        try {
+                            begin();
+                            synchronized (stateLock) {
+                                if (!isOpen()) {
+                                    return false;
+                                }
+                                receiverThread = NativeThread.current();
+                            }
+                            for (;;) {
+                                InetAddress ia = isa.getAddress();
+                                if (ia.isAnyLocalAddress())
+                                    ia = InetAddress.getLocalHost();
+                                n = Net.connect(fd, ia, isa.getPort());
+                                if (  (n == IOStatus.INTERRUPTED)
+                                      && isOpen())
+                                    continue;
+                                break;
+                            }
+                        } finally {
+                            receiverCleanup();
+                            end((n > 0) || (n == IOStatus.UNAVAILABLE));
+                            assert IOStatus.check(n);
+                        }
+                    } catch (IOException x) {
+                        /* If an exception was thrown, close the channel after
+                         * invoking end() so as to avoid bogus
+                         * AsynchronousCloseExceptions */
+                        close();
+                        throw x;
+                    }
+
+                    if (n > 0) {
+                        synchronized (stateLock) {
+                            /* Connection succeeded */
+                            state = ChannelState.CONNECTED;
+                            if (!isBound()) {
+                                InetSocketAddress boundIsa =
+                                        Net.localAddress(fd);
+                                port = boundIsa.getPort();
+                            }
+
+                            /* Receive COMM_UP */
+                            ByteBuffer buf = Util.getTemporaryDirectBuffer(50);
+                            try {
+                                receive(buf, null, null, true);
+                            } finally {
+                                Util.releaseTemporaryDirectBuffer(buf);
+                            }
+                            return true;
+                        }
+                    } else  {
+                        synchronized (stateLock) {
+                            /* If nonblocking and no exception then connection
+                             * pending; disallow another invocation */
+                            if (!isBlocking())
+                                state = ChannelState.PENDING;
+                            else
+                                assert false;
+                        }
+                    }
+                }
+                return false;
+            }
+        }
+    }
+
+    @Override
+    public boolean connect(SocketAddress endpoint,
+                           int maxOutStreams,
+                           int maxInStreams)
+            throws IOException {
+        return setOption(SCTP_INIT_MAXSTREAMS, InitMaxStreams.
+                create(maxInStreams, maxOutStreams)).connect(endpoint);
+
+    }
+
+    @Override
+    public boolean isConnectionPending() {
+        synchronized (stateLock) {
+            return (state == ChannelState.PENDING);
+        }
+    }
+
+    @Override
+    public boolean finishConnect() throws IOException {
+        synchronized (receiveLock) {
+            synchronized (sendLock) {
+                synchronized (stateLock) {
+                    if (!isOpen())
+                        throw new ClosedChannelException();
+                    if (isConnected())
+                        return true;
+                    if (state != ChannelState.PENDING)
+                        throw new NoConnectionPendingException();
+                }
+                int n = 0;
+                try {
+                    try {
+                        begin();
+                        synchronized (blockingLock()) {
+                            synchronized (stateLock) {
+                                if (!isOpen()) {
+                                    return false;
+                                }
+                                receiverThread = NativeThread.current();
+                            }
+                            if (!isBlocking()) {
+                                for (;;) {
+                                    n = checkConnect(fd, false, readyToConnect);
+                                    if (  (n == IOStatus.INTERRUPTED)
+                                          && isOpen())
+                                        continue;
+                                    break;
+                                }
+                            } else {
+                                for (;;) {
+                                    n = checkConnect(fd, true, readyToConnect);
+                                    if (n == 0) {
+                                        // Loop in case of
+                                        // spurious notifications
+                                        continue;
+                                    }
+                                    if (  (n == IOStatus.INTERRUPTED)
+                                          && isOpen())
+                                        continue;
+                                    break;
+                                }
+                            }
+                        }
+                    } finally {
+                        synchronized (stateLock) {
+                            receiverThread = 0;
+                            if (state == ChannelState.KILLPENDING) {
+                                kill();
+                                /* poll()/getsockopt() does not report
+                                 * error (throws exception, with n = 0)
+                                 * on Linux platform after dup2 and
+                                 * signal-wakeup. Force n to 0 so the
+                                 * end() can throw appropriate exception */
+                                n = 0;
+                            }
+                        }
+                        end((n > 0) || (n == IOStatus.UNAVAILABLE));
+                        assert IOStatus.check(n);
+                    }
+                } catch (IOException x) {
+                    /* If an exception was thrown, close the channel after
+                     * invoking end() so as to avoid bogus
+                     * AsynchronousCloseExceptions */
+                    close();
+                    throw x;
+                }
+
+                if (n > 0) {
+                    synchronized (stateLock) {
+                        state = ChannelState.CONNECTED;
+                        if (!isBound()) {
+                            InetSocketAddress boundIsa =
+                                    Net.localAddress(fd);
+                            port = boundIsa.getPort();
+                        }
+
+                        /* Receive COMM_UP */
+                        ByteBuffer buf = Util.getTemporaryDirectBuffer(50);
+                        try {
+                            receive(buf, null, null, true);
+                        } finally {
+                            Util.releaseTemporaryDirectBuffer(buf);
+                        }
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    protected void implConfigureBlocking(boolean block) throws IOException {
+        IOUtil.configureBlocking(fd, block);
+    }
+
+    @Override
+    public void implCloseSelectableChannel() throws IOException {
+        synchronized (stateLock) {
+            nd.preClose(fd);
+
+            if (receiverThread != 0)
+                NativeThread.signal(receiverThread);
+
+            if (senderThread != 0)
+                NativeThread.signal(senderThread);
+
+            if (!isRegistered())
+                kill();
+        }
+    }
+
+    @Override
+    public FileDescriptor getFD() {
+        return fd;
+    }
+
+    @Override
+    public int getFDVal() {
+        return fdVal;
+    }
+
+    /**
+     * Translates native poll revent ops into a ready operation ops
+     */
+    private boolean translateReadyOps(int ops, int initialOps, SelectionKeyImpl sk) {
+        int intOps = sk.nioInterestOps();
+        int oldOps = sk.nioReadyOps();
+        int newOps = initialOps;
+
+        if ((ops & PollArrayWrapper.POLLNVAL) != 0) {
+            /* This should only happen if this channel is pre-closed while a
+             * selection operation is in progress
+             * ## Throw an error if this channel has not been pre-closed */
+            return false;
+        }
+
+        if ((ops & (PollArrayWrapper.POLLERR
+                    | PollArrayWrapper.POLLHUP)) != 0) {
+            newOps = intOps;
+            sk.nioReadyOps(newOps);
+            /* No need to poll again in checkConnect,
+             * the error will be detected there */
+            readyToConnect = true;
+            return (newOps & ~oldOps) != 0;
+        }
+
+        if (((ops & PollArrayWrapper.POLLIN) != 0) &&
+            ((intOps & SelectionKey.OP_READ) != 0) &&
+            isConnected())
+            newOps |= SelectionKey.OP_READ;
+
+        if (((ops & PollArrayWrapper.POLLCONN) != 0) &&
+            ((intOps & SelectionKey.OP_CONNECT) != 0) &&
+            ((state == ChannelState.UNCONNECTED) || (state == ChannelState.PENDING))) {
+            newOps |= SelectionKey.OP_CONNECT;
+            readyToConnect = true;
+        }
+
+        if (((ops & PollArrayWrapper.POLLOUT) != 0) &&
+            ((intOps & SelectionKey.OP_WRITE) != 0) &&
+            isConnected())
+            newOps |= SelectionKey.OP_WRITE;
+
+        sk.nioReadyOps(newOps);
+        return (newOps & ~oldOps) != 0;
+    }
+
+    @Override
+    public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
+        return translateReadyOps(ops, sk.nioReadyOps(), sk);
+    }
+
+    @Override
+    @SuppressWarnings("all")
+    public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
+        return translateReadyOps(ops, 0, sk);
+    }
+
+    @Override
+    public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) {
+        int newOps = 0;
+        if ((ops & SelectionKey.OP_READ) != 0)
+            newOps |= PollArrayWrapper.POLLIN;
+        if ((ops & SelectionKey.OP_WRITE) != 0)
+            newOps |= PollArrayWrapper.POLLOUT;
+        if ((ops & SelectionKey.OP_CONNECT) != 0)
+            newOps |= PollArrayWrapper.POLLCONN;
+        sk.selector.putEventOps(sk, newOps);
+    }
+
+    @Override
+    public void kill() throws IOException {
+        synchronized (stateLock) {
+            if (state == ChannelState.KILLED)
+                return;
+            if (state == ChannelState.UNINITIALIZED) {
+                state = ChannelState.KILLED;
+                return;
+            }
+            assert !isOpen() && !isRegistered();
+
+            /* Postpone the kill if there is a waiting reader
+             * or writer thread. */
+            if (receiverThread == 0 && senderThread == 0) {
+                nd.close(fd);
+                state = ChannelState.KILLED;
+            } else {
+                state = ChannelState.KILLPENDING;
+            }
+        }
+    }
+
+    @Override
+    public <T> SctpChannel setOption(SctpSocketOption<T> name, T value)
+            throws IOException {
+        if (name == null)
+            throw new NullPointerException();
+        if (!supportedOptions().contains(name))
+            throw new UnsupportedOperationException("'" + name + "' not supported");
+
+        synchronized (stateLock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+
+            SctpNet.setSocketOption(fdVal, name, value, 0 /*oneToOne*/);
+        }
+        return this;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> T getOption(SctpSocketOption<T> name) throws IOException {
+        if (name == null)
+            throw new NullPointerException();
+        if (!supportedOptions().contains(name))
+            throw new UnsupportedOperationException("'" + name + "' not supported");
+
+        synchronized (stateLock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+
+            return (T)SctpNet.getSocketOption(fdVal, name, 0 /*oneToOne*/);
+        }
+    }
+
+    private static class DefaultOptionsHolder {
+        static final Set<SctpSocketOption<?>> defaultOptions = defaultOptions();
+
+        private static Set<SctpSocketOption<?>> defaultOptions() {
+            HashSet<SctpSocketOption<?>> set = new HashSet<SctpSocketOption<?>>(10);
+            set.add(SCTP_DISABLE_FRAGMENTS);
+            set.add(SCTP_EXPLICIT_COMPLETE);
+            set.add(SCTP_FRAGMENT_INTERLEAVE);
+            set.add(SCTP_INIT_MAXSTREAMS);
+            set.add(SCTP_NODELAY);
+            set.add(SCTP_PRIMARY_ADDR);
+            set.add(SCTP_SET_PEER_PRIMARY_ADDR);
+            set.add(SO_SNDBUF);
+            set.add(SO_RCVBUF);
+            set.add(SO_LINGER);
+            return Collections.unmodifiableSet(set);
+        }
+    }
+
+    @Override
+    public final Set<SctpSocketOption<?>> supportedOptions() {
+        return DefaultOptionsHolder.defaultOptions;
+    }
+
+    @Override
+    public <T> MessageInfo receive(ByteBuffer buffer,
+                                   T attachment,
+                                   NotificationHandler<T> handler)
+            throws IOException {
+        return receive(buffer, attachment, handler, false);
+    }
+
+    private <T> MessageInfo receive(ByteBuffer buffer,
+                                    T attachment,
+                                    NotificationHandler<T> handler,
+                                    boolean fromConnect)
+            throws IOException {
+        if (buffer == null)
+            throw new IllegalArgumentException("buffer cannot be null");
+
+        if (buffer.isReadOnly())
+            throw new IllegalArgumentException("Read-only buffer");
+
+        if (receiveInvoked.get())
+            throw new IllegalReceiveException(
+                    "cannot invoke receive from handler");
+        receiveInvoked.set(Boolean.TRUE);
+
+        try {
+            SctpResultContainer resultContainer = new SctpResultContainer();
+            do {
+                resultContainer.clear();
+                synchronized (receiveLock) {
+                    if (!ensureReceiveOpen())
+                        return null;
+
+                    if (commUpResultContainer != null) {
+                        resultContainer = commUpResultContainer;
+                        commUpResultContainer = null;
+                        continue;
+                    }
+
+                    int n = 0;
+                    try {
+                        begin();
+
+                        synchronized (stateLock) {
+                            if(!isOpen())
+                                return null;
+                            receiverThread = NativeThread.current();
+                        }
+
+                        do {
+                            n = receive(fdVal, buffer, resultContainer);
+                        } while ((n == IOStatus.INTERRUPTED) && isOpen());
+                    } finally {
+                        receiverCleanup();
+                        end((n > 0) || (n == IOStatus.UNAVAILABLE));
+                        assert IOStatus.check(n);
+                    }
+
+                    if (!resultContainer.isNotification()) {
+                        /* message or nothing */
+                        if (resultContainer.hasSomething()) {
+                            /* Set the association before returning */
+                            SctpMessageInfoImpl info =
+                                    resultContainer.getMessageInfo();
+                            synchronized (stateLock) {
+                                assert association != null;
+                                info.setAssociation(association);
+                            }
+                            return info;
+                        } else
+                            /* Non-blocking may return null if nothing available*/
+                            return null;
+                    } else { /* notification */
+                        synchronized (stateLock) {
+                            handleNotificationInternal(
+                                    resultContainer);
+                        }
+                    }
+
+                    if (fromConnect)  {
+                        /* If we reach here, then it was connect that invoked
+                         * receive an received the COMM_UP. Save it and allow
+                         * the user handler to process it upon next receive. */
+                        commUpResultContainer = resultContainer;
+                        return null;
+                    }
+                }  /* receiveLock */
+            } while (handler == null ? true :
+                (invokeNotificationHandler(resultContainer, handler, attachment)
+                 == HandlerResult.CONTINUE));
+
+            return null;
+        } finally {
+            receiveInvoked.set(Boolean.FALSE);
+        }
+    }
+
+    private int receive(int fd,
+                        ByteBuffer dst,
+                        SctpResultContainer resultContainer)
+            throws IOException {
+        int pos = dst.position();
+        int lim = dst.limit();
+        assert (pos <= lim);
+        int rem = (pos <= lim ? lim - pos : 0);
+        if (dst instanceof DirectBuffer && rem > 0)
+            return receiveIntoNativeBuffer(fd, resultContainer, dst, rem, pos);
+
+        /* Substitute a native buffer */
+        int newSize = Math.max(rem, 1);
+        ByteBuffer bb = Util.getTemporaryDirectBuffer(newSize);
+        try {
+            int n = receiveIntoNativeBuffer(fd, resultContainer, bb, newSize, 0);
+            bb.flip();
+            if (n > 0 && rem > 0)
+                dst.put(bb);
+            return n;
+        } finally {
+            Util.releaseTemporaryDirectBuffer(bb);
+        }
+    }
+
+    private int receiveIntoNativeBuffer(int fd,
+                                        SctpResultContainer resultContainer,
+                                        ByteBuffer bb,
+                                        int rem,
+                                        int pos)
+        throws IOException
+    {
+        int n = receive0(fd, resultContainer, ((DirectBuffer)bb).address() + pos, rem);
+
+        if (n > 0)
+            bb.position(pos + n);
+        return n;
+    }
+
+    private InternalNotificationHandler<?> internalNotificationHandler =
+            new InternalNotificationHandler();
+
+    private void handleNotificationInternal(SctpResultContainer resultContainer)
+    {
+        invokeNotificationHandler(resultContainer,
+                internalNotificationHandler, null);
+    }
+
+    private class InternalNotificationHandler<T>
+            extends AbstractNotificationHandler<T>
+    {
+        @Override
+        public HandlerResult handleNotification(
+                AssociationChangeNotification not, T unused) {
+            if (not.event().equals(
+                    AssociationChangeNotification.AssocChangeEvent.COMM_UP)) {
+                assert association == null;
+                SctpAssocChange sac = (SctpAssocChange) not;
+                association = new SctpAssociationImpl
+                       (sac.assocId(), sac.maxInStreams(), sac.maxOutStreams());
+            }
+            return HandlerResult.CONTINUE;
+        }
+    }
+
+    private <T> HandlerResult invokeNotificationHandler
+                                 (SctpResultContainer resultContainer,
+                                  NotificationHandler<T> handler,
+                                  T attachment) {
+        SctpNotification notification = resultContainer.notification();
+        synchronized (stateLock) {
+            notification.setAssociation(association);
+        }
+
+        if (!(handler instanceof AbstractNotificationHandler)) {
+            return handler.handleNotification(notification, attachment);
+        }
+
+        /* AbstractNotificationHandler */
+        AbstractNotificationHandler absHandler =
+                (AbstractNotificationHandler)handler;
+        switch(resultContainer.type()) {
+            case ASSOCIATION_CHANGED :
+                return absHandler.handleNotification(
+                        resultContainer.getAssociationChanged(), attachment);
+            case PEER_ADDRESS_CHANGED :
+                return absHandler.handleNotification(
+                        resultContainer.getPeerAddressChanged(), attachment);
+            case SEND_FAILED :
+                return absHandler.handleNotification(
+                        resultContainer.getSendFailed(), attachment);
+            case SHUTDOWN :
+                return absHandler.handleNotification(
+                        resultContainer.getShutdown(), attachment);
+            default :
+                /* implementation specific handlers */
+                return absHandler.handleNotification(
+                        resultContainer.notification(), attachment);
+        }
+    }
+
+    private void checkAssociation(Association sendAssociation) {
+        synchronized (stateLock) {
+            if (sendAssociation != null && !sendAssociation.equals(association)) {
+                throw new IllegalArgumentException(
+                        "Cannot send to another association");
+            }
+        }
+    }
+
+    private void checkStreamNumber(int streamNumber) {
+        synchronized (stateLock) {
+            if (association != null) {
+                if (streamNumber < 0 ||
+                      streamNumber >= association.maxOutboundStreams())
+                    throw new InvalidStreamException();
+            }
+        }
+    }
+
+    /* TODO: Add support for ttl and isComplete to both 121 12M
+     *       SCTP_EOR not yet supported on reference platforms
+     *       TTL support limited...
+     */
+    @Override
+    public int send(ByteBuffer buffer, MessageInfo messageInfo)
+            throws IOException {
+        if (buffer == null)
+            throw new IllegalArgumentException("buffer cannot be null");
+
+        if (messageInfo == null)
+            throw new IllegalArgumentException("messageInfo cannot be null");
+
+        checkAssociation(messageInfo.association());
+        checkStreamNumber(messageInfo.streamNumber());
+
+        synchronized (sendLock) {
+            ensureSendOpen();
+
+            int n = 0;
+            try {
+                begin();
+
+                synchronized (stateLock) {
+                    if(!isOpen())
+                        return 0;
+                    senderThread = NativeThread.current();
+                }
+
+                do {
+                    n = send(fdVal, buffer, messageInfo);
+                } while ((n == IOStatus.INTERRUPTED) && isOpen());
+
+                return IOStatus.normalize(n);
+            } finally {
+                senderCleanup();
+                end((n > 0) || (n == IOStatus.UNAVAILABLE));
+                assert IOStatus.check(n);
+            }
+        }
+    }
+
+    private int send(int fd, ByteBuffer src, MessageInfo messageInfo)
+            throws IOException {
+        int streamNumber = messageInfo.streamNumber();
+        SocketAddress target = messageInfo.address();
+        boolean unordered = messageInfo.isUnordered();
+        int ppid = messageInfo.payloadProtocolID();
+        int pos = src.position();
+        int lim = src.limit();
+
+        assert (pos <= lim && streamNumber > 0);
+        int rem = (pos <= lim ? lim - pos : 0);
+
+        if (src instanceof DirectBuffer)
+            return sendFromNativeBuffer(fd, src, rem, pos, target, streamNumber,
+                    unordered, ppid);
+
+        /* Substitute a native buffer */
+        ByteBuffer bb = Util.getTemporaryDirectBuffer(rem);
+        try {
+            bb.put(src);
+            bb.flip();
+            /* Do not update src until we see how many bytes were written */
+            src.position(pos);
+
+            int n = sendFromNativeBuffer(fd, bb, rem, pos, target, streamNumber,
+                    unordered, ppid);
+            if (n > 0) {
+                /* now update src */
+                src.position(pos + n);
+            }
+            return n;
+        } finally {
+            Util.releaseTemporaryDirectBuffer(bb);
+        }
+    }
+
+    private int sendFromNativeBuffer(int fd,
+                                     ByteBuffer bb,
+                                     int rem,
+                                     int pos,
+                                     SocketAddress target,
+                                     int streamNumber,
+                                     boolean unordered,
+                                     int ppid)
+            throws IOException {
+        int written = send0(fd, ((DirectBuffer)bb).address() + pos,
+                            rem, target, -1 /*121*/, streamNumber, unordered, ppid);
+        if (written > 0)
+            bb.position(pos + written);
+        return written;
+    }
+
+    @Override
+    public SctpChannel shutdown() throws IOException {
+        synchronized(stateLock) {
+            if (isShutdown)
+                return this;
+
+            ensureSendOpen();
+            SctpNet.shutdown(fdVal, -1);
+            if (senderThread != 0)
+                NativeThread.signal(senderThread);
+            isShutdown = true;
+        }
+        return this;
+    }
+
+    @Override
+    public Set<SocketAddress> getAllLocalAddresses()
+            throws IOException {
+        synchronized (stateLock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+            if (!isBound())
+                return Collections.EMPTY_SET;
+
+            return SctpNet.getLocalAddresses(fdVal);
+        }
+    }
+
+    @Override
+    public Set<SocketAddress> getRemoteAddresses()
+            throws IOException {
+        synchronized (stateLock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+            if (!isConnected())
+                return Collections.EMPTY_SET;
+
+            return SctpNet.getRemoteAddresses(fdVal, 0/*unused*/);
+        }
+    }
+
+    /* Native */
+    private static native void initIDs();
+
+    static native int receive0(int fd, SctpResultContainer resultContainer,
+            long address, int length) throws IOException;
+
+    static native int send0(int fd, long address, int length,
+            SocketAddress target, int assocId, int streamNumber,
+            boolean unordered, int ppid) throws IOException;
+
+    private static native int checkConnect(FileDescriptor fd, boolean block,
+            boolean ready) throws IOException;
+
+    static {
+        Util.load();   /* loads nio & net native libraries */
+        java.security.AccessController.doPrivileged(
+                new sun.security.action.LoadLibraryAction("sctp"));
+        initIDs();
+        nd = new SctpSocketDispatcher();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpMultiChannelImpl.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,959 @@
+/*
+ * Copyright 2009 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.InetAddress;
+import java.net.SocketAddress;
+import java.net.InetSocketAddress;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.HashMap;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.AlreadyBoundException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.NotYetBoundException;
+import java.nio.channels.spi.SelectorProvider;
+import com.sun.nio.sctp.AbstractNotificationHandler;
+import com.sun.nio.sctp.Association;
+import com.sun.nio.sctp.AssociationChangeNotification;
+import com.sun.nio.sctp.HandlerResult;
+import com.sun.nio.sctp.IllegalReceiveException;
+import com.sun.nio.sctp.InvalidStreamException;
+import com.sun.nio.sctp.IllegalUnbindException;
+import com.sun.nio.sctp.NotificationHandler;
+import com.sun.nio.sctp.MessageInfo;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpMultiChannel;
+import com.sun.nio.sctp.SctpSocketOption;
+import static com.sun.nio.sctp.SctpStandardSocketOption.*;
+import static sun.nio.ch.SctpResultContainer.*;
+
+/**
+ * An implementation of SctpMultiChannel
+ */
+public class SctpMultiChannelImpl extends SctpMultiChannel
+    implements SelChImpl
+{
+    /* Used to make native close and preClose calls */
+    private static NativeDispatcher nd;
+
+    private final FileDescriptor fd;
+
+    private final int fdVal;
+
+    /* IDs of native threads doing send and receives, for signalling */
+    private volatile long receiverThread = 0;
+    private volatile long senderThread = 0;
+
+    /* Lock held by current receiving thread */
+    private final Object receiveLock = new Object();
+
+    /* Lock held by current sending thread */
+    private final Object sendLock = new Object();
+
+    /* Lock held by any thread that modifies the state fields declared below
+     * DO NOT invoke a blocking I/O operation while holding this lock! */
+    private final Object stateLock = new Object();
+
+    private enum ChannelState {
+        UNINITIALIZED,
+        KILLPENDING,
+        KILLED,
+    }
+
+    /* -- The following fields are protected by stateLock -- */
+    private ChannelState state = ChannelState.UNINITIALIZED;
+
+    /* Binding: Once bound the port will remain constant. */
+    int port = -1;
+    private HashSet<InetSocketAddress> localAddresses = new HashSet<InetSocketAddress>();
+    /* Has the channel been bound to the wildcard address */
+    private boolean wildcard; /* false */
+
+    /* Keeps a map of addresses to association, and visa versa */
+    private HashMap<SocketAddress, Association> addressMap =
+                         new HashMap<SocketAddress, Association>();
+    private HashMap<Association, Set<SocketAddress>> associationMap =
+                         new HashMap<Association, Set<SocketAddress>>();
+
+    /* -- End of fields protected by stateLock -- */
+
+    /* If an association has been shutdown mark it for removal after
+     * the user handler has been invoked */
+    private final ThreadLocal<Association> associationToRemove =
+        new ThreadLocal<Association>() {
+             @Override protected Association initialValue() {
+                 return null;
+            }
+    };
+
+    /* A notification handler cannot invoke receive */
+    private final ThreadLocal<Boolean> receiveInvoked =
+        new ThreadLocal<Boolean>() {
+             @Override protected Boolean initialValue() {
+                 return Boolean.FALSE;
+            }
+    };
+
+    public SctpMultiChannelImpl(SelectorProvider provider)
+            throws IOException {
+        //TODO: update provider, remove public modifier
+        super(provider);
+        this.fd = SctpNet.socket(false /*one-to-many*/);
+        this.fdVal = IOUtil.fdVal(fd);
+    }
+
+    @Override
+    public SctpMultiChannel bind(SocketAddress local, int backlog)
+            throws IOException {
+        synchronized (receiveLock) {
+            synchronized (sendLock) {
+                synchronized (stateLock) {
+                    ensureOpen();
+                    if (isBound())
+                        throw new AlreadyBoundException();
+                    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());
+
+                    InetSocketAddress boundIsa = Net.localAddress(fd);
+                    port = boundIsa.getPort();
+                    localAddresses.add(isa);
+                    if (isa.getAddress().isAnyLocalAddress())
+                        wildcard = true;
+
+                    Net.listen(fd, backlog < 1 ? 50 : backlog);
+                }
+            }
+        }
+        return this;
+    }
+
+    @Override
+    public SctpMultiChannel bindAddress(InetAddress address)
+            throws IOException {
+        return bindUnbindAddress(address, true);
+    }
+
+    @Override
+    public SctpMultiChannel unbindAddress(InetAddress address)
+            throws IOException {
+        return bindUnbindAddress(address, false);
+    }
+
+    private SctpMultiChannel bindUnbindAddress(InetAddress address,
+                                               boolean add)
+            throws IOException {
+        if (address == null)
+            throw new IllegalArgumentException();
+
+        synchronized (receiveLock) {
+            synchronized (sendLock) {
+                synchronized (stateLock) {
+                    if (!isOpen())
+                        throw new ClosedChannelException();
+                    if (!isBound())
+                        throw new NotYetBoundException();
+                    if (wildcard)
+                        throw new IllegalStateException(
+                                "Cannot add or remove addresses from a channel that is bound to the wildcard address");
+                    if (address.isAnyLocalAddress())
+                        throw new IllegalArgumentException(
+                                "Cannot add or remove the wildcard address");
+                    if (add) {
+                        for (InetSocketAddress addr : localAddresses) {
+                            if (addr.getAddress().equals(address)) {
+                                throw new AlreadyBoundException();
+                            }
+                        }
+                    } else { /*removing */
+                        /* Verify that there is more than one address
+                         * and that address is already bound */
+                        if (localAddresses.size() <= 1)
+                            throw new IllegalUnbindException("Cannot remove address from a channel with only one address bound");
+                        boolean foundAddress = false;
+                        for (InetSocketAddress addr : localAddresses) {
+                            if (addr.getAddress().equals(address)) {
+                                foundAddress = true;
+                                break;
+                            }
+                        }
+                        if (!foundAddress )
+                            throw new IllegalUnbindException("Cannot remove address from a channel that is not bound to that address");
+                    }
+
+                    SctpNet.bindx(fdVal, new InetAddress[]{address}, port, add);
+
+                    /* Update our internal Set to reflect the addition/removal */
+                    if (add)
+                        localAddresses.add(new InetSocketAddress(address, port));
+                    else {
+                        for (InetSocketAddress addr : localAddresses) {
+                            if (addr.getAddress().equals(address)) {
+                                localAddresses.remove(addr);
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return this;
+    }
+
+    @Override
+    public Set<Association> associations()
+            throws ClosedChannelException, NotYetBoundException {
+        synchronized (stateLock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+            if (!isBound())
+                throw new NotYetBoundException();
+
+            return Collections.unmodifiableSet(associationMap.keySet());
+        }
+    }
+
+    private boolean isBound() {
+        synchronized (stateLock) {
+            return port == -1 ? false : true;
+        }
+    }
+
+    private void ensureOpen() throws IOException {
+        synchronized (stateLock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+        }
+    }
+
+    private void receiverCleanup() throws IOException {
+        synchronized (stateLock) {
+            receiverThread = 0;
+            if (state == ChannelState.KILLPENDING)
+                kill();
+        }
+    }
+
+    private void senderCleanup() throws IOException {
+        synchronized (stateLock) {
+            senderThread = 0;
+            if (state == ChannelState.KILLPENDING)
+                kill();
+        }
+    }
+
+    @Override
+    protected void implConfigureBlocking(boolean block) throws IOException {
+        IOUtil.configureBlocking(fd, block);
+    }
+
+    @Override
+    public void implCloseSelectableChannel() throws IOException {
+        synchronized (stateLock) {
+            nd.preClose(fd);
+
+            if (receiverThread != 0)
+                NativeThread.signal(receiverThread);
+
+            if (senderThread != 0)
+                NativeThread.signal(senderThread);
+
+            if (!isRegistered())
+                kill();
+        }
+    }
+
+    @Override
+    public FileDescriptor getFD() {
+        return fd;
+    }
+
+    @Override
+    public int getFDVal() {
+        return fdVal;
+    }
+
+    /**
+     * Translates native poll revent ops into a ready operation ops
+     */
+    private boolean translateReadyOps(int ops, int initialOps,
+                                      SelectionKeyImpl sk) {
+        int intOps = sk.nioInterestOps();
+        int oldOps = sk.nioReadyOps();
+        int newOps = initialOps;
+
+        if ((ops & PollArrayWrapper.POLLNVAL) != 0) {
+            /* This should only happen if this channel is pre-closed while a
+             * selection operation is in progress
+             * ## Throw an error if this channel has not been pre-closed */
+            return false;
+        }
+
+        if ((ops & (PollArrayWrapper.POLLERR
+                    | PollArrayWrapper.POLLHUP)) != 0) {
+            newOps = intOps;
+            sk.nioReadyOps(newOps);
+            return (newOps & ~oldOps) != 0;
+        }
+
+        if (((ops & PollArrayWrapper.POLLIN) != 0) &&
+            ((intOps & SelectionKey.OP_READ) != 0))
+            newOps |= SelectionKey.OP_READ;
+
+        if (((ops & PollArrayWrapper.POLLOUT) != 0) &&
+            ((intOps & SelectionKey.OP_WRITE) != 0))
+            newOps |= SelectionKey.OP_WRITE;
+
+        sk.nioReadyOps(newOps);
+        return (newOps & ~oldOps) != 0;
+    }
+
+    @Override
+    public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
+        return translateReadyOps(ops, sk.nioReadyOps(), sk);
+    }
+
+    @Override
+    public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
+        return translateReadyOps(ops, 0, sk);
+    }
+
+    @Override
+    public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) {
+        int newOps = 0;
+        if ((ops & SelectionKey.OP_READ) != 0)
+            newOps |= PollArrayWrapper.POLLIN;
+        if ((ops & SelectionKey.OP_WRITE) != 0)
+            newOps |= PollArrayWrapper.POLLOUT;
+        sk.selector.putEventOps(sk, newOps);
+    }
+
+    @Override
+    public void kill() throws IOException {
+        synchronized (stateLock) {
+            if (state == ChannelState.KILLED)
+                return;
+            if (state == ChannelState.UNINITIALIZED) {
+                state = ChannelState.KILLED;
+                return;
+            }
+            assert !isOpen() && !isRegistered();
+
+            /* Postpone the kill if there is a thread sending or receiving. */
+            if (receiverThread == 0 && senderThread == 0) {
+                nd.close(fd);
+                state = ChannelState.KILLED;
+            } else {
+                state = ChannelState.KILLPENDING;
+            }
+        }
+    }
+
+    @Override
+    public <T> SctpMultiChannel setOption(SctpSocketOption<T> name,
+                                          T value,
+                                          Association association)
+            throws IOException {
+        if (name == null)
+            throw new NullPointerException();
+        if (!(supportedOptions().contains(name)))
+            throw new UnsupportedOperationException("'" + name + "' not supported");
+
+        synchronized (stateLock) {
+            if (association != null && (name.equals(SCTP_PRIMARY_ADDR) ||
+                    name.equals(SCTP_SET_PEER_PRIMARY_ADDR))) {
+                checkAssociation(association);
+            }
+            if (!isOpen())
+                throw new ClosedChannelException();
+
+            SctpNet.setSocketOption(fdVal, name, value,
+                    association.associationID());
+        }
+        return this;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> T getOption(SctpSocketOption<T> name, Association association)
+            throws IOException {
+        if (name == null)
+            throw new NullPointerException();
+        if (!supportedOptions().contains(name))
+            throw new UnsupportedOperationException("'" + name + "' not supported");
+
+        synchronized (stateLock) {
+            checkAssociation(association);
+            if (!isOpen())
+                throw new ClosedChannelException();
+
+            return (T)SctpNet.getSocketOption(fdVal, name,
+                    association.associationID());
+        }
+    }
+
+    private static class DefaultOptionsHolder {
+        static final Set<SctpSocketOption<?>> defaultOptions = defaultOptions();
+
+        private static Set<SctpSocketOption<?>> defaultOptions() {
+            HashSet<SctpSocketOption<?>> set = new HashSet<SctpSocketOption<?>>(10);
+            set.add(SCTP_DISABLE_FRAGMENTS);
+            set.add(SCTP_EXPLICIT_COMPLETE);
+            set.add(SCTP_FRAGMENT_INTERLEAVE);
+            set.add(SCTP_INIT_MAXSTREAMS);
+            set.add(SCTP_NODELAY);
+            set.add(SCTP_PRIMARY_ADDR);
+            set.add(SCTP_SET_PEER_PRIMARY_ADDR);
+            set.add(SO_SNDBUF);
+            set.add(SO_RCVBUF);
+            set.add(SO_LINGER);
+            return Collections.unmodifiableSet(set);
+        }
+    }
+
+    @Override
+    public final Set<SctpSocketOption<?>> supportedOptions() {
+        return DefaultOptionsHolder.defaultOptions;
+    }
+
+    @Override
+    public <T> MessageInfo receive(ByteBuffer buffer,
+                                   T attachment,
+                                   NotificationHandler<T> handler)
+            throws IOException {
+        if (buffer == null)
+            throw new IllegalArgumentException("buffer cannot be null");
+
+        if (buffer.isReadOnly())
+            throw new IllegalArgumentException("Read-only buffer");
+
+        if (receiveInvoked.get())
+            throw new IllegalReceiveException(
+                    "cannot invoke receive from handler");
+        receiveInvoked.set(Boolean.TRUE);
+
+        try {
+            SctpResultContainer resultContainer = new SctpResultContainer();
+            do {
+                resultContainer.clear();
+                synchronized (receiveLock) {
+                    ensureOpen();
+                    if (!isBound())
+                        throw new NotYetBoundException();
+
+                    int n = 0;
+                    try {
+                        begin();
+
+                        synchronized (stateLock) {
+                            if(!isOpen())
+                                return null;
+                            receiverThread = NativeThread.current();
+                        }
+
+                        do {
+                            n = receive(fdVal, buffer, resultContainer);
+                        } while ((n == IOStatus.INTERRUPTED) && isOpen());
+
+                    } finally {
+                        receiverCleanup();
+                        end((n > 0) || (n == IOStatus.UNAVAILABLE));
+                        assert IOStatus.check(n);
+                    }
+
+                    if (!resultContainer.isNotification()) {
+                        /* message or nothing */
+                        if (resultContainer.hasSomething()) {
+                            /* Set the association before returning */
+                            SctpMessageInfoImpl info =
+                                    resultContainer.getMessageInfo();
+                            info.setAssociation(lookupAssociation(info.
+                                    associationID()));
+                            SecurityManager sm = System.getSecurityManager();
+                            if (sm != null) {
+                                InetSocketAddress isa  = (InetSocketAddress)info.address();
+                                if (!addressMap.containsKey(isa)) {
+                                    /* must be a new association */
+                                    try {
+                                        sm.checkAccept(isa.getAddress().getHostAddress(),
+                                                       isa.getPort());
+                                    } catch (SecurityException se) {
+                                        buffer.clear();
+                                        throw se;
+                                    }
+                                }
+                            }
+
+                            assert info.association() != null;
+                            return info;
+                        } else  {
+                          /* Non-blocking may return null if nothing available*/
+                            return null;
+                        }
+                    } else { /* notification */
+                        synchronized (stateLock) {
+                            handleNotificationInternal(
+                                    resultContainer);
+                        }
+                    }
+                } /* receiveLock */
+            } while (handler == null ? true :
+                (invokeNotificationHandler(resultContainer, handler, attachment)
+                 == HandlerResult.CONTINUE));
+        } finally {
+            receiveInvoked.set(Boolean.FALSE);
+        }
+
+        return null;
+    }
+
+    private int receive(int fd,
+                        ByteBuffer dst,
+                        SctpResultContainer resultContainer)
+            throws IOException {
+        int pos = dst.position();
+        int lim = dst.limit();
+        assert (pos <= lim);
+        int rem = (pos <= lim ? lim - pos : 0);
+        if (dst instanceof DirectBuffer && rem > 0)
+            return receiveIntoNativeBuffer(fd, resultContainer, dst, rem, pos);
+
+        /* Substitute a native buffer. */
+        int newSize = Math.max(rem, 1);
+        ByteBuffer bb = Util.getTemporaryDirectBuffer(newSize);
+        try {
+            int n = receiveIntoNativeBuffer(fd, resultContainer, bb, newSize, 0);
+            bb.flip();
+            if (n > 0 && rem > 0)
+                dst.put(bb);
+            return n;
+        } finally {
+            Util.releaseTemporaryDirectBuffer(bb);
+        }
+    }
+
+    private int receiveIntoNativeBuffer(int fd,
+                                        SctpResultContainer resultContainer,
+                                        ByteBuffer bb,
+                                        int rem,
+                                        int pos)
+            throws IOException {
+        int n = receive0(fd, resultContainer, ((DirectBuffer)bb).address() + pos, rem);
+        if (n > 0)
+            bb.position(pos + n);
+        return n;
+    }
+
+    private InternalNotificationHandler internalNotificationHandler =
+            new InternalNotificationHandler();
+
+    private void handleNotificationInternal(SctpResultContainer resultContainer)
+    {
+        invokeNotificationHandler(resultContainer,
+                internalNotificationHandler, null);
+    }
+
+    private class InternalNotificationHandler<T>
+            extends AbstractNotificationHandler<T>
+    {
+        @Override
+        public HandlerResult handleNotification(
+                AssociationChangeNotification not, T unused) {
+            SctpAssocChange sac = (SctpAssocChange) not;
+
+            /* Update map to reflect change in association */
+            switch (not.event()) {
+                case COMM_UP :
+                    Association newAssociation = new SctpAssociationImpl
+                       (sac.assocId(), sac.maxInStreams(), sac.maxOutStreams());
+                    addAssociation(newAssociation);
+                    break;
+                case SHUTDOWN :
+                case COMM_LOST :
+                //case RESTART: ???
+                    /* mark association for removal after user handler invoked*/
+                    associationToRemove.set(lookupAssociation(sac.assocId()));
+            }
+            return HandlerResult.CONTINUE;
+        }
+    }
+
+    private <T> HandlerResult invokeNotificationHandler(
+                                   SctpResultContainer resultContainer,
+                                   NotificationHandler<T> handler,
+                                   T attachment) {
+        HandlerResult result;
+        SctpNotification notification = resultContainer.notification();
+        notification.setAssociation(lookupAssociation(notification.assocId()));
+
+        if (!(handler instanceof AbstractNotificationHandler)) {
+            result = handler.handleNotification(notification, attachment);
+        } else { /* AbstractNotificationHandler */
+            AbstractNotificationHandler absHandler =
+                    (AbstractNotificationHandler)handler;
+            switch(resultContainer.type()) {
+                case ASSOCIATION_CHANGED :
+                    result = absHandler.handleNotification(
+                            resultContainer.getAssociationChanged(), attachment);
+                case PEER_ADDRESS_CHANGED :
+                    result = absHandler.handleNotification(
+                            resultContainer.getPeerAddressChanged(), attachment);
+                case SEND_FAILED :
+                    result = absHandler.handleNotification(
+                            resultContainer.getSendFailed(), attachment);
+                case SHUTDOWN :
+                    result =  absHandler.handleNotification(
+                            resultContainer.getShutdown(), attachment);
+                default :
+                    /* implementation specific handlers */
+                    result =  absHandler.handleNotification(
+                            resultContainer.notification(), attachment);
+            }
+        }
+
+        if (!(handler instanceof InternalNotificationHandler)) {
+            /* Only remove associations after user handler
+             * has finished with them */
+            Association assoc = associationToRemove.get();
+            if (assoc != null) {
+                removeAssociation(assoc);
+                associationToRemove.set(null);
+            }
+
+        }
+
+        return result;
+    }
+
+    private Association lookupAssociation(int assocId) {
+        /* Lookup the association in our internal map */
+        synchronized (stateLock) {
+            Set<Association> assocs = associationMap.keySet();
+            for (Association a : assocs) {
+                if (a.associationID() == assocId) {
+                    return a;
+                }
+            }
+        }
+        return null;
+    }
+
+    private void addAssociation(Association association) {
+        synchronized (stateLock) {
+            int assocId = association.associationID();
+            Set<SocketAddress> addresses = null;
+
+            try {
+                addresses = SctpNet.getRemoteAddresses(fdVal, assocId);
+            } catch (IOException unused) {
+                /* OK, determining connected addresses may not be possible
+                 * shutdown, connection lost, etc */
+            }
+
+            associationMap.put(association, addresses);
+            if (addresses != null) {
+                for (SocketAddress addr : addresses)
+                    addressMap.put(addr, association);
+            }
+        }
+    }
+
+    private void removeAssociation(Association association) {
+        synchronized (stateLock) {
+            int assocId = association.associationID();
+            Set<SocketAddress> addresses = null;
+
+            try {
+                addresses = SctpNet.getRemoteAddresses(fdVal, assocId);
+            } catch (IOException unused) {
+                /* OK, determining connected addresses may not be possible
+                 * shutdown, connection lost, etc */
+            }
+
+            Set<Association> assocs = associationMap.keySet();
+            for (Association a : assocs) {
+                if (a.associationID() == assocId) {
+                    associationMap.remove(a);
+                    break;
+                }
+            }
+            if (addresses != null) {
+                for (SocketAddress addr : addresses)
+                    addressMap.remove(addr);
+            } else {
+                /* We cannot determine the connected addresses */
+                Set<java.util.Map.Entry<SocketAddress, Association>> addrAssocs =
+                        addressMap.entrySet();
+                for (java.util.Map.Entry<SocketAddress, Association> entry : addrAssocs) {
+                    if (entry.getValue().equals(association)) {
+                        addressMap.remove(entry.getKey());
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * @throws  IllegalArgumentException
+     *          If the given association is not controlled by this channel
+     *
+     * @return  {@code true} if, and only if, the given association is one
+     *          of the current associations controlled by this channel
+     */
+    private boolean checkAssociation(Association messageAssoc) {
+        synchronized (stateLock) {
+            for (Association association : associationMap.keySet()) {
+                if (messageAssoc.equals(association)) {
+                    return true;
+                }
+            }
+        }
+        throw new IllegalArgumentException(
+              "Given Association is not controlled by this channel");
+    }
+
+    private void checkStreamNumber(Association assoc, int streamNumber) {
+        synchronized (stateLock) {
+            if (streamNumber < 0 || streamNumber >= assoc.maxOutboundStreams())
+                throw new InvalidStreamException();
+        }
+    }
+
+    /* TODO: Add support for ttl and isComplete to both 121 12M
+     *       SCTP_EOR not yet supported on reference platforms
+     *       TTL support limited...
+     */
+    @Override
+    public int send(ByteBuffer buffer, MessageInfo messageInfo)
+            throws IOException {
+        if (buffer == null)
+            throw new IllegalArgumentException("buffer cannot be null");
+
+        if (messageInfo == null)
+            throw new IllegalArgumentException("messageInfo cannot be null");
+
+        synchronized (sendLock) {
+            ensureOpen();
+
+            if (!isBound())
+                bind(null, 0);
+
+            int n = 0;
+            try {
+                int assocId = -1;
+                SocketAddress address = null;
+                begin();
+
+                synchronized (stateLock) {
+                    if(!isOpen())
+                        return 0;
+                    senderThread = NativeThread.current();
+
+                    /* Determine what address or association to send to */
+                    Association assoc = messageInfo.association();
+                    InetSocketAddress addr = (InetSocketAddress)messageInfo.address();
+                    if (assoc != null) {
+                        checkAssociation(assoc);
+                        checkStreamNumber(assoc, messageInfo.streamNumber());
+                        assocId = assoc.associationID();
+                        /* have we also got a preferred address */
+                        if (addr != null) {
+                            if (!assoc.equals(addressMap.get(addr)))
+                                throw new IllegalArgumentException("given preferred address is not part of this association");
+                            address = addr;
+                        }
+                    } else if (addr != null) {
+                        address = addr;
+                        Association association = addressMap.get(addr);
+                        if (association != null) {
+                            checkStreamNumber(association, messageInfo.streamNumber());
+                            assocId = association.associationID();
+
+                        } else { /* must be new association */
+                            SecurityManager sm = System.getSecurityManager();
+                            if (sm != null)
+                                sm.checkConnect(addr.getAddress().getHostAddress(),
+                                                addr.getPort());
+                        }
+                    } else {
+                        throw new AssertionError(
+                            "Both association and address cannot be null");
+                    }
+                }
+
+                do {
+                    n = send(fdVal, buffer, assocId, address, messageInfo);
+                } while ((n == IOStatus.INTERRUPTED) && isOpen());
+
+                return IOStatus.normalize(n);
+            } finally {
+                senderCleanup();
+                end((n > 0) || (n == IOStatus.UNAVAILABLE));
+                assert IOStatus.check(n);
+            }
+        }
+    }
+
+    private int send(int fd,
+                     ByteBuffer src,
+                     int assocId,
+                     SocketAddress target,
+                     MessageInfo messageInfo)
+            throws IOException {
+        int streamNumber = messageInfo.streamNumber();
+        boolean unordered = messageInfo.isUnordered();
+        int ppid = messageInfo.payloadProtocolID();
+        int pos = src.position();
+        int lim = src.limit();
+        assert (pos <= lim && streamNumber > 0);
+        int rem = (pos <= lim ? lim - pos : 0);
+
+        if (src instanceof DirectBuffer)
+            return sendFromNativeBuffer(fd, src, rem, pos, target, assocId,
+                    streamNumber, unordered, ppid);
+
+        /* Substitute a native buffer */
+        ByteBuffer bb = Util.getTemporaryDirectBuffer(rem);
+        try {
+            bb.put(src);
+            bb.flip();
+            /* Do not update src until we see how many bytes were written */
+            src.position(pos);
+
+            int n = sendFromNativeBuffer(fd, bb, rem, pos, target, assocId,
+                    streamNumber, unordered, ppid);
+            if (n > 0) {
+                /* now update src */
+                src.position(pos + n);
+            }
+            return n;
+        } finally {
+            Util.releaseTemporaryDirectBuffer(bb);
+        }
+    }
+
+    private int sendFromNativeBuffer(int fd,
+                                     ByteBuffer bb,
+                                     int rem,
+                                     int pos,
+                                     SocketAddress target,
+                                     int assocId,
+                                     int streamNumber,
+                                     boolean unordered,
+                                     int ppid)
+            throws IOException {
+        int written = send0(fd, ((DirectBuffer)bb).address() + pos,
+                            rem, target, assocId, streamNumber, unordered, ppid);
+        if (written > 0)
+            bb.position(pos + written);
+        return written;
+    }
+
+    @Override
+    public SctpMultiChannel shutdown(Association association)
+            throws IOException {
+        synchronized (stateLock) {
+            checkAssociation(association);
+            if (!isOpen())
+                throw new ClosedChannelException();
+
+            SctpNet.shutdown(fdVal, association.associationID());
+        }
+        return this;
+    }
+
+    @Override
+    public Set<SocketAddress> getAllLocalAddresses()
+            throws IOException {
+        synchronized (stateLock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+            if (!isBound())
+                return Collections.EMPTY_SET;
+
+            return SctpNet.getLocalAddresses(fdVal);
+        }
+    }
+
+    @Override
+    public Set<SocketAddress> getRemoteAddresses(Association association)
+            throws IOException {
+        synchronized (stateLock) {
+            checkAssociation(association);
+            if (!isOpen())
+                throw new ClosedChannelException();
+
+            return SctpNet.getRemoteAddresses(fdVal, association.associationID());
+        }
+    }
+
+    @Override
+    public SctpChannel branch(Association association)
+            throws IOException {
+        synchronized (stateLock) {
+            return null;  //TODO: implement
+        }
+    }
+
+    /* Use common native implementation shared between
+     * one-to-one and one-to-many */
+    private static int receive0(int fd,
+                                SctpResultContainer resultContainer,
+                                long address,
+                                int length)
+            throws IOException{
+        return SctpChannelImpl.receive0(fd, resultContainer, address,
+                length);
+    }
+
+    private static int send0(int fd,
+                             long address,
+                             int length,
+                             SocketAddress target,
+                             int assocId,
+                             int streamNumber,
+                             boolean unordered,
+                             int ppid)
+            throws IOException {
+        return SctpChannelImpl.send0(fd, address, length, target, assocId,
+                streamNumber, unordered, ppid);
+    }
+
+    static {
+        Util.load();   /* loads nio & net native libraries */
+        java.security.AccessController.doPrivileged(
+                new sun.security.action.LoadLibraryAction("sctp"));
+        nd = new SctpSocketDispatcher();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpNet.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2009 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.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.Set;
+import java.util.HashSet;
+import java.security.AccessController;
+import sun.security.action.GetPropertyAction;
+import com.sun.nio.sctp.SctpSocketOption;
+import static com.sun.nio.sctp.SctpStandardSocketOption.*;
+
+public class SctpNet {
+    static final String osName = AccessController.doPrivileged(
+                    new GetPropertyAction("os.name"));
+
+    /* -- Miscellaneous SCTP utilities -- */
+
+    static boolean bindxIPv4MappedAddresses() {
+        if ("SunOS".equals(osName)) {
+            /* Solaris supports IPv4Mapped Addresses with bindx */
+            return true;
+        } /* else {  //other OS/implementations  */
+
+        /* lksctp/linux requires Ipv4 addresses */
+        return false;
+    }
+
+    /**
+     * @param  oneToone
+     *         if {@code true} returns a one-to-one sctp socket, otherwise
+     *         returns a one-to-many sctp socket
+     */
+    static FileDescriptor socket(boolean oneToOne) throws IOException {
+        int nativefd = socket0(oneToOne);
+        return IOUtil.newFD(nativefd);
+    }
+
+    static void bindx(int fd, InetAddress[] addrs, int port, boolean add)
+            throws IOException {
+        bindx(fd, addrs, port, addrs.length, add,
+                bindxIPv4MappedAddresses());
+    }
+
+    static Set<SocketAddress> getLocalAddresses(int fd)
+            throws IOException {
+        HashSet<SocketAddress> set = null;
+        SocketAddress[] saa = getLocalAddresses0(fd);
+
+        if (saa != null) {
+            set = new HashSet<SocketAddress>(saa.length);
+            for (SocketAddress sa : saa)
+                set.add(sa);
+        }
+
+        return set;
+    }
+
+    static Set<SocketAddress> getRemoteAddresses(int fd, int assocId)
+            throws IOException {
+        HashSet<SocketAddress> set = null;
+        SocketAddress[] saa = getRemoteAddresses0(fd, assocId);
+
+        if (saa != null) {
+            set = new HashSet<SocketAddress>(saa.length);
+            for (SocketAddress sa : saa)
+                set.add(sa);
+        }
+
+        return set;
+    }
+
+    static void setSocketOption(int fd,
+                                SctpSocketOption name,
+                                Object value,
+                                int assocId)
+            throws IOException {
+        if (value == null)
+            throw new IllegalArgumentException("Invalid option value");
+
+        Class<?> type = name.type();
+        if (!type.isInstance(value))
+            throw new IllegalArgumentException("Invalid option value");
+
+        if (name.equals(SCTP_INIT_MAXSTREAMS)) {
+            InitMaxStreams maxStreamValue = (InitMaxStreams)value;
+            SctpNet.setInitMsgOption0(fd,
+                 maxStreamValue.maxInStreams(), maxStreamValue.maxOutStreams());
+        } else if (name.equals(SCTP_PRIMARY_ADDR) ||
+                   name.equals(SCTP_SET_PEER_PRIMARY_ADDR)) {
+
+            SocketAddress addr  = (SocketAddress) value;
+            if (addr == null)
+                throw new IllegalArgumentException("Invalid option value");
+
+            Net.checkAddress(addr);
+            InetSocketAddress netAddr = (InetSocketAddress)addr;
+
+            if (name.equals(SCTP_PRIMARY_ADDR)) {
+                setPrimAddrOption0(fd, assocId,
+                        netAddr.getAddress(), netAddr.getPort());
+            } else {
+                setPeerPrimAddrOption0(fd, assocId,
+                        netAddr.getAddress(), netAddr.getPort());
+            }
+        } else if (name.equals(SCTP_DISABLE_FRAGMENTS) ||
+            name.equals(SCTP_EXPLICIT_COMPLETE) ||
+            name.equals(SCTP_FRAGMENT_INTERLEAVE) ||
+            name.equals(SCTP_NODELAY) ||
+            name.equals(SO_SNDBUF) ||
+            name.equals(SO_RCVBUF) ||
+            name.equals(SO_LINGER)) {
+            setIntOption(fd, name, value);
+        } else {
+            throw new AssertionError("Unknown socket option");
+        }
+    }
+
+    static Object getSocketOption(int fd, SctpSocketOption name, int assocId)
+             throws IOException {
+         if (name.equals(SCTP_SET_PEER_PRIMARY_ADDR)) {
+            throw new IllegalArgumentException(
+                    "SCTP_SET_PEER_PRIMARY_ADDR cannot be retrieved");
+        } else if (name.equals(SCTP_INIT_MAXSTREAMS)) {
+            /* container for holding maxIn/Out streams */
+            int[] values = new int[2];
+            SctpNet.getInitMsgOption0(fd, values);
+            return InitMaxStreams.create(values[0], values[1]);
+        } else if (name.equals(SCTP_PRIMARY_ADDR)) {
+            return getPrimAddrOption0(fd, assocId);
+        } else if (name.equals(SCTP_DISABLE_FRAGMENTS) ||
+            name.equals(SCTP_EXPLICIT_COMPLETE) ||
+            name.equals(SCTP_FRAGMENT_INTERLEAVE) ||
+            name.equals(SCTP_NODELAY) ||
+            name.equals(SO_SNDBUF) ||
+            name.equals(SO_RCVBUF) ||
+            name.equals(SO_LINGER)) {
+            return getIntOption(fd, name);
+        } else {
+            throw new AssertionError("Unknown socket option");
+        }
+    }
+
+    static void setIntOption(int fd, SctpSocketOption name, Object value)
+            throws IOException {
+        if (value == null)
+            throw new IllegalArgumentException("Invalid option value");
+
+        Class<?> type = name.type();
+        if (type != Integer.class && type != Boolean.class)
+            throw new AssertionError("Should not reach here");
+
+        if (name == SO_RCVBUF ||
+            name == SO_SNDBUF)
+        {
+            int i = ((Integer)value).intValue();
+            if (i < 0)
+                throw new IllegalArgumentException(
+                        "Invalid send/receive buffer size");
+        } else if (name == SO_LINGER) {
+            int i = ((Integer)value).intValue();
+            if (i < 0)
+                value = Integer.valueOf(-1);
+            if (i > 65535)
+                value = Integer.valueOf(65535);
+        } else if (name.equals(SCTP_FRAGMENT_INTERLEAVE)) {
+            int i = ((Integer)value).intValue();
+            if (i < 0 || i > 2)
+                throw new IllegalArgumentException(
+                        "Invalid value for SCTP_FRAGMENT_INTERLEAVE");
+        }
+
+        int arg;
+        if (type == Integer.class) {
+            arg = ((Integer)value).intValue();
+        } else {
+            boolean b = ((Boolean)value).booleanValue();
+            arg = (b) ? 1 : 0;
+        }
+
+        setIntOption0(fd, ((SctpStdSocketOption)name).constValue(), arg);
+    }
+
+    static Object getIntOption(int fd, SctpSocketOption name)
+            throws IOException {
+        Class<?> type = name.type();
+
+        if (type != Integer.class && type != Boolean.class)
+            throw new AssertionError("Should not reach here");
+
+        if (!(name instanceof SctpStdSocketOption))
+            throw new AssertionError("Should not reach here");
+
+        int value = getIntOption0(fd,
+                ((SctpStdSocketOption)name).constValue());
+
+        if (type == Integer.class) {
+            return Integer.valueOf(value);
+        } else {
+            return (value == 0) ? Boolean.FALSE : Boolean.TRUE;
+        }
+    }
+
+    static void shutdown(int fd, int assocId)
+            throws IOException {
+        shutdown0(fd, assocId);
+    }
+
+    /* Native Methods */
+    static native int socket0(boolean oneToOne) throws IOException;
+
+    static native void bindx(int fd, InetAddress[] addrs, int port, int length,
+            boolean add, boolean preferIPv6) throws IOException;
+
+    static native int getIntOption0(int fd, int opt) throws IOException;
+
+    static native void setIntOption0(int fd, int opt, int arg)
+        throws IOException;
+
+    static native SocketAddress[] getLocalAddresses0(int fd) throws IOException;
+
+    static native SocketAddress[] getRemoteAddresses0(int fd, int assocId)
+            throws IOException;
+
+    static native void setPrimAddrOption0(int fd, int assocId, InetAddress ia,
+            int port) throws IOException;
+
+    static native void setPeerPrimAddrOption0(int fd, int assocId,
+            InetAddress ia, int port) throws IOException;
+
+    static native SocketAddress getPrimAddrOption0(int fd, int assocId)
+            throws IOException;
+
+    /* retVals [0] maxInStreams, [1] maxOutStreams */
+    static native void getInitMsgOption0(int fd, int[] retVals) throws IOException;
+
+    static native void setInitMsgOption0(int fd, int arg1, int arg2)
+            throws IOException;
+
+    static native void shutdown0(int fd, int assocId);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpNotification.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2009 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 com.sun.nio.sctp.Association;
+import com.sun.nio.sctp.Notification;
+
+/**
+ * All Notification implemenations MUST implement this interface to provide
+ * access to the native association identidier.
+ */
+interface SctpNotification extends Notification {
+    int assocId();
+    void setAssociation(Association association);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpPeerAddrChange.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2009 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.SocketAddress;
+import com.sun.nio.sctp.Association;
+import com.sun.nio.sctp.PeerAddressChangeNotification;
+
+/**
+ * An implementation of PeerAddressChangeNotification
+ */
+public class SctpPeerAddrChange extends PeerAddressChangeNotification
+    implements SctpNotification
+{
+    /* static final ints so that they can be referenced from native */
+    private final static int SCTP_ADDR_AVAILABLE = 1;
+    private final static int SCTP_ADDR_UNREACHABLE = 2;
+    private final static int SCTP_ADDR_REMOVED = 3;
+    private final static int SCTP_ADDR_ADDED = 4;
+    private final static int SCTP_ADDR_MADE_PRIM = 5;
+    private final static int SCTP_ADDR_CONFIRMED =6;
+
+    private Association association;
+
+    /* assocId is used to lookup the association before the notification is
+     * returned to user code */
+    private int assocId;
+    private SocketAddress address;
+    private AddressChangeEvent event;
+
+    /* Invoked from native */
+    private SctpPeerAddrChange(int assocId, SocketAddress address, int intEvent) {
+        switch (intEvent) {
+            case SCTP_ADDR_AVAILABLE :
+                this.event = AddressChangeEvent.ADDR_AVAILABLE;
+                break;
+            case SCTP_ADDR_UNREACHABLE :
+                this.event = AddressChangeEvent.ADDR_UNREACHABLE;
+                break;
+            case SCTP_ADDR_REMOVED :
+                this.event = AddressChangeEvent.ADDR_REMOVED;
+                break;
+            case SCTP_ADDR_ADDED :
+                this.event = AddressChangeEvent.ADDR_ADDED;
+                break;
+            case SCTP_ADDR_MADE_PRIM :
+                this.event = AddressChangeEvent.ADDR_MADE_PRIMARY;
+                break;
+            case SCTP_ADDR_CONFIRMED :
+                this.event = AddressChangeEvent.ADDR_CONFIRMED;
+                break;
+            default:
+                throw new AssertionError("Unknown event type");
+        }
+        this.assocId = assocId;
+        this.address = address;
+    }
+
+    @Override
+    public int assocId() {
+        return assocId;
+    }
+
+    @Override
+    public void setAssociation(Association association) {
+        this.association = association;
+    }
+
+    @Override
+    public SocketAddress address() {
+        assert address != null;
+        return address;
+    }
+
+    @Override
+    public Association association() {
+        assert association != null;
+        return association;
+    }
+
+    @Override
+    public AddressChangeEvent event() {
+        assert event != null;
+        return event;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(super.toString()).append(" [");
+        sb.append("Address: ").append(address);
+        sb.append(", Association:").append(association);
+        sb.append(", Event: ").append(event).append("]");
+        return sb.toString();
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpResultContainer.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2009 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;
+
+/**
+ * Wraps the actual message or notification so that it can be
+ * set and returned from the native receive implementation.
+ */
+public class SctpResultContainer {
+    /* static final ints so that they can be referenced from native */
+    static final int NOTHING = 0;
+    static final int MESSAGE = 1;
+    static final int SEND_FAILED = 2;
+    static final int ASSOCIATION_CHANGED = 3;
+    static final int PEER_ADDRESS_CHANGED = 4;
+    static final int SHUTDOWN = 5;
+
+    private Object value;
+    private int type;
+
+    int type() {
+        return type;
+    }
+
+    boolean hasSomething() {
+        return type() != NOTHING;
+    }
+
+    boolean isNotification() {
+        return type() != MESSAGE && type() != NOTHING ? true : false;
+    }
+
+    void clear() {
+        type = NOTHING;
+        value = null;
+    }
+
+    SctpNotification notification() {
+        assert type() != MESSAGE && type() != NOTHING;
+
+        return (SctpNotification) value;
+    }
+
+    SctpMessageInfoImpl getMessageInfo() {
+        assert type() == MESSAGE;
+
+        if (value instanceof SctpMessageInfoImpl)
+            return (SctpMessageInfoImpl) value;
+
+        return null;
+    }
+
+    SctpSendFailed getSendFailed() {
+        assert type() == SEND_FAILED;
+
+        if (value instanceof SctpSendFailed)
+            return (SctpSendFailed) value;
+
+        return null;
+    }
+
+    SctpAssocChange getAssociationChanged() {
+        assert type() == ASSOCIATION_CHANGED;
+
+        if (value instanceof SctpAssocChange)
+            return (SctpAssocChange) value;
+
+        return null;
+    }
+
+    SctpPeerAddrChange getPeerAddressChanged() {
+        assert type() == PEER_ADDRESS_CHANGED;
+
+        if (value instanceof SctpPeerAddrChange)
+            return (SctpPeerAddrChange) value;
+
+        return null;
+    }
+
+    SctpShutdown getShutdown() {
+        assert type() == SHUTDOWN;
+
+        if (value instanceof SctpShutdown)
+            return (SctpShutdown) value;
+
+        return null;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Type: ");
+        switch (type) {
+            case NOTHING:              sb.append("NOTHING");             break;
+            case MESSAGE:              sb.append("MESSAGE");             break;
+            case SEND_FAILED:          sb.append("SEND FAILED");         break;
+            case ASSOCIATION_CHANGED:  sb.append("ASSOCIATION CHANGE");  break;
+            case PEER_ADDRESS_CHANGED: sb.append("PEER ADDRESS CHANGE"); break;
+            case SHUTDOWN:             sb.append("SHUTDOWN");            break;
+            default :                  sb.append("Unknown result type");
+        }
+       return sb.append(", Value: ").append(value.toString()).toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpSendFailed.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2009 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.ByteBuffer;
+import java.net.SocketAddress;
+import com.sun.nio.sctp.Association;
+import com.sun.nio.sctp.SendFailedNotification;
+
+/**
+ * An implementation of SendFailedNotification
+ */
+public class SctpSendFailed extends SendFailedNotification
+    implements SctpNotification
+{
+    private Association association;
+    /* assocId is used to lookup the association before the notification is
+     * returned to user code */
+    private int assocId;
+    private SocketAddress address;
+    private ByteBuffer buffer;
+    private int errorCode;
+    private int streamNumber;
+
+    /* Invoked from native */
+    private SctpSendFailed(int assocId,
+                           SocketAddress address,
+                           ByteBuffer buffer,
+                           int errorCode,
+                           int streamNumber) {
+        this.assocId = assocId;
+        this.errorCode = errorCode;
+        this.streamNumber = streamNumber;
+        this.address = address;
+        this.buffer = buffer;
+    }
+
+    @Override
+    public int assocId() {
+        return assocId;
+    }
+
+    @Override
+    public void setAssociation(Association association) {
+        this.association = association;
+    }
+
+    @Override
+    public Association association() {
+        /* may be null */
+        return association;
+    }
+
+    @Override
+    public SocketAddress address() {
+        assert address != null;
+        return address;
+    }
+
+    @Override
+    public ByteBuffer buffer() {
+        assert buffer != null;
+        return buffer;
+    }
+
+    @Override
+    public int errorCode() {
+        return errorCode;
+    }
+
+    @Override
+    public int streamNumber() {
+        return streamNumber;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(super.toString()).append(" [");
+        sb.append("Association:").append(association);
+        sb.append(", Address: ").append(address);
+        sb.append(", buffer: ").append(buffer);
+        sb.append(", errorCode: ").append(errorCode);
+        sb.append(", streamNumber: ").append(streamNumber);
+        sb.append("]");
+        return sb.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpServerChannelImpl.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,429 @@
+/*
+ * Copyright 2009 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.SocketAddress;
+import java.net.InetSocketAddress;
+import java.net.InetAddress;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Set;
+import java.util.HashSet;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.AlreadyBoundException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.NotYetBoundException;
+import java.nio.channels.spi.SelectorProvider;
+import com.sun.nio.sctp.IllegalUnbindException;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpServerChannel;
+import com.sun.nio.sctp.SctpSocketOption;
+import com.sun.nio.sctp.SctpStandardSocketOption;
+
+/**
+ * An implementation of SctpServerChannel
+ */
+public class SctpServerChannelImpl extends SctpServerChannel
+    implements SelChImpl
+{
+    /* Used to make native close and preClose calls */
+    private static NativeDispatcher nd;
+
+    private final FileDescriptor fd;
+
+    private final int fdVal;
+
+    /* IDs of native thread doing accept, for signalling */
+    private volatile long thread = 0;
+
+    /* Lock held by thread currently blocked in this channel */
+    private final Object lock = new Object();
+
+    /* Lock held by any thread that modifies the state fields declared below
+     * DO NOT invoke a blocking I/O operation while holding this lock! */
+    private final Object stateLock = new Object();
+
+    private enum ChannelState {
+        UNINITIALIZED,
+        INUSE,
+        KILLPENDING,
+        KILLED,
+    }
+    /* -- The following fields are protected by stateLock -- */
+    private ChannelState state = ChannelState.UNINITIALIZED;
+
+    /* Binding: Once bound the port will remain constant. */
+    int port = -1;
+    private HashSet<InetSocketAddress> localAddresses = new HashSet<InetSocketAddress>();
+    /* Has the channel been bound to the wildcard address */
+    private boolean wildcard; /* false */
+
+    /* -- End of fields protected by stateLock -- */
+
+    /**
+     * Initializes a new instance of this class.
+     */
+    public SctpServerChannelImpl(SelectorProvider provider)
+            throws IOException {
+        //TODO: update provider remove public modifier
+        super(provider);
+        this.fd = SctpNet.socket(true);
+        this.fdVal = IOUtil.fdVal(fd);
+        this.state = ChannelState.INUSE;
+    }
+
+    @Override
+    public SctpServerChannel bind(SocketAddress local, int backlog)
+            throws IOException {
+        synchronized (lock) {
+            synchronized (stateLock) {
+                if (!isOpen())
+                    throw new ClosedChannelException();
+                if (isBound())
+                    throw new AlreadyBoundException();
+
+                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());
+
+                InetSocketAddress boundIsa = Net.localAddress(fd);
+                port = boundIsa.getPort();
+                localAddresses.add(isa);
+                    if (isa.getAddress().isAnyLocalAddress())
+                        wildcard = true;
+
+                Net.listen(fd, backlog < 1 ? 50 : backlog);
+            }
+        }
+        return this;
+    }
+
+    @Override
+    public SctpServerChannel bindAddress(InetAddress address)
+            throws IOException {
+        return bindUnbindAddress(address, true);
+    }
+
+    @Override
+    public SctpServerChannel unbindAddress(InetAddress address)
+            throws IOException {
+        return bindUnbindAddress(address, false);
+    }
+
+    private SctpServerChannel bindUnbindAddress(InetAddress address, boolean add)
+            throws IOException {
+        if (address == null)
+            throw new IllegalArgumentException();
+
+        synchronized (lock) {
+            synchronized (stateLock) {
+                if (!isOpen())
+                    throw new ClosedChannelException();
+                if (!isBound())
+                    throw new NotYetBoundException();
+                if (wildcard)
+                    throw new IllegalStateException(
+                            "Cannot add or remove addresses from a channel that is bound to the wildcard address");
+                if (address.isAnyLocalAddress())
+                    throw new IllegalArgumentException(
+                            "Cannot add or remove the wildcard address");
+                if (add) {
+                    for (InetSocketAddress addr : localAddresses) {
+                        if (addr.getAddress().equals(address)) {
+                            throw new AlreadyBoundException();
+                        }
+                    }
+                } else { /*removing */
+                    /* Verify that there is more than one address
+                     * and that address is already bound */
+                    if (localAddresses.size() <= 1)
+                        throw new IllegalUnbindException("Cannot remove address from a channel with only one address bound");
+                    boolean foundAddress = false;
+                    for (InetSocketAddress addr : localAddresses) {
+                        if (addr.getAddress().equals(address)) {
+                            foundAddress = true;
+                            break;
+                        }
+                    }
+                    if (!foundAddress )
+                        throw new IllegalUnbindException("Cannot remove address from a channel that is not bound to that address");
+                }
+
+                SctpNet.bindx(fdVal, new InetAddress[]{address}, port, add);
+
+                /* Update our internal Set to reflect the addition/removal */
+                if (add)
+                    localAddresses.add(new InetSocketAddress(address, port));
+                else {
+                    for (InetSocketAddress addr : localAddresses) {
+                        if (addr.getAddress().equals(address)) {
+                            localAddresses.remove(addr);
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        return this;
+    }
+
+    private boolean isBound() {
+        synchronized (stateLock) {
+            return port == -1 ? false : true;
+        }
+    }
+
+    private void acceptCleanup() throws IOException {
+        synchronized (stateLock) {
+            thread = 0;
+            if (state == ChannelState.KILLPENDING)
+                kill();
+        }
+    }
+
+    @Override
+    public SctpChannel accept() throws IOException {
+        synchronized (lock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+            if (!isBound())
+                throw new NotYetBoundException();
+            SctpChannel sc = null;
+
+            int n = 0;
+            FileDescriptor newfd = new FileDescriptor();
+            InetSocketAddress[] isaa = new InetSocketAddress[1];
+
+            try {
+                begin();
+                if (!isOpen())
+                    return null;
+                thread = NativeThread.current();
+                for (;;) {
+                    n = accept0(fd, newfd, isaa);
+                    if ((n == IOStatus.INTERRUPTED) && isOpen())
+                        continue;
+                    break;
+                }
+            } finally {
+                acceptCleanup();
+                end(n > 0);
+                assert IOStatus.check(n);
+            }
+
+            if (n < 1)
+                return null;
+
+            IOUtil.configureBlocking(newfd, true);
+            InetSocketAddress isa = isaa[0];
+            sc = new SctpChannelImpl(provider(), newfd);
+
+            SecurityManager sm = System.getSecurityManager();
+            if (sm != null)
+                sm.checkAccept(isa.getAddress().getHostAddress(),
+                               isa.getPort());
+
+            return sc;
+        }
+    }
+
+    @Override
+    protected void implConfigureBlocking(boolean block) throws IOException {
+        IOUtil.configureBlocking(fd, block);
+    }
+
+    @Override
+    public void implCloseSelectableChannel() throws IOException {
+        synchronized (stateLock) {
+            nd.preClose(fd);
+            if (thread != 0)
+                NativeThread.signal(thread);
+            if (!isRegistered())
+                kill();
+        }
+    }
+
+    @Override
+    public void kill() throws IOException {
+        synchronized (stateLock) {
+            if (state == ChannelState.KILLED)
+                return;
+            if (state == ChannelState.UNINITIALIZED) {
+                state = ChannelState.KILLED;
+                return;
+            }
+            assert !isOpen() && !isRegistered();
+
+            // Postpone the kill if there is a thread in accept
+            if (thread == 0) {
+                nd.close(fd);
+                state = ChannelState.KILLED;
+            } else {
+                state = ChannelState.KILLPENDING;
+            }
+        }
+    }
+
+    @Override
+    public FileDescriptor getFD() {
+        return fd;
+    }
+
+    @Override
+    public int getFDVal() {
+        return fdVal;
+    }
+
+    /**
+     * Translates native poll revent ops into a ready operation ops
+     */
+    private boolean translateReadyOps(int ops, int initialOps,
+                                     SelectionKeyImpl sk) {
+        int intOps = sk.nioInterestOps();
+        int oldOps = sk.nioReadyOps();
+        int newOps = initialOps;
+
+        if ((ops & PollArrayWrapper.POLLNVAL) != 0) {
+            /* This should only happen if this channel is pre-closed while a
+             * selection operation is in progress
+             * ## Throw an error if this channel has not been pre-closed */
+            return false;
+        }
+
+        if ((ops & (PollArrayWrapper.POLLERR
+                    | PollArrayWrapper.POLLHUP)) != 0) {
+            newOps = intOps;
+            sk.nioReadyOps(newOps);
+            return (newOps & ~oldOps) != 0;
+        }
+
+        if (((ops & PollArrayWrapper.POLLIN) != 0) &&
+            ((intOps & SelectionKey.OP_ACCEPT) != 0))
+                newOps |= SelectionKey.OP_ACCEPT;
+
+        sk.nioReadyOps(newOps);
+        return (newOps & ~oldOps) != 0;
+    }
+
+    @Override
+    public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
+        return translateReadyOps(ops, sk.nioReadyOps(), sk);
+    }
+
+    @Override
+    public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
+        return translateReadyOps(ops, 0, sk);
+    }
+
+    @Override
+    public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) {
+        int newOps = 0;
+
+        /* Translate ops */
+        if ((ops & SelectionKey.OP_ACCEPT) != 0)
+            newOps |= PollArrayWrapper.POLLIN;
+        /* Place ops into pollfd array */
+        sk.selector.putEventOps(sk, newOps);
+
+    }
+
+    @Override
+    public <T> SctpServerChannel setOption(SctpSocketOption<T> name, T value)
+            throws IOException {
+        if (name == null)
+            throw new NullPointerException();
+        if (!supportedOptions().contains(name))
+            throw new UnsupportedOperationException("'" + name + "' not supported");
+
+        synchronized (stateLock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+
+            SctpNet.setSocketOption(fdVal, name, value, 0 /*oneToOne*/);
+            return this;
+        }
+    }
+
+    @Override
+    public <T> T getOption(SctpSocketOption<T> name) throws IOException {
+        if (name == null)
+            throw new NullPointerException();
+        if (!supportedOptions().contains(name))
+            throw new UnsupportedOperationException("'" + name + "' not supported");
+
+        synchronized (stateLock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+
+            return (T) SctpNet.getSocketOption(fdVal, name, 0 /*oneToOne*/);
+        }
+    }
+
+    private static class DefaultOptionsHolder {
+        static final Set<SctpSocketOption<?>> defaultOptions = defaultOptions();
+
+        private static Set<SctpSocketOption<?>> defaultOptions() {
+            HashSet<SctpSocketOption<?>> set = new HashSet<SctpSocketOption<?>>(1);
+            set.add(SctpStandardSocketOption.SCTP_INIT_MAXSTREAMS);
+            return Collections.unmodifiableSet(set);
+        }
+    }
+
+    @Override
+    public final Set<SctpSocketOption<?>> supportedOptions() {
+        return DefaultOptionsHolder.defaultOptions;
+    }
+
+    @Override
+    public Set<SocketAddress> getAllLocalAddresses()
+            throws IOException {
+        synchronized (stateLock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+            if (!isBound())
+                return null;
+
+            return SctpNet.getLocalAddresses(fdVal);
+        }
+    }
+
+    /* Native */
+    private static native void initIDs();
+
+    private static native int accept0(FileDescriptor ssfd,
+        FileDescriptor newfd, InetSocketAddress[] isaa) throws IOException;
+
+    static {
+        Util.load();   // loads nio & net native libraries
+        java.security.AccessController.doPrivileged(
+                new sun.security.action.LoadLibraryAction("sctp"));
+        nd = new SctpSocketDispatcher();
+        initIDs();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpShutdown.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2009 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 com.sun.nio.sctp.Association;
+import com.sun.nio.sctp.ShutdownNotification;
+
+/**
+ * An implementation of ShutdownNotification
+ */
+public class SctpShutdown extends ShutdownNotification
+    implements SctpNotification
+{
+    private Association association;
+    /* assocId is used to lookup the association before the notification is
+     * returned to user code */
+    private int assocId;
+
+    /* Invoked from native */
+    private SctpShutdown(int assocId) {
+        this.assocId = assocId;
+    }
+
+    @Override
+    public int assocId() {
+        return assocId;
+    }
+
+    @Override
+    public void setAssociation(Association association) {
+        this.association = association;
+    }
+
+    @Override
+    public Association association() {
+        assert association != null;
+        return association;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(super.toString()).append(" [");
+        sb.append("Association:").append(association).append("]");
+        return sb.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpSocketDispatcher.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2009 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.io.FileDescriptor;
+
+/**
+ * Only used for {@code close} and {@code preclose}. All other methods
+ * throw {@code IOException}.
+ */
+class SctpSocketDispatcher extends NativeDispatcher {
+    @Override
+    @SuppressWarnings("unused")
+    int read(FileDescriptor fd, long address, int len) throws IOException {
+         throw new IOException("Operation Unsupported");
+    }
+
+    @Override
+    @SuppressWarnings("unused")
+    long readv(FileDescriptor fd, long address, int len) throws IOException {
+         throw new IOException("Operation Unsupported");
+    }
+
+    @Override
+    @SuppressWarnings("unused")
+    int write(FileDescriptor fd, long address, int len) throws IOException {
+         throw new IOException("Operation Unsupported");
+    }
+
+    @Override
+    @SuppressWarnings("unused")
+    long writev(FileDescriptor fd, long address, int len) throws IOException {
+         throw new IOException("Operation Unsupported");
+    }
+
+    @Override
+    void close(FileDescriptor fd) throws IOException {
+        FileDispatcherImpl.close0(fd);
+    }
+
+    @Override
+    void preClose(FileDescriptor fd) throws IOException {
+        FileDispatcherImpl.preClose0(fd);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/native/sun/nio/ch/Sctp.h	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,329 @@
+/*
+ * Copyright 2009 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.
+ */
+
+#ifndef SUN_NIO_CH_SCTP_H
+#define SUN_NIO_CH_SCTP_H
+
+#ifdef __solaris__
+
+#define _XPG4_2
+#define __EXTENSIONS__
+#include <sys/socket.h>
+#include <netinet/sctp.h>
+#include "jni.h"
+
+/* Current Solaris headers don't comply with draft rfc */
+#ifndef SCTP_EOF
+#define SCTP_EOF MSG_EOF
+#endif
+
+#ifndef SCTP_UNORDERED
+#define SCTP_UNORDERED MSG_UNORDERED
+#endif
+
+/* The current version of the socket API extension shipped with Solaris does
+ * not define the following options that the Java API (optionally) supports */
+#ifndef SCTP_EXPLICIT_EOR
+#define SCTP_EXPLICIT_EOR -1
+#endif
+#ifndef SCTP_FRAGMENT_INTERLEAVE
+#define SCTP_FRAGMENT_INTERLEAVE -1
+#endif
+#ifndef SCTP_SET_PEER_PRIMARY_ADDR
+#define SCTP_SET_PEER_PRIMARY_ADDR -1
+#endif
+
+/* Function types to support dynamic linking of socket API extension functions
+ * for SCTP. This is so that there is no linkage depandancy during build or
+ * runtime for libsctp.*/
+typedef int sctp_getladdrs_func(int sock, sctp_assoc_t id, void **addrs);
+typedef int sctp_freeladdrs_func(void* addrs);
+typedef int sctp_getpaddrs_func(int sock, sctp_assoc_t id, void **addrs);
+typedef int sctp_freepaddrs_func(void *addrs);
+typedef int sctp_bindx_func(int sock, void *addrs, int addrcnt, int flags);
+
+
+#else /* __linux__ */
+#include <stdint.h>
+#include <linux/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "jni.h"
+
+//Causes compiler error if not found, should make warning and uncomment
+/*#include <netinet/sctp.h>*/
+
+#ifndef IPPROTO_SCTP
+#define IPPROTO_SCTP    132
+#endif
+
+/* The current version of lksctp does
+ * not define the following option that the Java API (optionally) supports */
+#ifndef SCTP_EXPLICIT_EOR
+#define SCTP_EXPLICIT_EOR -1
+#endif
+
+/* Definitions taken from lksctp-tools-1.0.8/src/include/netinet/sctp.h */
+#ifndef SCTP_INITMSG
+
+enum sctp_optname {
+        SCTP_RTOINFO,
+#define SCTP_RTOINFO SCTP_RTOINFO
+        SCTP_ASSOCINFO,
+#define SCTP_ASSOCINFO SCTP_ASSOCINFO
+        SCTP_INITMSG,
+#define SCTP_INITMSG SCTP_INITMSG
+        SCTP_NODELAY,   /* Get/set nodelay option. */
+#define SCTP_NODELAY    SCTP_NODELAY
+        SCTP_AUTOCLOSE,
+#define SCTP_AUTOCLOSE SCTP_AUTOCLOSE
+        SCTP_SET_PEER_PRIMARY_ADDR,
+#define SCTP_SET_PEER_PRIMARY_ADDR SCTP_SET_PEER_PRIMARY_ADDR
+        SCTP_PRIMARY_ADDR,
+#define SCTP_PRIMARY_ADDR SCTP_PRIMARY_ADDR
+        SCTP_ADAPTATION_LAYER,
+#define SCTP_ADAPTATION_LAYER SCTP_ADAPTATION_LAYER
+        SCTP_DISABLE_FRAGMENTS,
+#define SCTP_DISABLE_FRAGMENTS SCTP_DISABLE_FRAGMENTS
+        SCTP_PEER_ADDR_PARAMS,
+#define SCTP_PEER_ADDR_PARAMS SCTP_PEER_ADDR_PARAMS
+        SCTP_DEFAULT_SEND_PARAM,
+#define SCTP_DEFAULT_SEND_PARAM SCTP_DEFAULT_SEND_PARAM
+        SCTP_EVENTS,
+#define SCTP_EVENTS SCTP_EVENTS
+        SCTP_I_WANT_MAPPED_V4_ADDR,  /* Turn on/off mapped v4 addresses  */
+#define SCTP_I_WANT_MAPPED_V4_ADDR SCTP_I_WANT_MAPPED_V4_ADDR
+        SCTP_MAXSEG,    /* Get/set maximum fragment. */
+#define SCTP_MAXSEG     SCTP_MAXSEG
+        SCTP_STATUS,
+#define SCTP_STATUS SCTP_STATUS
+        SCTP_GET_PEER_ADDR_INFO,
+#define SCTP_GET_PEER_ADDR_INFO SCTP_GET_PEER_ADDR_INFO
+        SCTP_DELAYED_ACK_TIME,
+#define SCTP_DELAYED_ACK_TIME SCTP_DELAYED_ACK_TIME
+        SCTP_CONTEXT,   /* Receive Context */
+#define SCTP_CONTEXT SCTP_CONTEXT
+        SCTP_FRAGMENT_INTERLEAVE,
+#define SCTP_FRAGMENT_INTERLEAVE SCTP_FRAGMENT_INTERLEAVE
+        SCTP_PARTIAL_DELIVERY_POINT,    /* Set/Get partial delivery point */
+#define SCTP_PARTIAL_DELIVERY_POINT SCTP_PARTIAL_DELIVERY_POINT
+        SCTP_MAX_BURST,         /* Set/Get max burst */
+#define SCTP_MAX_BURST SCTP_MAX_BURST
+};
+
+enum sctp_sac_state {
+        SCTP_COMM_UP,
+        SCTP_COMM_LOST,
+        SCTP_RESTART,
+        SCTP_SHUTDOWN_COMP,
+        SCTP_CANT_STR_ASSOC,
+};
+
+enum sctp_spc_state {
+        SCTP_ADDR_AVAILABLE,
+        SCTP_ADDR_UNREACHABLE,
+        SCTP_ADDR_REMOVED,
+        SCTP_ADDR_ADDED,
+        SCTP_ADDR_MADE_PRIM,
+        SCTP_ADDR_CONFIRMED,
+};
+
+enum sctp_sinfo_flags {
+        SCTP_UNORDERED = 1,  /* Send/receive message unordered. */
+        SCTP_ADDR_OVER = 2,  /* Override the primary destination. */
+        SCTP_ABORT=4,        /* Send an ABORT message to the peer. */
+        SCTP_EOF=MSG_FIN,    /* Initiate graceful shutdown process. */
+};
+
+enum sctp_sn_type {
+        SCTP_SN_TYPE_BASE     = (1<<15),
+        SCTP_ASSOC_CHANGE,
+        SCTP_PEER_ADDR_CHANGE,
+        SCTP_SEND_FAILED,
+        SCTP_REMOTE_ERROR,
+        SCTP_SHUTDOWN_EVENT,
+        SCTP_PARTIAL_DELIVERY_EVENT,
+        SCTP_ADAPTATION_INDICATION,
+};
+
+typedef enum sctp_cmsg_type {
+        SCTP_INIT,              /* 5.2.1 SCTP Initiation Structure */
+#define SCTP_INIT SCTP_INIT
+        SCTP_SNDRCV,            /* 5.2.2 SCTP Header Information Structure */
+#define SCTP_SNDRCV SCTP_SNDRCV
+} sctp_cmsg_t;
+
+enum sctp_msg_flags {
+        MSG_NOTIFICATION = 0x8000,
+#define MSG_NOTIFICATION MSG_NOTIFICATION
+};
+
+#define SCTP_BINDX_ADD_ADDR 0x01
+#define SCTP_BINDX_REM_ADDR 0x02
+
+typedef __s32 sctp_assoc_t;
+
+struct sctp_initmsg {
+        __u16 sinit_num_ostreams;
+        __u16 sinit_max_instreams;
+        __u16 sinit_max_attempts;
+        __u16 sinit_max_init_timeo;
+};
+
+struct sctp_sndrcvinfo {
+        __u16 sinfo_stream;
+        __u16 sinfo_ssn;
+        __u16 sinfo_flags;
+        __u32 sinfo_ppid;
+        __u32 sinfo_context;
+        __u32 sinfo_timetolive;
+        __u32 sinfo_tsn;
+        __u32 sinfo_cumtsn;
+        sctp_assoc_t sinfo_assoc_id;
+};
+
+struct sctp_event_subscribe {
+        __u8 sctp_data_io_event;
+        __u8 sctp_association_event;
+        __u8 sctp_address_event;
+        __u8 sctp_send_failure_event;
+        __u8 sctp_peer_error_event;
+        __u8 sctp_shutdown_event;
+        __u8 sctp_partial_delivery_event;
+        __u8 sctp_adaptation_layer_event;
+};
+
+struct sctp_send_failed {
+        __u16 ssf_type;
+        __u16 ssf_flags;
+        __u32 ssf_length;
+        __u32 ssf_error;
+        struct sctp_sndrcvinfo ssf_info;
+        sctp_assoc_t ssf_assoc_id;
+        __u8 ssf_data[0];
+};
+
+struct sctp_assoc_change {
+        __u16 sac_type;
+        __u16 sac_flags;
+        __u32 sac_length;
+        __u16 sac_state;
+        __u16 sac_error;
+        __u16 sac_outbound_streams;
+        __u16 sac_inbound_streams;
+        sctp_assoc_t sac_assoc_id;
+        __u8 sac_info[0];
+};
+
+struct sctp_shutdown_event {
+        __u16 sse_type;
+        __u16 sse_flags;
+        __u32 sse_length;
+        sctp_assoc_t sse_assoc_id;
+};
+
+struct sctp_paddr_change {
+        __u16 spc_type;
+        __u16 spc_flags;
+        __u32 spc_length;
+        struct sockaddr_storage spc_aaddr;
+        int spc_state;
+        int spc_error;
+        sctp_assoc_t spc_assoc_id;
+} __attribute__((packed, aligned(4)));
+
+struct sctp_remote_error {
+        __u16 sre_type;
+        __u16 sre_flags;
+        __u32 sre_length;
+        __u16 sre_error;
+        sctp_assoc_t sre_assoc_id;
+        __u8 sre_data[0];
+};
+
+struct sctp_adaptation_event {
+        __u16 sai_type;
+        __u16 sai_flags;
+        __u32 sai_length;
+        __u32 sai_adaptation_ind;
+        sctp_assoc_t sai_assoc_id;
+};
+
+struct sctp_setprim {
+        sctp_assoc_t            ssp_assoc_id;
+        struct sockaddr_storage ssp_addr;
+} __attribute__((packed, aligned(4)));
+
+struct sctp_setpeerprim {
+        sctp_assoc_t            sspp_assoc_id;
+        struct sockaddr_storage sspp_addr;
+} __attribute__((packed, aligned(4)));
+
+
+struct sctp_pdapi_event {
+        __u16 pdapi_type;
+        __u16 pdapi_flags;
+        __u32 pdapi_length;
+        __u32 pdapi_indication;
+        sctp_assoc_t pdapi_assoc_id;
+};
+
+union sctp_notification {
+        struct {
+                __u16 sn_type;             /* Notification type. */
+                __u16 sn_flags;
+                __u32 sn_length;
+        } sn_header;
+        struct sctp_assoc_change sn_assoc_change;
+        struct sctp_paddr_change sn_paddr_change;
+        struct sctp_remote_error sn_remote_error;
+        struct sctp_send_failed sn_send_failed;
+        struct sctp_shutdown_event sn_shutdown_event;
+        struct sctp_adaptation_event sn_adaptation_event;
+        struct sctp_pdapi_event sn_pdapi_event;
+};
+
+#endif /* SCTP_INITMSG */
+
+/* Function types to support dynamic linking of socket API extension functions
+ * for SCTP. This is so that there is no linkage depandancy during build or
+ * runtime for libsctp.*/
+typedef int sctp_getladdrs_func(int sd, sctp_assoc_t id, struct sockaddr **addrs);
+typedef int sctp_freeladdrs_func(struct sockaddr *addrs);
+typedef int sctp_getpaddrs_func(int sd, sctp_assoc_t id, struct sockaddr **addrs);
+typedef int sctp_freepaddrs_func(struct sockaddr *addrs);
+typedef int sctp_bindx_func(int sd, struct sockaddr *addrs, int addrcnt, int flags);
+
+#endif /* __linux__ */
+
+sctp_getladdrs_func* nio_sctp_getladdrs;
+sctp_freeladdrs_func* nio_sctp_freeladdrs;
+sctp_getpaddrs_func* nio_sctp_getpaddrs;
+sctp_freepaddrs_func* nio_sctp_freepaddrs;
+sctp_bindx_func* nio_sctp_bindx;
+
+jboolean loadSocketExtensionFuncs(JNIEnv* env);
+
+#endif /* !SUN_NIO_CH_SCTP_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/native/sun/nio/ch/SctpChannelImpl.c	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,594 @@
+/*
+ * Copyright 2009 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 <stdlib.h>
+#include <string.h>
+#include "Sctp.h"
+
+#include "jni.h"
+#include "nio_util.h"
+#include "nio.h"
+#include "net_util.h"
+#include "net_util_md.h"
+#include "sun_nio_ch_SctpNet.h"
+#include "sun_nio_ch_SctpChannelImpl.h"
+#include "sun_nio_ch_SctpAssocChange.h"
+#include "sun_nio_ch_SctpResultContainer.h"
+#include "sun_nio_ch_SctpPeerAddrChange.h"
+
+/* sizeof(union sctp_notification */
+#define NOTIFICATION_BUFFER_SIZE 280
+
+#define MESSAGE_IMPL_CLASS              "sun/nio/ch/SctpMessageInfoImpl"
+#define RESULT_CONTAINER_CLASS          "sun/nio/ch/SctpResultContainer"
+#define SEND_FAILED_CLASS               "sun/nio/ch/SctpSendFailed"
+#define ASSOC_CHANGE_CLASS              "sun/nio/ch/SctpAssocChange"
+#define PEER_CHANGE_CLASS               "sun/nio/ch/SctpPeerAddrChange"
+#define SHUTDOWN_CLASS                  "sun/nio/ch/SctpShutdown"
+
+struct controlData {
+    int assocId;
+    unsigned short streamNumber;
+    jboolean unordered;
+    unsigned int ppid;
+};
+
+static jclass    smi_class;    /* sun.nio.ch.SctpMessageInfoImpl            */
+static jmethodID smi_ctrID;    /* sun.nio.ch.SctpMessageInfoImpl.<init>     */
+static jfieldID  src_valueID;  /* sun.nio.ch.SctpResultContainer.value      */
+static jfieldID  src_typeID;   /* sun.nio.ch.SctpResultContainer.type       */
+static jclass    ssf_class;    /* sun.nio.ch.SctpSendFailed                 */
+static jmethodID ssf_ctrID;    /* sun.nio.ch.SctpSendFailed.<init>          */
+static jclass    sac_class;    /* sun.nio.ch.SctpAssociationChanged         */
+static jmethodID sac_ctrID;    /* sun.nio.ch.SctpAssociationChanged.<init>  */
+static jclass    spc_class;    /* sun.nio.ch.SctpPeerAddressChanged         */
+static jmethodID spc_ctrID;    /* sun.nio.ch.SctpPeerAddressChanged.<init>  */
+static jclass    ss_class;     /* sun.nio.ch.SctpShutdown                   */
+static jmethodID ss_ctrID;     /* sun.nio.ch.SctpShutdown.<init>            */
+static jfieldID  isa_addrID;   /* java.net.InetSocketAddress.addr           */
+static jfieldID  isa_portID;   /* java.net.InetSocketAddress.port           */
+
+/* defined in SctpNet.c */
+jobject SockAddrToInetSocketAddress(JNIEnv* env, struct sockaddr* addr);
+
+/* use SocketChannelImpl's checkConnect implementation */
+extern jint Java_sun_nio_ch_SocketChannelImpl_checkConnect(JNIEnv* env,
+    jobject this, jobject fdo, jboolean block, jboolean ready);
+
+/*
+ * Class:     sun_nio_ch_SctpChannelImpl
+ * Method:    initIDs
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_sun_nio_ch_SctpChannelImpl_initIDs
+  (JNIEnv *env, jclass klass) {
+    jclass cls;
+
+    /* SctpMessageInfoImpl */
+    cls = (*env)->FindClass(env, MESSAGE_IMPL_CLASS);
+    CHECK_NULL(cls);
+    smi_class = (*env)->NewGlobalRef(env, cls);
+    CHECK_NULL(smi_class);
+    smi_ctrID = (*env)->GetMethodID(env, cls, "<init>",
+            "(ILjava/net/SocketAddress;IIZZI)V");
+    CHECK_NULL(smi_ctrID);
+
+    /* SctpResultContainer */
+    cls = (*env)->FindClass(env, RESULT_CONTAINER_CLASS);
+    CHECK_NULL(cls);
+    src_valueID = (*env)->GetFieldID(env, cls, "value", "Ljava/lang/Object;");
+    CHECK_NULL(src_valueID);
+    src_typeID = (*env)->GetFieldID(env, cls, "type", "I");
+    CHECK_NULL(src_typeID);
+
+    /* SctpSendFailed */
+    cls = (*env)->FindClass(env, SEND_FAILED_CLASS);
+    CHECK_NULL(cls);
+    ssf_class = (*env)->NewGlobalRef(env, cls);
+    CHECK_NULL(ssf_class);
+    ssf_ctrID = (*env)->GetMethodID(env, cls, "<init>",
+        "(ILjava/net/SocketAddress;Ljava/nio/ByteBuffer;II)V");
+    CHECK_NULL(ssf_ctrID);
+
+    /* SctpAssocChange */
+    cls = (*env)->FindClass(env, ASSOC_CHANGE_CLASS);
+    CHECK_NULL(cls);
+    sac_class = (*env)->NewGlobalRef(env, cls);
+    CHECK_NULL(sac_class);
+    sac_ctrID = (*env)->GetMethodID(env, cls, "<init>", "(IIII)V");
+    CHECK_NULL(sac_ctrID);
+
+    /* SctpPeerAddrChange */
+    cls = (*env)->FindClass(env, PEER_CHANGE_CLASS);
+    CHECK_NULL(cls);
+    spc_class = (*env)->NewGlobalRef(env, cls);
+    CHECK_NULL(spc_class);
+    spc_ctrID = (*env)->GetMethodID(env, cls, "<init>",
+            "(ILjava/net/SocketAddress;I)V");
+    CHECK_NULL(spc_ctrID);
+
+    /* sun.nio.ch.SctpShutdown */
+    cls = (*env)->FindClass(env, SHUTDOWN_CLASS);
+    CHECK_NULL(cls);
+    ss_class = (*env)->NewGlobalRef(env, cls);
+    CHECK_NULL(ss_class);
+    ss_ctrID = (*env)->GetMethodID(env, cls, "<init>", "(I)V");
+    CHECK_NULL(ss_ctrID);
+
+    /* InetSocketAddress */
+    cls = (*env)->FindClass(env, "java/net/InetSocketAddress");
+    CHECK_NULL(cls);
+    isa_addrID = (*env)->GetFieldID(env, cls, "addr", "Ljava/net/InetAddress;");
+    CHECK_NULL(isa_addrID);
+    isa_portID = (*env)->GetFieldID(env, cls, "port", "I");
+}
+
+void getControlData
+  (struct msghdr* msg, struct controlData* cdata) {
+    struct cmsghdr* cmsg;
+
+    for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+        if (cmsg->cmsg_level == IPPROTO_SCTP && cmsg->cmsg_type == SCTP_SNDRCV) {
+            struct sctp_sndrcvinfo *sri;
+
+            sri = (struct sctp_sndrcvinfo *) CMSG_DATA(cmsg);
+            cdata->assocId = sri->sinfo_assoc_id;
+            cdata->streamNumber = sri->sinfo_stream;
+            cdata->unordered = (sri->sinfo_flags & SCTP_UNORDERED) ? JNI_TRUE :
+                JNI_FALSE;
+            cdata->ppid = ntohl(sri->sinfo_ppid);
+
+            return;
+        }
+    }
+    return;
+}
+
+void setControlData
+  (struct msghdr* msg, struct controlData* cdata) {
+    struct cmsghdr* cmsg;
+    struct sctp_sndrcvinfo *sri;
+
+    cmsg = CMSG_FIRSTHDR(msg);
+    cmsg->cmsg_level = IPPROTO_SCTP;
+    cmsg->cmsg_type = SCTP_SNDRCV;
+    cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
+
+    /* Initialize the payload */
+    sri = (struct sctp_sndrcvinfo*) CMSG_DATA(cmsg);
+    memset(sri, 0, sizeof (*sri));
+
+    if (cdata->streamNumber > 0) {
+        sri->sinfo_stream = cdata->streamNumber;
+    }
+    if (cdata->assocId > 0) {
+        sri->sinfo_assoc_id = cdata->assocId;
+    }
+    if (cdata->unordered == JNI_TRUE) {
+        sri->sinfo_flags = sri->sinfo_flags | SCTP_UNORDERED;
+    }
+
+    if (cdata->ppid > 0) {
+        sri->sinfo_ppid = htonl(cdata->ppid);
+    }
+
+    /* Sum of the length of all control messages in the buffer. */
+    msg->msg_controllen = cmsg->cmsg_len;
+}
+
+// TODO: test: can create send failed without any data? if so need to
+// update API so that buffer can be null if no data.
+void handleSendFailed
+  (JNIEnv* env, int fd, jobject resultContainerObj, struct sctp_send_failed *ssf,
+   int read, jboolean isEOR, struct sockaddr* sap) {
+    jobject bufferObj = NULL, resultObj, isaObj;
+    char *addressP;
+    struct sctp_sndrcvinfo *sri;
+    int remaining, dataLength;
+
+    /* the actual undelivered message data is directly after the ssf */
+    int dataOffset = sizeof(struct sctp_send_failed);
+
+    sri = (struct sctp_sndrcvinfo*) &ssf->ssf_info;
+
+    /* the number of bytes remaining to be read in the sctp_send_failed notif*/
+    remaining = ssf->ssf_length - read;
+
+    /* the size of the actual undelivered message */
+    dataLength = ssf->ssf_length - dataOffset;
+
+    /* retrieved address from sockaddr */
+    isaObj = SockAddrToInetSocketAddress(env, sap);
+
+    /* data retrieved from sff_data */
+    if (dataLength > 0) {
+        struct iovec iov[1];
+        struct msghdr msg[1];
+        int rv, alreadyRead;
+        char *dataP = (char*) ssf;
+        dataP += dataOffset;
+
+        if ((addressP = malloc(dataLength)) == NULL) {
+            JNU_ThrowOutOfMemoryError(env, "handleSendFailed");
+            return;
+        }
+
+        memset(msg, 0, sizeof (*msg));
+        msg->msg_iov = iov;
+        msg->msg_iovlen = 1;
+
+        bufferObj = (*env)->NewDirectByteBuffer(env, addressP, dataLength);
+        CHECK_NULL(bufferObj);
+
+        alreadyRead = read - dataOffset;
+        if (alreadyRead > 0) {
+            memcpy(addressP, /*ssf->ssf_data*/ dataP, alreadyRead);
+            iov->iov_base = addressP + alreadyRead;
+            iov->iov_len = dataLength - alreadyRead;
+        } else {
+            iov->iov_base = addressP;
+            iov->iov_len = dataLength;
+        }
+
+        if (remaining > 0) {
+            if ((rv = recvmsg(fd, msg, 0)) < 0) {
+                fprintf(stdout, "\nNative: handleSFN: recvmsg failed: errno = %d  ", errno);
+                handleSocketError(env, errno);
+                return;
+            }
+
+            if (rv != (dataLength - alreadyRead) || !(msg->msg_flags & MSG_EOR)) {
+                //TODO: assert false: "should not reach here";
+                return;
+            }
+            // TODO: Set and document (in API) buffers position.
+        }
+    }
+
+    /* create SctpSendFailed */
+    resultObj = (*env)->NewObject(env, ssf_class, ssf_ctrID, ssf->ssf_assoc_id,
+            sri->sinfo_stream, ssf->ssf_error, isaObj, bufferObj);
+    CHECK_NULL(resultObj);
+    (*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj);
+    (*env)->SetIntField(env, resultContainerObj, src_typeID,
+            sun_nio_ch_SctpResultContainer_SEND_FAILED);
+}
+
+void handleAssocChange
+  (JNIEnv* env, jobject resultContainerObj, struct sctp_assoc_change *sac) {
+    jobject resultObj;
+    int state = 0;
+
+    switch (sac->sac_state) {
+        case SCTP_COMM_UP :
+            state = sun_nio_ch_SctpAssocChange_SCTP_COMM_UP;
+            break;
+        case SCTP_COMM_LOST :
+            state = sun_nio_ch_SctpAssocChange_SCTP_COMM_LOST;
+            break;
+        case SCTP_RESTART :
+            state = sun_nio_ch_SctpAssocChange_SCTP_RESTART;
+            break;
+        case SCTP_SHUTDOWN_COMP :
+            state = sun_nio_ch_SctpAssocChange_SCTP_SHUTDOWN;
+            break;
+        case SCTP_CANT_STR_ASSOC :
+            state = sun_nio_ch_SctpAssocChange_SCTP_CANT_START;
+    }
+
+    /* create SctpAssociationChanged */
+    resultObj = (*env)->NewObject(env, sac_class, sac_ctrID, sac->sac_assoc_id,
+        state, sac->sac_outbound_streams, sac->sac_inbound_streams);
+    CHECK_NULL(resultObj);
+    (*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj);
+    (*env)->SetIntField(env, resultContainerObj, src_typeID,
+            sun_nio_ch_SctpResultContainer_ASSOCIATION_CHANGED);
+}
+
+void handleShutdown
+  (JNIEnv* env, jobject resultContainerObj, struct sctp_shutdown_event* sse) {
+    /* create SctpShutdown */
+    jobject resultObj = (*env)->NewObject(env, ss_class, ss_ctrID, sse->sse_assoc_id);
+    CHECK_NULL(resultObj);
+    (*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj);
+    (*env)->SetIntField(env, resultContainerObj, src_typeID,
+            sun_nio_ch_SctpResultContainer_SHUTDOWN);
+}
+
+void handlePeerAddrChange
+  (JNIEnv* env, jobject resultContainerObj, struct sctp_paddr_change* spc) {
+    int event = 0;
+    jobject addressObj, resultObj;
+    unsigned int state = spc->spc_state;
+
+    switch (state) {
+        case SCTP_ADDR_AVAILABLE :
+            event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_AVAILABLE;
+            break;
+        case SCTP_ADDR_UNREACHABLE :
+            event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_UNREACHABLE;
+            break;
+        case SCTP_ADDR_REMOVED :
+            event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_REMOVED;
+            break;
+        case SCTP_ADDR_ADDED :
+            event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_ADDED;
+            break;
+        case SCTP_ADDR_MADE_PRIM :
+            event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_MADE_PRIM;
+#ifdef __linux__  /* Solaris currently doesn't support SCTP_ADDR_CONFIRMED */
+            break;
+        case SCTP_ADDR_CONFIRMED :
+            event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_CONFIRMED;
+#endif  /* __linux__ */
+    }
+
+    addressObj = SockAddrToInetSocketAddress(env, (struct sockaddr*)&spc->spc_aaddr);
+
+    /* create SctpPeerAddressChanged */
+    resultObj = (*env)->NewObject(env, spc_class, spc_ctrID, spc->spc_assoc_id,
+            addressObj, event);
+    CHECK_NULL(resultObj);
+    (*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj);
+    (*env)->SetIntField(env, resultContainerObj, src_typeID,
+            sun_nio_ch_SctpResultContainer_PEER_ADDRESS_CHANGED);
+}
+
+void handleUninteresting
+  (union sctp_notification *snp) {
+    //fprintf(stdout,"\nNative: handleUninterestingNotification: Receive notification type [%u]", snp->sn_header.sn_type);
+}
+
+/**
+ * Handle notifications from the SCTP stack.
+ * Returns JNI_TRUE if the notification is one that is of interest to the
+ * Java API, otherwise JNI_FALSE.
+ */
+jboolean handleNotification
+  (JNIEnv* env, int fd, jobject resultContainerObj, union sctp_notification* snp,
+   int read, jboolean isEOR, struct sockaddr* sap) {
+    switch (snp->sn_header.sn_type) {
+        case SCTP_SEND_FAILED:
+            handleSendFailed(env, fd, resultContainerObj, &snp->sn_send_failed,
+                    read, isEOR, sap);
+            return JNI_TRUE;
+        case SCTP_ASSOC_CHANGE:
+            handleAssocChange(env, resultContainerObj, &snp->sn_assoc_change);
+            return JNI_TRUE;
+        case SCTP_SHUTDOWN_EVENT:
+            handleShutdown(env, resultContainerObj, &snp->sn_shutdown_event);
+            return JNI_TRUE;
+        case SCTP_PEER_ADDR_CHANGE:
+            handlePeerAddrChange(env, resultContainerObj, &snp->sn_paddr_change);
+            return JNI_TRUE;
+        default :
+            /* the Java API is not interested in this event, maybe we are? */
+            handleUninteresting(snp);
+    }
+    return JNI_FALSE;
+}
+
+void handleMessage
+  (JNIEnv* env, jobject resultContainerObj, struct msghdr* msg,int read,
+   jboolean isEOR, struct sockaddr* sap) {
+    jobject isa, resultObj;
+    struct controlData cdata[1];
+
+    if (read == 0) {
+        /* we reached EOF */
+        read = -1;
+    }
+
+    isa = SockAddrToInetSocketAddress(env, sap);
+    getControlData(msg, cdata);
+
+    /* create SctpMessageInfoImpl */
+    resultObj = (*env)->NewObject(env, smi_class, smi_ctrID, cdata->assocId,
+                                  isa, read, cdata->streamNumber,
+                                  isEOR ? JNI_TRUE : JNI_FALSE,
+                                  cdata->unordered, cdata->ppid);
+    CHECK_NULL(resultObj);
+    (*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj);
+    (*env)->SetIntField(env, resultContainerObj, src_typeID,
+                        sun_nio_ch_SctpResultContainer_MESSAGE);
+}
+
+/*
+ * Class:     sun_nio_ch_SctpChannelImpl
+ * Method:    receive0
+ * Signature: (ILsun/nio/ch/SctpResultContainer;JI)I
+ */
+JNIEXPORT jint JNICALL Java_sun_nio_ch_SctpChannelImpl_receive0
+  (JNIEnv *env, jclass klass, jint fd, jobject resultContainerObj,
+   jlong address, jint length) {
+    SOCKADDR sa;
+    int sa_len = sizeof(sa);
+    ssize_t rv = 0;
+    jlong *addr = jlong_to_ptr(address);
+    struct iovec iov[1];
+    struct msghdr msg[1];
+    char cbuf[CMSG_SPACE(sizeof (struct sctp_sndrcvinfo))];
+
+    /* Set up the msghdr structure for receiving */
+    memset(msg, 0, sizeof (*msg));
+    msg->msg_name = &sa;
+    msg->msg_namelen = sa_len;
+    iov->iov_base = addr;
+    iov->iov_len = length;
+    msg->msg_iov = iov;
+    msg->msg_iovlen = 1;
+    msg->msg_control = cbuf;
+    msg->msg_controllen = sizeof(cbuf);
+    msg->msg_flags = 0;
+
+    do {
+        if ((rv = recvmsg(fd, msg, 0)) < 0) {
+            if (errno == EWOULDBLOCK) {
+                return IOS_UNAVAILABLE;
+            } else if (errno == EINTR) {
+                return IOS_INTERRUPTED;
+
+#ifdef __linux__
+            } else if (errno == ENOTCONN) {
+                /* ENOTCONN when EOF reached */
+                rv = 0;
+                /* there will be no control data */
+                msg->msg_controllen = 0;
+#endif /* __linux__ */
+
+            } else {
+                handleSocketError(env, errno);
+                return 0;
+            }
+        }
+
+        if (msg->msg_flags & MSG_NOTIFICATION) {
+            char *bufp = (char*)addr;
+            union sctp_notification *snp;
+
+            if (!(msg->msg_flags & MSG_EOR) && length < NOTIFICATION_BUFFER_SIZE) {
+                char buf[NOTIFICATION_BUFFER_SIZE];
+                int rvSAVE = rv;
+                memcpy(buf, addr, rv);
+                iov->iov_base = buf + rv;
+                iov->iov_len = NOTIFICATION_BUFFER_SIZE - rv;
+                if ((rv = recvmsg(fd, msg, 0)) < 0) {
+                    handleSocketError(env, errno);
+                    return 0;
+                }
+                bufp = buf;
+                rv += rvSAVE;
+            }
+            snp = (union sctp_notification *) bufp;
+            if (handleNotification(env, fd, resultContainerObj, snp, rv,
+                                   (msg->msg_flags & MSG_EOR),
+                                   (struct sockaddr*)&sa ) == JNI_TRUE) {
+                /* We have received a notification that is of interest to
+                   to the Java API. The appropriate notification will be
+                   set in the result container. */
+                return 0;
+            }
+
+            // set iov back to addr, and reset msg_controllen
+            iov->iov_base = addr;
+            iov->iov_len = length;
+            msg->msg_control = cbuf;
+            msg->msg_controllen = sizeof(cbuf);
+        }
+    } while (msg->msg_flags & MSG_NOTIFICATION);
+
+    handleMessage(env, resultContainerObj, msg, rv,
+            (msg->msg_flags & MSG_EOR), (struct sockaddr*)&sa);
+    return rv;
+}
+
+/*
+ * Class:     sun_nio_ch_SctpChannelImpl
+ * Method:    send0
+ * Signature: (IJILjava/net/SocketAddress;IIZI)I
+ */
+JNIEXPORT jint JNICALL Java_sun_nio_ch_SctpChannelImpl_send0
+  (JNIEnv *env, jclass klass, jint fd, jlong address, jint length,
+   jobject saTarget, jint assocId, jint streamNumber, jboolean unordered,
+   jint ppid) {
+    SOCKADDR sa;
+    int sa_len = sizeof(sa);
+    ssize_t rv = 0;
+    jlong *addr = jlong_to_ptr(address);
+    struct iovec iov[1];
+    struct msghdr msg[1];
+    int cbuf_size = CMSG_SPACE(sizeof (struct sctp_sndrcvinfo));
+    char cbuf[CMSG_SPACE(sizeof (struct sctp_sndrcvinfo))];
+    struct controlData cdata[1];
+
+    /* SctpChannel:
+     *    saTarget may contain the preferred address or NULL to use primary,
+     *    assocId will always be -1
+     * SctpMultiChannell:
+     *    Setup new association, saTarget will contain address, assocId = -1
+     *    Association already existing, assocId != -1, saTarget = preferred addr
+     */
+    if (saTarget != NULL /*&& assocId <= 0*/) {
+
+        jobject targetAddress = (*env)->GetObjectField(env, saTarget, isa_addrID);
+        jint targetPort = (*env)->GetIntField(env, saTarget, isa_portID);
+
+        if (NET_InetAddressToSockaddr(env, targetAddress, targetPort,
+                                      (struct sockaddr *)&sa,
+                                      &sa_len, JNI_TRUE) != 0) {
+            return IOS_THROWN;
+        }
+    } else {
+        memset(&sa, '\x0', sa_len);
+        sa_len = 0;
+    }
+
+    /* Set up the msghdr structure for sending */
+    memset(msg, 0, sizeof (*msg));
+    memset(cbuf, 0, cbuf_size);
+    msg->msg_name = &sa;
+    msg->msg_namelen = sa_len;
+    iov->iov_base = addr;
+    iov->iov_len = length;
+    msg->msg_iov = iov;
+    msg->msg_iovlen = 1;
+    msg->msg_control = cbuf;
+    msg->msg_controllen = cbuf_size;
+    msg->msg_flags = 0;
+
+    cdata->streamNumber = streamNumber;
+    cdata->assocId = assocId;
+    cdata->unordered = unordered;
+    cdata->ppid = ppid;
+    setControlData(msg, cdata);
+
+    if ((rv = sendmsg(fd, msg, 0)) < 0) {
+        if (errno == EWOULDBLOCK) {
+            return IOS_UNAVAILABLE;
+        } else if (errno == EINTR) {
+            return IOS_INTERRUPTED;
+        } else if (errno == EPIPE) {
+            JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
+                            "Socket is shutdown for writing");
+        } else {
+            handleSocketError(env, errno);
+            return 0;
+        }
+    }
+
+    return rv;
+}
+
+/*
+ * Class:     sun_nio_ch_SctpChannelImpl
+ * Method:    checkConnect
+ * Signature: (Ljava/io/FileDescriptor;ZZ)I
+ */
+JNIEXPORT jint JNICALL Java_sun_nio_ch_SctpChannelImpl_checkConnect
+  (JNIEnv* env, jobject this, jobject fdo, jboolean block, jboolean ready) {
+    return Java_sun_nio_ch_SocketChannelImpl_checkConnect(env, this,
+                                                          fdo, block, ready);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/native/sun/nio/ch/SctpNet.c	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,609 @@
+/*
+ * Copyright 2009 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 <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+
+#include "Sctp.h"
+#include "jni.h"
+#include "jni_util.h"
+#include "nio_util.h"
+#include "nio.h"
+#include "net_util.h"
+#include "net_util_md.h"
+#include "sun_nio_ch_SctpNet.h"
+#include "sun_nio_ch_SctpStdSocketOption.h"
+
+static jclass isaCls = 0;
+static jmethodID isaCtrID = 0;
+
+static const char* nativeSctpLib = "libsctp.so.1";
+static jboolean funcsLoaded = JNI_FALSE;
+
+JNIEXPORT jint JNICALL JNI_OnLoad
+  (JavaVM *vm, void *reserved) {
+    return JNI_VERSION_1_2;
+}
+
+/**
+ * Loads the native sctp library that contains the socket extension
+ * functions, as well as locating the individual functions.
+ * There will be a pending exception if this method returns false.
+ */
+jboolean loadSocketExtensionFuncs
+  (JNIEnv* env) {
+    if (dlopen(nativeSctpLib, RTLD_GLOBAL | RTLD_LAZY) == NULL) {
+        JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
+              dlerror());
+        return JNI_FALSE;
+    }
+
+    if ((nio_sctp_getladdrs = (sctp_getladdrs_func*)
+            dlsym(RTLD_DEFAULT, "sctp_getladdrs")) == NULL) {
+        JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
+              dlerror());
+        return JNI_FALSE;
+    }
+
+    if ((nio_sctp_freeladdrs = (sctp_freeladdrs_func*)
+            dlsym(RTLD_DEFAULT, "sctp_freeladdrs")) == NULL) {
+        JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
+              dlerror());
+        return JNI_FALSE;
+    }
+
+    if ((nio_sctp_getpaddrs = (sctp_getpaddrs_func*)
+            dlsym(RTLD_DEFAULT, "sctp_getpaddrs")) == NULL) {
+        JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
+              dlerror());
+        return JNI_FALSE;
+    }
+
+    if ((nio_sctp_freepaddrs = (sctp_freepaddrs_func*)
+            dlsym(RTLD_DEFAULT, "sctp_freepaddrs")) == NULL) {
+        JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
+              dlerror());
+        return JNI_FALSE;
+    }
+
+    if ((nio_sctp_bindx = (sctp_bindx_func*)
+            dlsym(RTLD_DEFAULT, "sctp_bindx")) == NULL) {
+        JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
+              dlerror());
+        return JNI_FALSE;
+    }
+
+    funcsLoaded = JNI_TRUE;
+    return JNI_TRUE;
+}
+
+/*
+ * Class:     sun_nio_ch_SctpNet
+ * Method:    socket0
+ * Signature: (Z)I
+ */
+JNIEXPORT jint JNICALL Java_sun_nio_ch_SctpNet_socket0
+  (JNIEnv *env, jclass klass, jboolean oneToOne) {
+    int fd;
+    struct sctp_event_subscribe event;
+
+    /* Try to load the socket API extension functions */
+    if (!funcsLoaded && !loadSocketExtensionFuncs(env)) {
+        return 0;
+    }
+
+    fd = socket(ipv6_available() ? AF_INET6 : AF_INET,
+            (oneToOne ? SOCK_STREAM : SOCK_SEQPACKET), IPPROTO_SCTP);
+
+    if (fd < 0) {
+        return handleSocketError(env, errno);
+    }
+
+    /* Enable events */
+    memset(&event, 0, sizeof(event));
+    event.sctp_data_io_event = 1;
+    event.sctp_association_event = 1;
+    event.sctp_address_event = 1;
+    event.sctp_send_failure_event = 1;
+    //event.sctp_peer_error_event = 1;
+    event.sctp_shutdown_event = 1;
+    //event.sctp_partial_delivery_event = 1;
+    //event.sctp_adaptation_layer_event = 1;
+    if (setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(event)) != 0) {
+       handleSocketError(env, errno);
+    }
+    return fd;
+}
+
+/*
+ * Class:     sun_nio_ch_SctpNet
+ * Method:    bindx
+ * Signature: (I[Ljava/net/InetAddress;IIZ)V
+ */
+JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_bindx
+  (JNIEnv *env, jclass klass, jint fd, jobjectArray addrs, jint port,
+   jint addrsLength, jboolean add, jboolean preferIPv6) {
+    SOCKADDR *sap, *tmpSap;
+    int i, sa_len = sizeof(SOCKADDR);
+    jobject ia;
+
+    if (addrsLength < 1)
+        return;
+
+    if ((sap = calloc(addrsLength,  sa_len)) == NULL) {
+          JNU_ThrowOutOfMemoryError(env, "heap allocation failure");
+        return;
+    }
+
+    tmpSap = sap;
+    for (i=0; i<addrsLength; i++) {
+        ia = (*env)->GetObjectArrayElement(env, addrs, i);
+        if (NET_InetAddressToSockaddr(env, ia, port, (struct sockaddr*)tmpSap,
+                                      &sa_len, preferIPv6) != 0) {
+            free(sap);
+            return;
+        }
+        tmpSap++;
+    }
+
+    if (nio_sctp_bindx(fd, (void*)sap, addrsLength, add ? SCTP_BINDX_ADD_ADDR :
+                       SCTP_BINDX_REM_ADDR) != 0) {
+        handleSocketError(env, errno);
+    }
+
+    free(sap);
+}
+
+void initializeISA
+  (JNIEnv* env) {
+    if (isaCls == 0) {
+        jclass c = (*env)->FindClass(env, "java/net/InetSocketAddress");
+        CHECK_NULL(c);
+        isaCls = (*env)->NewGlobalRef(env, c);
+        CHECK_NULL(isaCls);
+        (*env)->DeleteLocalRef(env, c);
+        isaCtrID = (*env)->GetMethodID(env, isaCls, "<init>",
+                                     "(Ljava/net/InetAddress;I)V");
+    }
+}
+
+jobject SockAddrToInetSocketAddress
+  (JNIEnv *env, struct sockaddr* sap) {
+    int port = 0;
+
+    jobject ia = NET_SockaddrToInetAddress(env, sap, &port);
+    if (ia == NULL)
+        return NULL;
+
+    if (isaCls == 0) {
+        initializeISA(env);
+        CHECK_NULL_RETURN(isaCls, NULL);
+    }
+
+    return (*env)->NewObject(env, isaCls, isaCtrID, ia, port);
+}
+
+/*
+ * Class:     sun_nio_ch_SctpNet
+ * Method:    getLocalAddresses0
+ * Signature: (I)[Ljava/net/SocketAddress;
+ */
+JNIEXPORT jobjectArray JNICALL Java_sun_nio_ch_SctpNet_getLocalAddresses0
+  (JNIEnv *env, jclass klass, jint fd) {
+    void *addr_buf, *laddr;
+    struct sockaddr* sap;
+    int i, addrCount;
+    jobjectArray isaa;
+
+#ifdef __solaris__
+    if ((addrCount = nio_sctp_getladdrs(fd, 0, (void **)&addr_buf)) == -1) {
+#else /* __linux__ */
+    if ((addrCount = nio_sctp_getladdrs(fd, 0, (struct sockaddr **)&addr_buf)) == -1) {
+#endif
+        handleSocketError(env, errno);
+        return NULL;
+    }
+
+    if (addrCount < 1)
+        return NULL;
+
+    if (isaCls == 0) {
+        initializeISA(env);
+        CHECK_NULL_RETURN(isaCls, NULL);
+    }
+
+    isaa = (*env)->NewObjectArray(env, addrCount, isaCls, NULL);
+    if (isaa == NULL) {
+        nio_sctp_freeladdrs(addr_buf);
+        return NULL;
+    }
+
+    laddr = addr_buf;
+    for (i=0; i<addrCount; i++) {
+        int port = 0;
+        jobject isa = NULL, ia;
+        sap = (struct sockaddr*)addr_buf;
+        ia = NET_SockaddrToInetAddress(env, sap, &port);
+        if (ia != NULL)
+            isa = (*env)->NewObject(env, isaCls, isaCtrID, ia, port);
+        if (isa != NULL)
+            (*env)->SetObjectArrayElement(env, isaa, i, isa);
+
+        if (sap->sa_family == AF_INET)
+            addr_buf = ((struct sockaddr_in*)addr_buf) + 1;
+        else
+            addr_buf = ((struct sockaddr_in6*)addr_buf) + 1;
+    }
+
+    nio_sctp_freeladdrs(laddr);
+    return isaa;
+}
+
+jobjectArray getRemoteAddresses
+  (JNIEnv *env, jint fd, sctp_assoc_t id) {
+    void *addr_buf, *paddr;
+    struct sockaddr* sap;
+    int i, addrCount;
+    jobjectArray isaa;
+
+#if __solaris__
+    if ((addrCount = nio_sctp_getpaddrs(fd, id, (void **)&addr_buf)) == -1) {
+#else /* __linux__ */
+    if ((addrCount = nio_sctp_getpaddrs(fd, id, (struct sockaddr**)&addr_buf)) == -1) {
+#endif
+        handleSocketError(env, errno);
+        return NULL;
+    }
+
+    if (addrCount < 1)
+        return NULL;
+
+    if (isaCls == 0) {
+        initializeISA(env);
+        CHECK_NULL_RETURN(isaCls, NULL);
+    }
+
+    isaa = (*env)->NewObjectArray(env, addrCount, isaCls, NULL);
+    if (isaa == NULL) {
+        nio_sctp_freepaddrs(addr_buf);
+        return NULL;
+    }
+
+    paddr = addr_buf;
+    for (i=0; i<addrCount; i++) {
+        jobject ia, isa = NULL;
+        int port;
+        sap = (struct sockaddr*)addr_buf;
+        ia = NET_SockaddrToInetAddress(env, sap, &port);
+        if (ia != NULL)
+            isa = (*env)->NewObject(env, isaCls, isaCtrID, ia, port);
+        if (isa != NULL)
+            (*env)->SetObjectArrayElement(env, isaa, i, isa);
+
+        if (sap->sa_family == AF_INET)
+            addr_buf = ((struct sockaddr_in*)addr_buf) + 1;
+        else
+            addr_buf = ((struct sockaddr_in6*)addr_buf) + 1;
+    }
+
+    nio_sctp_freepaddrs(paddr);
+
+    return isaa;
+}
+
+ /*
+ * Class:     sun_nio_ch_SctpNet
+ * Method:    getRemoteAddresses0
+ * Signature: (II)[Ljava/net/SocketAddress;
+ */
+JNIEXPORT jobjectArray JNICALL Java_sun_nio_ch_SctpNet_getRemoteAddresses0
+  (JNIEnv *env, jclass klass, jint fd, jint assocId) {
+    return getRemoteAddresses(env, fd, assocId);
+}
+
+/* Map the Java level option to the native level */
+int mapSocketOption
+  (jint cmd, int *level, int *optname) {
+    static struct {
+        jint cmd;
+        int level;
+        int optname;
+    } const opts[] = {
+        { sun_nio_ch_SctpStdSocketOption_SCTP_DISABLE_FRAGMENTS,   IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS },
+        { sun_nio_ch_SctpStdSocketOption_SCTP_EXPLICIT_COMPLETE,   IPPROTO_SCTP, SCTP_EXPLICIT_EOR },
+        { sun_nio_ch_SctpStdSocketOption_SCTP_FRAGMENT_INTERLEAVE, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE },
+        { sun_nio_ch_SctpStdSocketOption_SCTP_NODELAY,             IPPROTO_SCTP, SCTP_NODELAY },
+        { sun_nio_ch_SctpStdSocketOption_SO_SNDBUF,                SOL_SOCKET,   SO_SNDBUF },
+        { sun_nio_ch_SctpStdSocketOption_SO_RCVBUF,                SOL_SOCKET,   SO_RCVBUF },
+        { sun_nio_ch_SctpStdSocketOption_SO_LINGER,                SOL_SOCKET,   SO_LINGER } };
+
+    int i;
+    for (i=0; i<(int)(sizeof(opts) / sizeof(opts[0])); i++) {
+        if (cmd == opts[i].cmd) {
+            *level = opts[i].level;
+            *optname = opts[i].optname;
+            return 0;
+        }
+    }
+
+    /* not found */
+    return -1;
+}
+
+/*
+ * Class:     sun_nio_ch_SctpNet
+ * Method:    setIntOption0
+ * Signature: (III)V
+ */
+JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_setIntOption0
+  (JNIEnv *env, jclass klass, jint fd, jint opt, int arg) {
+    int klevel, kopt;
+    int result;
+    struct linger linger;
+    void *parg;
+    int arglen;
+
+    if (mapSocketOption(opt, &klevel, &kopt) < 0) {
+        JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "Unsupported socket option");
+        return;
+    }
+
+    if (opt == sun_nio_ch_SctpStdSocketOption_SO_LINGER) {
+        parg = (void *)&linger;
+        arglen = sizeof(linger);
+        if (arg >= 0) {
+            linger.l_onoff = 1;
+            linger.l_linger = arg;
+        } else {
+            linger.l_onoff = 0;
+            linger.l_linger = 0;
+        }
+    } else {
+        parg = (void *)&arg;
+        arglen = sizeof(arg);
+    }
+
+    if (setsockopt(fd, klevel, kopt, parg, arglen) < 0) {
+        JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "sun_nio_ch_SctpNet.setIntOption0");
+    }
+}
+
+/*
+ * Class:     sun_nio_ch_SctpNet
+ * Method:    getIntOption0
+ * Signature: (II)I
+ */
+JNIEXPORT int JNICALL Java_sun_nio_ch_SctpNet_getIntOption0
+  (JNIEnv *env, jclass klass, jint fd, jint opt) {
+    int klevel, kopt;
+    int result;
+    struct linger linger;
+    void *arg;
+    unsigned int arglen;
+
+    if (mapSocketOption(opt, &klevel, &kopt) < 0) {
+        JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "Unsupported socket option");
+        return -1;
+    }
+
+    if (opt == sun_nio_ch_SctpStdSocketOption_SO_LINGER) {
+        arg = (void *)&linger;
+        arglen = sizeof(linger);
+    } else {
+        arg = (void *)&result;
+        arglen = sizeof(result);
+    }
+
+    if (getsockopt(fd, klevel, kopt, arg, &arglen) < 0) {
+        JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "sun.nio.ch.Net.getIntOption");
+        return -1;
+    }
+
+    if (opt == sun_nio_ch_SctpStdSocketOption_SO_LINGER)
+        return linger.l_onoff ? linger.l_linger : -1;
+    else
+        return result;
+}
+
+/*
+ * Class:     sun_nio_ch_SctpNet
+ * Method:    getPrimAddrOption0
+ * Signature: (II)Ljava/net/SocketAddress;
+ */
+JNIEXPORT jobject JNICALL Java_sun_nio_ch_SctpNet_getPrimAddrOption0
+  (JNIEnv *env, jclass klass, jint fd, jint assocId) {
+    struct sctp_setprim prim;
+    struct sockaddr_storage ss;
+    int ss_len = sizeof(ss);
+    unsigned int prim_len = sizeof(prim);
+
+    prim.ssp_assoc_id = assocId;
+    prim.ssp_addr = ss;
+
+    if (getsockopt(fd, IPPROTO_SCTP, SCTP_PRIMARY_ADDR, &prim, &prim_len) < 0) {
+        JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "sun.nio.ch.SctpNet.getPrimAddrOption0");
+        return NULL;
+    }
+
+    return SockAddrToInetSocketAddress(env, (struct sockaddr*)&ss);
+}
+
+/*
+ * Class:     sun_nio_ch_SctpNet
+ * Method:    setPrimAddrOption0
+ * Signature: (IILjava/net/InetAddress;I)V
+ */
+JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_setPrimAddrOption0
+  (JNIEnv *env, jclass klass, jint fd, jint assocId, jobject iaObj, jint port) {
+    struct sctp_setprim prim;
+    struct sockaddr_storage ss;
+    int ss_len = sizeof(ss);
+
+    if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&ss,
+                                  &ss_len, JNI_TRUE) != 0) {
+        return;
+    }
+
+    prim.ssp_assoc_id = assocId;
+    prim.ssp_addr = ss;
+
+    if (setsockopt(fd, IPPROTO_SCTP, SCTP_PRIMARY_ADDR, &prim, sizeof(prim)) < 0) {
+        JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "sun.nio.ch.SctpNet.setPrimAddrOption0");
+    }
+}
+
+/*
+ * Class:     sun_nio_ch_SctpNet
+ * Method:    setPeerPrimAddrOption0
+ * Signature: (IILjava/net/InetAddress;I)V
+ */
+JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_setPeerPrimAddrOption0
+  (JNIEnv *env, jclass klass, jint fd, jint assocId, jobject iaObj, jint port) {
+    struct sctp_setpeerprim prim;
+    struct sockaddr_storage ss;
+    int ss_len = sizeof(ss);
+
+    if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&ss,
+                                  &ss_len, JNI_TRUE) != 0) {
+        return;
+    }
+
+    prim.sspp_assoc_id = assocId;
+    prim.sspp_addr = ss;
+
+    if (setsockopt(fd, IPPROTO_SCTP, SCTP_SET_PEER_PRIMARY_ADDR, &prim,
+            sizeof(prim)) < 0) {
+        JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "sun.nio.ch.SctpNet.setPeerPrimAddrOption0");
+    }
+}
+
+/*
+ * Class:     sun_nio_ch_SctpNet
+ * Method:    getInitMsgOption0
+ * Signature: (I[I)V
+ */
+JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_getInitMsgOption0
+  (JNIEnv *env, jclass klass, jint fd, jintArray retVal) {
+    struct sctp_initmsg sctp_initmsg;
+    unsigned int sim_len = sizeof(sctp_initmsg);
+    int vals[2];
+
+    if (getsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &sctp_initmsg,
+            &sim_len) < 0) {
+        JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "sun.nio.ch.SctpNet.getInitMsgOption0");
+        return;
+    }
+
+    vals[0] = sctp_initmsg.sinit_max_instreams;
+    vals[1] = sctp_initmsg.sinit_num_ostreams;
+    (*env)->SetIntArrayRegion(env, retVal, 0, 2, vals);
+}
+
+/*
+ * Class:     sun_nio_ch_SctpNet
+ * Method:    setInitMsgOption0
+ * Signature: (III)V
+ */
+JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_setInitMsgOption0
+  (JNIEnv *env, jclass klass, jint fd, jint inArg, jint outArg) {
+    struct sctp_initmsg sctp_initmsg;
+
+    sctp_initmsg.sinit_max_instreams = (unsigned int)inArg;
+    sctp_initmsg.sinit_num_ostreams = (unsigned int)outArg;
+    sctp_initmsg.sinit_max_attempts = 0;  // default
+    sctp_initmsg.sinit_max_init_timeo = 0;  // default
+
+    if (setsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &sctp_initmsg,
+          sizeof(sctp_initmsg)) < 0) {
+        JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "sun.nio.ch.SctpNet.setInitMsgOption0");
+    }
+}
+
+/*
+ * Class:     sun_nio_ch_SctpNet
+ * Method:    shutdown0
+ * Signature: (II)V
+ */
+JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_shutdown0
+  (JNIEnv *env, jclass klass, jint fd, jint assocId) {
+    int rv;
+    struct msghdr msg[1];
+    struct iovec iov[1];
+    int cbuf_size = CMSG_SPACE(sizeof (struct sctp_sndrcvinfo));
+    char cbuf[CMSG_SPACE(sizeof (struct sctp_sndrcvinfo))];
+    struct cmsghdr* cmsg;
+    struct sctp_sndrcvinfo *sri;
+
+    /* SctpSocketChannel */
+    if (assocId < 0) {
+        shutdown(fd, SHUT_WR);
+        return;
+    }
+
+    memset(msg, 0, sizeof (*msg));
+    memset(cbuf, 0, cbuf_size);
+    msg->msg_name = NULL;
+    msg->msg_namelen = 0;
+    iov->iov_base = NULL;
+    iov->iov_len = 0;
+    msg->msg_iov = iov;
+    msg->msg_iovlen = 1;
+    msg->msg_control = cbuf;
+    msg->msg_controllen = cbuf_size;
+    msg->msg_flags = 0;
+
+    cmsg = CMSG_FIRSTHDR(msg);
+    cmsg->cmsg_level = IPPROTO_SCTP;
+    cmsg->cmsg_type = SCTP_SNDRCV;
+    cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
+
+    /* Initialize the payload: */
+    sri = (struct sctp_sndrcvinfo*) CMSG_DATA(cmsg);
+    memset(sri, 0, sizeof (*sri));
+
+    if (assocId > 0) {
+        sri->sinfo_assoc_id = assocId;
+    }
+
+    sri->sinfo_flags = sri->sinfo_flags | SCTP_EOF;
+
+    /* Sum of the length of all control messages in the buffer. */
+    msg->msg_controllen = cmsg->cmsg_len;
+
+    if ((rv = sendmsg(fd, msg, 0)) < 0) {
+        handleSocketError(env, errno);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/native/sun/nio/ch/SctpServerChannelImpl.c	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2009 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 "sun_nio_ch_SctpServerChannelImpl.h"
+
+extern void Java_sun_nio_ch_ServerSocketChannelImpl_initIDs(JNIEnv* env,
+    jclass c);
+
+extern jint Java_sun_nio_ch_ServerSocketChannelImpl_accept0(JNIEnv* env,
+    jobject this, jobject ssfdo, jobject newfdo, jobjectArray isaa);
+
+/*
+ * Class:     sun_nio_ch_SctpServerChannelImpl
+ * Method:    initIDs
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_sun_nio_ch_SctpServerChannelImpl_initIDs
+  (JNIEnv* env, jclass c) {
+    Java_sun_nio_ch_ServerSocketChannelImpl_initIDs(env, c);
+}
+
+/*
+ * Class:     sun_nio_ch_SctpServerChannelImpl
+ * Method:    accept0
+ * Signature: (Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;[Ljava/net/InetSocketAddress;)I
+ */
+JNIEXPORT jint JNICALL Java_sun_nio_ch_SctpServerChannelImpl_accept0
+  (JNIEnv* env, jobject this, jobject ssfdo, jobject newfdo, jobjectArray isaa) {
+    return Java_sun_nio_ch_ServerSocketChannelImpl_accept0(env, this,
+                                                           ssfdo, newfdo, isaa);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/windows/classes/sun/nio/ch/SctpChannelImpl.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2009 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.SocketAddress;
+import java.net.InetAddress;
+import java.io.IOException;
+import java.util.Set;
+import java.nio.ByteBuffer;
+import java.nio.channels.spi.SelectorProvider;
+import com.sun.nio.sctp.Association;
+import com.sun.nio.sctp.MessageInfo;
+import com.sun.nio.sctp.NotificationHandler;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpSocketOption;
+
+/**
+ * Unimplemented.
+ */
+public class SctpChannelImpl extends SctpChannel
+{
+    private static final String message = "SCTP not supported on this platform";
+
+    public SctpChannelImpl(SelectorProvider provider) {
+        super(provider);
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public Association association() {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public SctpChannel bind(SocketAddress local)
+                            throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public SctpChannel bindAddress(InetAddress address)
+         throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public SctpChannel unbindAddress(InetAddress address)
+         throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public boolean connect(SocketAddress remote) throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public boolean connect(SocketAddress remote, int maxOutStreams,
+       int maxInStreams) throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public boolean isConnectionPending() {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public boolean finishConnect() throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public Set<SocketAddress> getAllLocalAddresses()
+            throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public Set<SocketAddress> getRemoteAddresses()
+            throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public SctpChannel shutdown() throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public <T> T getOption(SctpSocketOption<T> name)
+            throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public <T> SctpChannel setOption(SctpSocketOption<T> name, T value)
+        throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public Set<SctpSocketOption<?>> supportedOptions() {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public <T> MessageInfo receive(ByteBuffer dst, T attachment,
+            NotificationHandler<T> handler) throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public int send(ByteBuffer src, MessageInfo messageInfo)
+            throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    protected void implConfigureBlocking(boolean block) throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public void implCloseSelectableChannel() throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/windows/classes/sun/nio/ch/SctpMultiChannelImpl.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2009 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.SocketAddress;
+import java.net.InetAddress;
+import java.io.IOException;
+import java.util.Set;
+import java.nio.ByteBuffer;
+import java.nio.channels.spi.SelectorProvider;
+import com.sun.nio.sctp.Association;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.MessageInfo;
+import com.sun.nio.sctp.NotificationHandler;
+import com.sun.nio.sctp.SctpMultiChannel;
+import com.sun.nio.sctp.SctpSocketOption;
+
+/**
+ * Unimplemented.
+ */
+public class SctpMultiChannelImpl extends SctpMultiChannel
+{
+    private static final String message = "SCTP not supported on this platform";
+
+    public SctpMultiChannelImpl(SelectorProvider provider) {
+        super(provider);
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public Set<Association> associations() {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public SctpMultiChannel bind(SocketAddress local,
+            int backlog) throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public SctpMultiChannel bindAddress(InetAddress address)
+         throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public SctpMultiChannel unbindAddress(InetAddress address)
+         throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public Set<SocketAddress> getAllLocalAddresses()
+            throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public Set<SocketAddress> getRemoteAddresses
+            (Association association) throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public SctpMultiChannel shutdown(Association association)
+            throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public <T> T getOption(SctpSocketOption<T> name,
+            Association association) throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public <T> SctpMultiChannel setOption(SctpSocketOption<T> name,
+            T value, Association association) throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public Set<SctpSocketOption<?>> supportedOptions() {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public <T> MessageInfo receive(ByteBuffer buffer, T attachment,
+            NotificationHandler<T> handler) throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public int send(ByteBuffer buffer, MessageInfo messageInfo)
+            throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public SctpChannel branch(Association association)
+            throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    protected void implConfigureBlocking(boolean block) throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public void implCloseSelectableChannel() throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/windows/classes/sun/nio/ch/SctpServerChannelImpl.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2009 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.SocketAddress;
+import java.net.InetAddress;
+import java.io.IOException;
+import java.util.Set;
+import java.nio.channels.spi.SelectorProvider;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpServerChannel;
+import com.sun.nio.sctp.SctpSocketOption;
+
+/**
+ * Unimplemented.
+ */
+public class SctpServerChannelImpl extends SctpServerChannel
+{
+    private static final String message = "SCTP not supported on this platform";
+
+    public SctpServerChannelImpl(SelectorProvider provider) {
+        super(provider);
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public SctpChannel accept() throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public SctpServerChannel bind(SocketAddress local,
+            int backlog) throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public SctpServerChannel bindAddress(InetAddress address)
+         throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public SctpServerChannel unbindAddress(InetAddress address)
+         throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public Set<SocketAddress> getAllLocalAddresses()
+            throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public <T> T getOption(SctpSocketOption<T> name) throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public <T> SctpServerChannel setOption(SctpSocketOption<T> name,
+            T value) throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public Set<SctpSocketOption<?>> supportedOptions() {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    protected void implConfigureBlocking(boolean block) throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+
+    @Override
+    public void implCloseSelectableChannel() throws IOException {
+        throw new UnsupportedOperationException(message);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/MessageInfoTests.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2009 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 4927640
+ * @summary Tests the SCTP protocol implementation
+ * @author chegar
+ */
+
+import java.net.SocketAddress;
+import com.sun.nio.sctp.Association;
+import com.sun.nio.sctp.MessageInfo;
+
+public class MessageInfoTests {
+    static final int DEFAULT_STREAM_NUMBER = 14;
+    static final int TEST_STREAM_NUMBER = 15;
+    static final int TEST_PPID = 8;
+    static final long TEST_TTL = 10000L;
+    static final SocketAddress addr = new TestSocketAddress();
+    static final Association assoc = new TestAssociation(1, 1, 1);
+
+    void test(String[] args) {
+        /* TEST 1 : createOutGoing(SocketAddress,int) */
+        MessageInfo info = MessageInfo.createOutgoing(addr,
+                                                      DEFAULT_STREAM_NUMBER);
+        checkDefaults(info);
+        checkGetterSetters(info);
+
+        /* TEST 2 : createOutGoing(Association,SocketAddress,int) */
+        info = MessageInfo.createOutgoing(assoc, addr, DEFAULT_STREAM_NUMBER);
+        checkDefaults(info);
+        check(info.association().equals(assoc), "incorrect association");
+        checkGetterSetters(info);
+
+        /* TEST 3: null values */
+        info = MessageInfo.createOutgoing(null, 0);
+        check(info.address() == null, "address should be null");
+        check(info.association() == null, "association should be null");
+        info = MessageInfo.createOutgoing(assoc, null, 0);
+        check(info.address() == null, "address should be null");
+
+        /* Test 4: IllegalArgumentException */
+        testIAE(new Runnable() {
+          public void run() { MessageInfo.createOutgoing(addr, -1); } });
+        testIAE(new Runnable() {
+          public void run() { MessageInfo.createOutgoing(addr, 65537); } });
+        testIAE(new Runnable() {
+          public void run() { MessageInfo.createOutgoing(null, addr, 0); } });
+        testIAE(new Runnable() {
+          public void run() { MessageInfo.createOutgoing(assoc, addr, -1); } });
+        testIAE(new Runnable() {
+          public void run() { MessageInfo.createOutgoing(assoc, addr, 65537);}});
+
+        final MessageInfo iaeInfo = MessageInfo.createOutgoing(assoc, addr, 0);
+        testIAE(new Runnable() {
+          public void run() { iaeInfo.streamNumber(-1); } });
+        testIAE(new Runnable() {
+          public void run() { iaeInfo.streamNumber(65537); } });
+    }
+
+   /* TEST : unordered = false, timeToLive = 0, complete = true,
+    *        payloadProtocolID = 0. */
+    void checkDefaults(MessageInfo info) {
+        check(info.isUnordered() == false, "default unordered value not false");
+        check(info.timeToLive() == 0L, "timeToLive should be 0L");
+        check(info.isComplete() == true, "default complete value not true");
+        check(info.payloadProtocolID() == 0, "default PPID not 0");
+        check(info.bytes() == 0, "default bytes value not 0");
+        check(info.streamNumber() == DEFAULT_STREAM_NUMBER,
+                "incorrect default stream number");
+        check(info.address().equals(addr), "incorrect address");
+    }
+
+    void checkGetterSetters(MessageInfo info) {
+        check(info.streamNumber(TEST_STREAM_NUMBER).streamNumber() ==
+                TEST_STREAM_NUMBER, "stream number not being set correctly");
+
+        check(info.complete(false).isComplete() == false,
+                "complete not being set correctly");
+
+        check(info.unordered(true).isUnordered() == true,
+                "unordered not being set correctly");
+
+        check(info.payloadProtocolID(TEST_PPID).payloadProtocolID() ==
+                                  TEST_PPID, "PPID not being set correctly");
+
+        check(info.timeToLive(TEST_TTL).timeToLive() == TEST_TTL,
+                "TTL not being set correctly");
+    }
+
+    void testIAE(Runnable runnable) {
+        try {
+            runnable.run();
+            fail("IllegalArgumentException should have been thrown");
+        } catch(IllegalArgumentException iae) {
+            pass();
+        }
+    }
+
+    static class TestSocketAddress extends SocketAddress {}
+
+    static class TestAssociation extends Association {
+        TestAssociation(int assocID, int maxInStreams, int maxOutStreams) {
+            super(assocID, maxInStreams, maxOutStreams);
+        }
+    }
+
+          //--------------------- Infrastructure ---------------------------
+    boolean debug = true;
+    volatile int passed = 0, failed = 0;
+    void pass() {passed++;}
+    void fail() {failed++; Thread.dumpStack();}
+    void fail(String msg) {System.err.println(msg); fail();}
+    void unexpected(Throwable t) {failed++; t.printStackTrace();}
+    void check(boolean cond) {if (cond) pass(); else fail();}
+    void check(boolean cond, String failMessage) {if (cond) pass();
+          else fail(failMessage);}
+    void debug(String message) {if(debug) { System.out.println(message); }  }
+    public static void main(String[] args) throws Throwable {
+        Class<?> k = new Object(){}.getClass().getEnclosingClass();
+        try {k.getMethod("instanceMain",String[].class)
+                .invoke( k.newInstance(), (Object) args);}
+        catch (Throwable e) {throw e.getCause();}}
+    public void instanceMain(String[] args) throws Throwable {
+        try {test(args);} catch (Throwable t) {unexpected(t);}
+        System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
+        if (failed > 0) throw new AssertionError("Some tests failed");}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/SctpChannel/Bind.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,350 @@
+/*
+ * Copyright 2009 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 4927640
+ * @summary Tests the SCTP protocol implementation
+ * @author chegar
+ */
+
+import java.net.*;
+import java.io.*;
+import java.util.List;
+import java.util.Set;
+import java.util.Iterator;
+import java.nio.ByteBuffer;
+import java.nio.channels.AlreadyBoundException;
+import java.nio.channels.AlreadyConnectedException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.UnsupportedAddressTypeException;
+import com.sun.nio.sctp.AssociationChangeNotification;
+import com.sun.nio.sctp.AbstractNotificationHandler;
+import com.sun.nio.sctp.HandlerResult;
+import com.sun.nio.sctp.IllegalUnbindException;
+import com.sun.nio.sctp.MessageInfo;
+import com.sun.nio.sctp.PeerAddressChangeNotification;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpServerChannel;
+import com.sun.nio.sctp.ShutdownNotification;
+import static java.lang.System.out;
+
+/**
+ * Tests bind, bindAddress, unbindAddress, getLocalAddress, and
+ * getAllLocalAddresses.
+ */
+public class Bind {
+     void test(String[] args) {
+        if (!Util.isSCTPSupported()) {
+            out.println("SCTP protocol is not supported");
+            out.println("Test cannot be run");
+            return;
+        }
+
+        /* Simply bind tests */
+        testBind();
+
+        /* Test unconnected */
+        testBindUnbind(false);
+
+        /* Test connected */
+        /* Adding/Removing addresses from a connected association is optional.
+         * This test can be run on systems that support dynamic address
+         * reconfiguration */
+        //testBindUnbind(true);
+    }
+
+    void testBind() {
+        SctpChannel channel = null;
+        try {
+            channel = SctpChannel.open();
+
+            /* TEST 1: empty set if channel is not bound */
+            check(channel.getAllLocalAddresses().isEmpty(),
+                    "getAllLocalAddresses returned non empty set for unbound channel");
+
+            /* TEST 2: null to bind the channel to an automatically assigned
+             *         socket address */
+            channel.bind(null);
+
+            /* TEST 3: non empty set if the channel is bound */
+            check(!channel.getAllLocalAddresses().isEmpty(),
+                    "getAllLocalAddresses returned empty set for bound channel");
+            debug("getAllLocalAddresses on channel bound to the wildcard:\n"
+                    + channel.getAllLocalAddresses());
+
+            /* TEST 4: AlreadyBoundException if this channel is already bound */
+            try { channel.bind(null); }
+            catch (AlreadyBoundException unused) { pass(); }
+            catch (IOException ioe) { unexpected(ioe); }
+
+            /* TEST 5: UnsupportedAddressTypeException */
+            try {
+                channel.close();  /* open a new unbound channel for test */
+                channel = SctpChannel.open();
+                channel.bind(new UnsupportedSocketAddress());
+                fail("UnsupportedSocketAddress expected");
+            } catch (UnsupportedAddressTypeException unused) { pass();
+            } catch (IOException ioe) { unexpected(ioe); }
+
+            /* TEST 6: AlreadyConnectedException */
+            try {
+                channel.close();  /* open a new unbound channel for test */
+                channel = SctpChannel.open();
+                connectChannel(channel);
+                channel.bind(null);
+                fail("AlreadyConnectedException expected");
+            } catch (AlreadyConnectedException unused) { pass();
+            } catch (IOException ioe) { unexpected(ioe); }
+
+            /* TEST 7: ClosedChannelException - If this channel is closed */
+            try {
+                channel.close();  /* open a new unbound channel for test */
+                channel = SctpChannel.open();
+                channel.close();
+                channel.bind(null);
+                fail("ClosedChannelException expected");
+            } catch (ClosedChannelException unused) { pass();
+            } catch (IOException ioe) { unexpected(ioe); }
+
+            /* TEST 8: ClosedChannelException if channel is closed */
+            try {
+                channel.getAllLocalAddresses();
+                fail("should have thrown ClosedChannelException");
+            } catch (ClosedChannelException cce) {
+               pass();
+            } catch (Exception ioe) {
+                unexpected(ioe);
+            }
+        } catch (IOException ioe) {
+            unexpected(ioe);
+        } finally {
+            try { channel.close(); }
+            catch (IOException ioe) { unexpected(ioe); }
+        }
+    }
+
+    void testBindUnbind(boolean connected) {
+        SctpChannel channel = null;
+        SctpChannel peerChannel = null;
+
+        debug("testBindUnbind, connected: " + connected);
+        try {
+            channel = SctpChannel.open();
+
+            List<InetAddress> addresses = Util.getAddresses(true, false);
+            Iterator iterator = addresses.iterator();
+            InetSocketAddress a = new InetSocketAddress((InetAddress)iterator.next(), 0);
+            debug("channel.bind( " + a + ")");
+            channel.bind(a);
+            while (iterator.hasNext()) {
+                InetAddress ia = (InetAddress)iterator.next();
+                debug("channel.bindAddress(" + ia + ")");
+                channel.bindAddress(ia);
+            }
+            if (debug) {Util.dumpAddresses(channel, out);}
+
+            if (connected) {
+                /* Test with connected channel */
+                peerChannel = connectChannel(channel);
+            }
+
+            /* TEST 1: bind/unbindAddresses on the system addresses */
+            debug("bind/unbindAddresses on the system addresses");
+            List<InetAddress> addrs = Util.getAddresses(true, false);
+            for (InetAddress addr : addrs) {
+                try {
+                    debug("unbindAddress: " + addr);
+                    check(boundAddress(channel, addr), "trying to remove address that is not bound");
+                    channel.unbindAddress(addr);
+                    if (debug) {Util.dumpAddresses(channel, out);}
+                    check(!boundAddress(channel, addr), "address was not removed");
+
+                    debug("bindAddress: " + addr);
+                    channel.bindAddress(addr);
+                    if (debug) {Util.dumpAddresses(channel, out);}
+                    check(boundAddress(channel, addr), "address is not bound");
+                } catch (IOException ioe) {
+                    unexpected(ioe);
+                }
+            }
+
+            /* TEST 2: bindAddress - already bound address. */
+            InetAddress againAddress = addrs.get(0);
+            try {
+                debug("bind already bound address " + againAddress);
+                channel.bindAddress(againAddress);
+            } catch (AlreadyBoundException unused) {
+                debug("Caught AlreadyBoundException - OK");
+                pass();
+            } catch (IOException ioe) {
+                unexpected(ioe);
+            }
+
+            /* TEST 3: bind non local address */
+            try {
+                InetAddress nla = InetAddress.getByName("123.123.123.123");
+                debug("bind non local address " + nla);
+                channel.bindAddress(nla);
+            } catch (IOException ioe) {
+                debug("Informative only " + ioe);
+            }
+
+            /* TEST 4: unbind address that is not bound */
+            try {
+                debug("unbind address that is not bound " + againAddress);
+                /* remove address first then again */
+                channel.unbindAddress(againAddress);
+                channel.unbindAddress(againAddress);
+            } catch (IllegalUnbindException unused) {
+                debug("Caught IllegalUnbindException - OK");
+                pass();
+            } catch (IOException ioe) {
+                unexpected(ioe);
+            }
+
+            /* TEST 5: unbind address that is not bound */
+            try {
+                InetAddress nla = InetAddress.getByName("123.123.123.123");
+                debug("unbind address that is not bound " + nla);
+                channel.unbindAddress(nla);
+
+            } catch (IllegalUnbindException unused) {
+                debug("Caught IllegalUnbindException - OK");
+                pass();
+            } catch (IOException ioe) {
+                unexpected(ioe);
+            }
+
+            if (connected) {
+                channel.shutdown();
+
+                BindNotificationHandler handler = new BindNotificationHandler();
+                ByteBuffer buffer = ByteBuffer.allocate(10);
+                MessageInfo info;
+                while((info = peerChannel.receive(buffer, null, handler)) != null) {
+                    if (info != null) {
+                        if (info.bytes() == -1) {
+                            debug("peerChannel Reached EOF");
+                            break;
+                        }
+                    }
+                }
+
+                while((info = channel.receive(buffer, null, handler)) != null) {
+                    if (info != null) {
+                        if (info.bytes() == -1) {
+                            debug("channel Reached EOF");
+                            break;
+                        }
+                    }
+                }
+            }
+        } catch (IOException ioe) {
+            ioe.printStackTrace();
+        } finally {
+            try { if (channel != null) channel.close(); }
+            catch (IOException ioe) { unexpected(ioe); }
+        }
+    }
+
+    boolean boundAddress(SctpChannel channel, InetAddress addr)
+        throws IOException {
+        for (SocketAddress boundAddr : channel.getAllLocalAddresses()) {
+            if (((InetSocketAddress) boundAddr).getAddress().equals(addr))
+                return true;
+        }
+        return false;
+    }
+
+    SctpChannel connectChannel(SctpChannel channel)
+        throws IOException {
+        debug("connecting channel...");
+        try {
+            SctpServerChannel ssc = SctpServerChannel.open();
+            ssc.bind(null);
+            Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
+            Iterator<SocketAddress> iterator = addrs.iterator();
+            SocketAddress addr = iterator.next();
+            debug("using " + addr + "...");
+            channel.connect(addr);
+            SctpChannel peerChannel = ssc.accept();
+            ssc.close();
+            debug("connected");
+            return peerChannel;
+        } catch (IOException ioe) {
+            debug("Cannot connect channel");
+            unexpected(ioe);
+            throw ioe;
+        }
+    }
+
+    class BindNotificationHandler extends AbstractNotificationHandler<Object>
+    {
+        @Override
+        public HandlerResult handleNotification(
+                AssociationChangeNotification acn, Object unused)
+        {
+            debug("AssociationChangeNotification: " +  acn);
+            return HandlerResult.CONTINUE;
+        }
+
+        @Override
+        public HandlerResult handleNotification(
+                PeerAddressChangeNotification pacn, Object unused)
+        {
+            debug("PeerAddressChangeNotification: " +  pacn);
+            return HandlerResult.CONTINUE;
+        }
+
+        @Override
+        public HandlerResult handleNotification(
+                ShutdownNotification sn, Object unused)
+        {
+            debug("ShutdownNotification: " +  sn);
+            return HandlerResult.CONTINUE;
+        }
+    }
+
+    class UnsupportedSocketAddress extends SocketAddress { }
+
+    //--------------------- Infrastructure ---------------------------
+    boolean debug = true;
+    volatile int passed = 0, failed = 0;
+    void pass() {passed++;}
+    void fail() {failed++; Thread.dumpStack();}
+    void fail(String msg) {System.err.println(msg); fail();}
+    void unexpected(Throwable t) {failed++; t.printStackTrace();}
+    void check(boolean cond) {if (cond) pass(); else fail();}
+    void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
+    void debug(String message) {if(debug) { System.out.println(message); }  }
+    public static void main(String[] args) throws Throwable {
+        Class<?> k = new Object(){}.getClass().getEnclosingClass();
+        try {k.getMethod("instanceMain",String[].class)
+                .invoke( k.newInstance(), (Object) args);}
+        catch (Throwable e) {throw e.getCause();}}
+    public void instanceMain(String[] args) throws Throwable {
+        try {test(args);} catch (Throwable t) {unexpected(t);}
+        System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
+        if (failed > 0) throw new AssertionError("Some tests failed");}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/SctpChannel/Connect.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2009 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 4927640
+ * @summary Tests the SCTP protocol implementation
+ * @author chegar
+ */
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.io.IOException;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.nio.channels.AlreadyConnectedException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.ConnectionPendingException;
+import java.nio.channels.NoConnectionPendingException;
+import java.nio.channels.UnresolvedAddressException;
+import java.nio.channels.UnsupportedAddressTypeException;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpServerChannel;
+import static java.lang.System.out;
+import static java.lang.System.err;
+
+/**
+ * Tests connect, finishConnect, isConnectionPending,
+ * getRemoteAddresses and association.
+ */
+public class Connect {
+    final CountDownLatch finishedLatch = new CountDownLatch(1);
+
+    void test(String[] args) {
+        SocketAddress address = null;
+        Server server = null;
+
+        if (!Util.isSCTPSupported()) {
+            out.println("SCTP protocol is not supported");
+            out.println("Test cannot be run");
+            return;
+        }
+
+        if (args.length == 2) {
+            /* requested to connect to a specific address */
+            try {
+                int port = Integer.valueOf(args[1]);
+                address = new InetSocketAddress(args[0], port);
+            } catch (NumberFormatException nfe) {
+                err.println(nfe);
+            }
+        } else {
+            /* start server on local machine, default */
+            try {
+                server = new Server();
+                server.start();
+                address = server.address();
+                debug("Server started and listening on " + address);
+            } catch (IOException ioe) {
+                ioe.printStackTrace();
+                return;
+            }
+        }
+
+        doTest(address);
+    }
+
+    void doTest(SocketAddress addr) {
+        SctpChannel channel = null;
+        final SocketAddress peerAddress = addr;
+
+        try {
+            channel = SctpChannel.open();
+
+            /* TEST 0.5 Verify default values for new/unconnected channel */
+            check(channel.getRemoteAddresses().isEmpty(),
+                    "non empty set for unconnected channel");
+            check(channel.association() == null,
+                    "non-null association for unconnected channel");
+            check(!channel.isConnectionPending(),
+                    "should not have a connection pending");
+
+            /* TEST 1: non-blocking connect */
+            channel.configureBlocking(false);
+            if (channel.connect(peerAddress) != true) {
+                debug("non-blocking connect did not immediately succeed");
+                check(channel.isConnectionPending(),
+                        "should return true for isConnectionPending");
+                try {
+                    channel.connect(peerAddress);
+                    fail("should have thrown ConnectionPendingException");
+                } catch (ConnectionPendingException cpe) {
+                    pass();
+                } catch (IOException ioe) {
+                    unexpected(ioe);
+                }
+                channel.configureBlocking(true);
+                check(channel.finishConnect(),
+                        "finishConnect should have returned true");
+            }
+
+            /* TEST 1.5 Verify after connect */
+            check(!channel.getRemoteAddresses().isEmpty(),
+                    "empty set for connected channel");
+            check(channel.association() != null,
+                    "null association for connected channel");
+            check(!channel.isConnectionPending(),
+                    "pending connection for connected channel");
+
+            /* TEST 2: Verify AlreadyConnectedException thrown */
+            try {
+                channel.connect(peerAddress);
+                fail("should have thrown AlreadyConnectedException");
+            } catch (AlreadyConnectedException unused) {
+                pass();
+            }  catch (IOException ioe) {
+                unexpected(ioe);
+            }
+
+            /* TEST 3: UnresolvedAddressException */
+            channel.close();
+            channel = SctpChannel.open();
+            InetSocketAddress unresolved =
+                    InetSocketAddress.createUnresolved("xxyyzzabc", 4567);
+            try {
+                channel.connect(unresolved);
+                fail("should have thrown UnresolvedAddressException");
+            } catch (UnresolvedAddressException unused) {
+                pass();
+            }  catch (IOException ioe) {
+                unexpected(ioe);
+            }
+
+            /* TEST 4: UnsupportedAddressTypeException */
+            SocketAddress unsupported = new UnsupportedSocketAddress();
+            try {
+                channel.connect(unsupported);
+                fail("should have thrown UnsupportedAddressTypeException");
+            } catch (UnsupportedAddressTypeException unused) {
+                pass();
+            }  catch (IOException ioe) {
+                unexpected(ioe);
+            }
+
+            /* TEST 5: ClosedChannelException */
+            channel.close();
+            final SctpChannel closedChannel = channel;
+            testCCE(new Callable<Void>() {
+                public Void call() throws IOException {
+                    closedChannel.connect(peerAddress); return null; } });
+
+            /* TEST 5.5 getRemoteAddresses */
+            testCCE(new Callable<Void>() {
+                public Void call() throws IOException {
+                    closedChannel.getRemoteAddresses(); return null; } });
+            testCCE(new Callable<Void>() {
+                public Void call() throws IOException {
+                    closedChannel.association(); return null; } });
+            check(!channel.isConnectionPending(),
+                    "pending connection for closed channel");
+
+            /* Run some more finishConnect tests */
+
+            /* TEST 6: NoConnectionPendingException */
+            channel = SctpChannel.open();
+            try {
+                channel.finishConnect();
+                fail("should have thrown NoConnectionPendingException");
+            } catch (NoConnectionPendingException unused) {
+                pass();
+            }  catch (IOException ioe) {
+                unexpected(ioe);
+            }
+
+            /* TEST 7: ClosedChannelException */
+            channel.close();
+            final SctpChannel cceChannel = channel;
+            testCCE(new Callable<Void>() {
+                public Void call() throws IOException {
+                    cceChannel.finishConnect(); return null; } });
+        } catch (IOException ioe) {
+            unexpected(ioe);
+        } finally {
+            finishedLatch.countDown();
+            try { if (channel != null) channel.close(); }
+            catch (IOException e) { unexpected(e);}
+        }
+    }
+
+    class UnsupportedSocketAddress extends SocketAddress { }
+
+    void testCCE(Callable callable) {
+        try {
+            callable.call();
+            fail("should have thrown ClosedChannelException");
+        } catch (ClosedChannelException cce) {
+           pass();
+        } catch (Exception ioe) {
+            unexpected(ioe);
+        }
+    }
+
+    class Server implements Runnable
+    {
+        final InetSocketAddress serverAddr;
+        private SctpServerChannel ssc;
+
+        public Server() throws IOException {
+            ssc = SctpServerChannel.open().bind(null);
+            java.util.Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
+            if (addrs.isEmpty())
+                debug("addrs should not be empty");
+
+            serverAddr = (InetSocketAddress) addrs.iterator().next();
+        }
+
+        public void start() {
+            (new Thread(this, "Server-"  + serverAddr.getPort())).start();
+        }
+
+        public InetSocketAddress address() {
+            return serverAddr;
+        }
+
+        @Override
+        public void run() {
+            SctpChannel sc = null;
+            try {
+                sc = ssc.accept();
+                finishedLatch.await();
+            } catch (IOException ioe) {
+                unexpected(ioe);
+            } catch (InterruptedException ie) {
+                unexpected(ie);
+            } finally {
+                try { if (ssc != null) ssc.close(); }
+                catch (IOException  ioe) { unexpected(ioe); }
+                try { if (sc != null) sc.close(); }
+                catch (IOException  ioe) { unexpected(ioe); }
+            }
+        }
+    }
+
+        //--------------------- Infrastructure ---------------------------
+    boolean debug = true;
+    volatile int passed = 0, failed = 0;
+    void pass() {passed++;}
+    void fail() {failed++; Thread.dumpStack();}
+    void fail(String msg) {System.err.println(msg); fail();}
+    void unexpected(Throwable t) {failed++; t.printStackTrace();}
+    void check(boolean cond) {if (cond) pass(); else fail();}
+    void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
+    void debug(String message) {if(debug) { System.out.println(message); }  }
+    public static void main(String[] args) throws Throwable {
+        Class<?> k = new Object(){}.getClass().getEnclosingClass();
+        try {k.getMethod("instanceMain",String[].class)
+                .invoke( k.newInstance(), (Object) args);}
+        catch (Throwable e) {throw e.getCause();}}
+    public void instanceMain(String[] args) throws Throwable {
+        try {test(args);} catch (Throwable t) {unexpected(t);}
+        System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
+        if (failed > 0) throw new AssertionError("Some tests failed");}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/SctpChannel/Receive.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,361 @@
+/*
+ * Copyright 2009 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 4927640
+ * @summary Tests the SCTP protocol implementation
+ * @author chegar
+ */
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.nio.ByteBuffer;
+import java.nio.channels.NotYetConnectedException;
+import java.nio.channels.ClosedChannelException;
+import com.sun.nio.sctp.AbstractNotificationHandler;
+import com.sun.nio.sctp.AssociationChangeNotification;
+import com.sun.nio.sctp.AssociationChangeNotification.AssocChangeEvent;
+import com.sun.nio.sctp.HandlerResult;
+import com.sun.nio.sctp.IllegalReceiveException;
+import com.sun.nio.sctp.MessageInfo;
+import com.sun.nio.sctp.Notification;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpServerChannel;
+import com.sun.nio.sctp.ShutdownNotification;
+import static java.lang.System.out;
+import static java.lang.System.err;
+
+public class Receive {
+    /* Latches used to synchronize between the client and server so that
+     * connections without any IO may not be closed without being accepted */
+    final CountDownLatch clientFinishedLatch = new CountDownLatch(1);
+    final CountDownLatch serverFinishedLatch = new CountDownLatch(1);
+
+    /* Used to verify that the ppid is being sent and received correctly */
+    static final int PPID = 5;
+
+    void test(String[] args) {
+        SocketAddress address = null;
+        Server server;
+
+
+        if (!Util.isSCTPSupported()) {
+            out.println("SCTP protocol is not supported");
+            out.println("Test cannot be run");
+            return;
+        }
+
+        if (args.length == 2) {
+            /* requested to connecct to a specific address */
+            try {
+                int port = Integer.valueOf(args[1]);
+                address = new InetSocketAddress(args[0], port);
+            } catch (NumberFormatException nfe) {
+                err.println(nfe);
+            }
+        } else {
+            /* start server on local machine, default */
+            try {
+                server = new Server();
+                server.start();
+                address = server.address();
+                debug("Server started and listening on " + address);
+            } catch (IOException ioe) {
+                ioe.printStackTrace();
+                return;
+            }
+        }
+
+        doTest(address);
+    }
+
+    void doTest(SocketAddress peerAddress) {
+        SctpChannel channel = null;
+        ByteBuffer buffer = ByteBuffer.allocate(Util.LARGE_BUFFER);
+        MessageInfo info;
+
+        try {
+            channel = SctpChannel.open();
+            ReceiveNotificationHandler handler =
+                new ReceiveNotificationHandler(channel);
+
+            /* TEST 1: Verify NotYetConnectedException thrown */
+            try {
+                channel.receive(buffer, null, handler);
+                fail("should have thrown NotYetConnectedException");
+            } catch (NotYetConnectedException unused) {
+                pass();
+            }  catch (IOException ioe) {
+                unexpected(ioe);
+            }
+
+            channel.connect(peerAddress);
+
+            /* TEST 2: receive small message */
+            do {
+                debug("Test 2: invoking receive");
+                info = channel.receive(buffer, null, handler);
+                if (info == null) {
+                    fail("unexpected null from receive");
+                    return;
+                }
+            } while (!info.isComplete());
+
+            buffer.flip();
+            check(handler.receivedCommUp(), "SCTP_COMM_UP not received");
+            check(info != null, "info is null");
+            check(info.address() != null, "address is null");
+            check(info.association() != null, "association is null");
+            check(info.isComplete(), "message is not complete");
+            check(info.isUnordered() != true,
+                  "message should not be unordered");
+            check(info.streamNumber() >= 0, "invalid stream number");
+            check(info.payloadProtocolID() == PPID, "PPID incorrect");
+            check(info.bytes() == Util.SMALL_MESSAGE.getBytes("ISO-8859-1").
+                  length, "bytes received not equal to message length");
+            check(info.bytes() == buffer.remaining(), "bytes != remaining");
+            check(Util.compare(buffer, Util.SMALL_MESSAGE),
+                  "received message not the same as sent message");
+
+            buffer.clear();
+
+             /* TEST 3: receive large message */
+            do {
+                debug("Test 3: invoking receive");
+                info = channel.receive(buffer, null, handler);
+                if (info == null) {
+                    fail("unexpected null from receive");
+                    return;
+                }
+            } while (!info.isComplete());
+
+            buffer.flip();
+            check(info != null, "info is null");
+            check(info.address() != null, "address is null");
+            check(info.association() != null, "association is null");
+            check(info.isComplete(), "message is not complete");
+            check(info.isUnordered() != true,
+                  "message should not be unordered");
+            check(info.streamNumber() >= 0, "invalid stream number");
+            check(info.bytes() == Util.LARGE_MESSAGE.getBytes("ISO-8859-1").
+                  length, "bytes received not equal to message length");
+            check(info.bytes() == buffer.remaining(), "bytes != remaining");
+            check(Util.compare(buffer, Util.LARGE_MESSAGE),
+                  "received message not the same as sent message");
+
+            buffer.clear();
+
+            /* TEST 4: EOF */
+            buffer.clear();  // buffer position 0
+            info = channel.receive(buffer,null, handler);
+            check(info != null, "info is null");
+            check(info.bytes() == -1, "should have received EOF");
+            check(buffer.position() == 0, "buffer position should be unchanged");
+
+            /* TEST 5: ClosedChannelException */
+            channel.close();
+            try {
+                channel.receive(buffer, null, null);
+                fail("should have thrown ClosedChannelException");
+            } catch (ClosedChannelException cce) {
+               pass();
+            } catch (IOException ioe) {
+                unexpected(ioe);
+            }
+            handler = null;
+
+            /* TEST 6: handler returns RETURN after handling a notification */
+            ReceiveNotificationHandler handler2 =
+                new ReceiveNotificationHandler(null); /* HandlerResult.RETURN */
+            channel = SctpChannel.open(peerAddress, 0, 0);
+            info = channel.receive(buffer, null, handler2);
+            check(info == null, "channel should return null");
+            check(handler2.receivedCommUp(), "SCTP_COMM_UP not received");
+            check(buffer.position() == 0, "buffer position should be unchanged");
+
+            /* TEST 7: Non blocking channel return null if no data */
+            channel.configureBlocking(false);
+            info = channel.receive(buffer, null, null);
+            check(info == null, "non-blocking channel should return null");
+            check(buffer.position() == 0, "buffer position should be unchanged");
+        } catch (IOException ioe) {
+            unexpected(ioe);
+        } finally {
+            clientFinishedLatch.countDown();
+            try { serverFinishedLatch.await(10L, TimeUnit.SECONDS); }
+            catch (InterruptedException ie) { unexpected(ie); }
+            if (channel != null) {
+                try { channel.close(); }
+                catch (IOException e) { unexpected (e);}
+            }
+        }
+    }
+
+    class Server implements Runnable
+    {
+        final InetSocketAddress serverAddr;
+        private SctpServerChannel ssc;
+
+        public Server() throws IOException {
+            ssc = SctpServerChannel.open().bind(null);
+            java.util.Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
+            if (addrs.isEmpty())
+                debug("addrs should not be empty");
+
+            serverAddr = (InetSocketAddress) addrs.iterator().next();
+        }
+
+        public void start() {
+            (new Thread(this, "Server-"  + serverAddr.getPort())).start();
+        }
+
+        public InetSocketAddress address() {
+            return serverAddr;
+        }
+
+        @Override
+        public void run() {
+            try {
+                SctpChannel sc = ssc.accept();
+
+                /* send a small message */
+                MessageInfo info = MessageInfo.createOutgoing(null, 0)
+                        .payloadProtocolID(PPID);
+                ByteBuffer buf = ByteBuffer.allocateDirect(Util.SMALL_BUFFER);
+                buf.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1"));
+                buf.flip();
+
+                debug("sending small message: " + buf);
+                sc.send(buf, info);
+
+                /* send a large message */
+                buf = ByteBuffer.allocateDirect(Util.LARGE_BUFFER);
+                buf.put(Util.LARGE_MESSAGE.getBytes("ISO-8859-1"));
+                buf.flip();
+
+                debug("sending large message: " + buf);
+                sc.send(buf, info);
+                sc.shutdown();
+                debug("shutdown");
+                ReceiveNotificationHandler handler =
+                    new ReceiveNotificationHandler(sc);
+                sc.receive(buf, null, handler);
+                sc.close();
+
+                /* accept another socket for the TEST 6 */
+                sc = ssc.accept();
+                ssc.close();
+
+                clientFinishedLatch.await(10L, TimeUnit.SECONDS);
+                serverFinishedLatch.countDown();
+                sc.close();
+            } catch (IOException ioe) {
+                unexpected(ioe);
+            } catch (InterruptedException ie) {
+                unexpected(ie);
+            }
+        }
+    }
+
+    class ReceiveNotificationHandler extends AbstractNotificationHandler<Object>
+    {
+        SctpChannel channel;
+        boolean receivedCommUp;  // false
+
+        public ReceiveNotificationHandler(SctpChannel channel) {
+            this.channel = channel;
+        }
+
+        public boolean receivedCommUp() {
+            return receivedCommUp;
+        }
+
+        @Override
+        public HandlerResult handleNotification(
+                Notification notification, Object attachment) {
+            fail("Unknown notification type");
+            return HandlerResult.CONTINUE;
+        }
+
+        @Override
+        public HandlerResult handleNotification(
+                AssociationChangeNotification notification, Object attachment) {
+            AssocChangeEvent event = notification.event();
+            debug("AssociationChangeNotification");
+            debug("  Association: " + notification.association());
+            debug("  Event: " + event);
+
+            if (event.equals(AssocChangeEvent.COMM_UP))
+                receivedCommUp = true;
+
+            if (channel == null)
+                return HandlerResult.RETURN;
+
+            /* TEST 4: IllegalReceiveException - If the given handler invokes
+             * the receive method of this channel*/
+            ByteBuffer buffer = ByteBuffer.allocate(10);
+            try {
+                channel.receive(buffer, null, this);
+                fail("IllegalReceiveException expected");
+            } catch (IllegalReceiveException unused) {
+                pass();
+            } catch (IOException ioe) {
+                unexpected(ioe);
+            }
+
+            return HandlerResult.CONTINUE;
+        }
+
+        @Override
+        public HandlerResult handleNotification(
+                ShutdownNotification notification, Object attachment) {
+            debug("ShutdownNotification");
+            debug("  Association: " + notification.association());
+            return HandlerResult.CONTINUE;
+        }
+    }
+        //--------------------- Infrastructure ---------------------------
+    boolean debug = true;
+    volatile int passed = 0, failed = 0;
+    void pass() {passed++;}
+    void fail() {failed++; Thread.dumpStack();}
+    void fail(String msg) {System.err.println(msg); fail();}
+    void unexpected(Throwable t) {failed++; t.printStackTrace();}
+    void check(boolean cond) {if (cond) pass(); else fail();}
+    void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
+    void debug(String message) {if(debug) {
+          System.out.println(Thread.currentThread() + " " + message); } }
+    public static void main(String[] args) throws Throwable {
+        Class<?> k = new Object(){}.getClass().getEnclosingClass();
+        try {k.getMethod("instanceMain",String[].class)
+                .invoke( k.newInstance(), (Object) args);}
+        catch (Throwable e) {throw e.getCause();}}
+    public void instanceMain(String[] args) throws Throwable {
+        try {test(args);} catch (Throwable t) {unexpected(t);}
+        System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
+        if (failed > 0) throw new AssertionError("Some tests failed");}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/SctpChannel/Send.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,419 @@
+/*
+ * Copyright 2009 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 4927640
+ * @summary Tests the SCTP protocol implementation
+ * @author chegar
+ */
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.nio.ByteBuffer;
+import java.nio.channels.NotYetConnectedException;
+import java.nio.channels.ClosedChannelException;
+import com.sun.nio.sctp.AbstractNotificationHandler;
+import com.sun.nio.sctp.Association;
+import com.sun.nio.sctp.AssociationChangeNotification;
+import com.sun.nio.sctp.AssociationChangeNotification.AssocChangeEvent;
+import com.sun.nio.sctp.HandlerResult;
+import com.sun.nio.sctp.InvalidStreamException;
+import com.sun.nio.sctp.MessageInfo;
+import com.sun.nio.sctp.Notification;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpServerChannel;
+import static java.lang.System.out;
+import static java.lang.System.err;
+
+public class Send {
+    /* Latches used to synchronize between the client and server so that
+     * connections without any IO may not be closed without being accepted */
+    final CountDownLatch clientFinishedLatch = new CountDownLatch(1);
+    final CountDownLatch serverFinishedLatch = new CountDownLatch(1);
+
+    SendNotificationHandler handler = new SendNotificationHandler();
+
+    void test(String[] args) {
+        SocketAddress address = null;
+        Server server = null;
+
+        if (!Util.isSCTPSupported()) {
+            out.println("SCTP protocol is not supported");
+            out.println("Test cannot be run");
+            return;
+        }
+
+        if (args.length == 2) {
+            /* requested to connecct to a specific address */
+            try {
+                int port = Integer.valueOf(args[1]);
+                address = new InetSocketAddress(args[0], port);
+            } catch (NumberFormatException nfe) {
+                err.println(nfe);
+            }
+        } else {
+            /* start server on local machine, default */
+            try {
+                server = new Server();
+                server.start();
+                address = server.address();
+                debug("Server started and listening on " + address);
+            } catch (IOException ioe) {
+                ioe.printStackTrace();
+                return;
+            }
+        }
+
+        doTest(address);
+    }
+
+    void doTest(SocketAddress peerAddress) {
+        SctpChannel channel = null;
+        ByteBuffer buffer = ByteBuffer.allocate(Util.LARGE_BUFFER);
+        MessageInfo info = MessageInfo.createOutgoing(null, 0);
+
+        try {
+            channel = SctpChannel.open();
+
+            /* TEST 1: Verify NotYetConnectedException thrown */
+            try {
+                channel.send(buffer, info);
+                fail("should have thrown NotYetConnectedException");
+            } catch (NotYetConnectedException unused) {
+                pass();
+            }  catch (IOException ioe) {
+                unexpected(ioe);
+            }
+
+            channel.connect(peerAddress);
+            /* Receive CommUp */
+            channel.receive(buffer, null, handler);
+
+            /* save for TEST 8 */
+            Association association = channel.association();
+
+            /* TEST 2: send small message */
+            int streamNumber = 0;
+            debug("sending on stream number: " + streamNumber);
+            info = MessageInfo.createOutgoing(null, streamNumber);
+            buffer.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1"));
+            buffer.flip();
+            int position = buffer.position();
+            int remaining = buffer.remaining();
+
+            debug("sending small message: " + buffer);
+            int sent = channel.send(buffer, info);
+
+            check(sent == remaining, "sent should be equal to remaining");
+            check(buffer.position() == (position + sent),
+                    "buffers position should have been incremented by sent");
+
+            buffer.clear();
+
+            /* TEST 3: send large message */
+            streamNumber = handler.maxOutStreams() - 1;
+            debug("sending on stream number: " + streamNumber);
+            info = MessageInfo.createOutgoing(null, streamNumber);
+            buffer.put(Util.LARGE_MESSAGE.getBytes("ISO-8859-1"));
+            buffer.flip();
+            position = buffer.position();
+            remaining = buffer.remaining();
+
+            debug("sending large message: " + buffer);
+            sent = channel.send(buffer, info);
+
+            check(sent == remaining, "sent should be equal to remaining");
+            check(buffer.position() == (position + sent),
+                    "buffers position should have been incremented by sent");
+
+            /* TEST 4: InvalidStreamExcepton */
+            streamNumber = handler.maxInStreams;
+            info = MessageInfo.createOutgoing(null, streamNumber);
+            buffer.clear();
+            buffer.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1"));
+            buffer.flip();
+            position = buffer.position();
+            remaining = buffer.remaining();
+
+            debug("sending on stream number: " + streamNumber);
+            debug("sending small message: " + buffer);
+            try {
+                sent = channel.send(buffer, info);
+                fail("should have thrown InvalidStreamExcepton");
+            } catch (InvalidStreamException ise){
+                pass();
+            } catch (IOException ioe) {
+                unexpected(ioe);
+            }
+            check(buffer.remaining() == remaining,
+                    "remaining should not be changed");
+            check(buffer.position() == position,
+                    "buffers position should not be changed");
+
+            /* TEST 5: Non blocking send should return zero if there is
+               insufficient room in the underlying output buffer */
+            buffer.clear();
+            channel.configureBlocking(false);
+            info = MessageInfo.createOutgoing(null, 1);
+            buffer.put(Util.LARGE_MESSAGE.getBytes("ISO-8859-1"));
+            buffer.flip();
+
+            int count = 0;  // do not loop forever
+            do {
+                position = buffer.position();
+                remaining = buffer.remaining();
+                debug("sending large message: " + buffer);
+                sent = channel.send(buffer, info);
+                if (sent == 0) {
+                    check(buffer.remaining() == remaining,
+                          "remaining should not be changed");
+                    check(buffer.position() == position,
+                          "buffers position should not be changed");
+                }
+                buffer.rewind();
+            } while (sent != 0 && count++ < 100);
+
+            /* TEST 6: ClosedChannelException */
+            channel.close();
+            try {
+                channel.send(buffer, info);
+                fail("should have thrown ClosedChannelException");
+            } catch (ClosedChannelException cce) {
+               pass();
+            } catch (IOException ioe) {
+                unexpected(ioe);
+            }
+
+            /* TEST 7: send without previous receive.
+             * Verify that send can still throw InvalidStreamExcepton */
+            debug("Opening new channel.");
+            channel = SctpChannel.open(peerAddress, 0, 0);
+            streamNumber = Short.MAX_VALUE - 1;
+            info = MessageInfo.createOutgoing(null, streamNumber);
+            buffer.clear();
+            buffer.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1"));
+            buffer.flip();
+            position = buffer.position();
+            remaining = buffer.remaining();
+
+            debug("sending on stream number: " + streamNumber);
+            debug("sending small message: " + buffer);
+            try {
+                sent = channel.send(buffer, info);
+                fail("should have thrown InvalidStreamExcepton");
+            } catch (InvalidStreamException ise){
+                pass();
+            } catch (IOException ioe) {
+                unexpected(ioe);
+            }
+            check(buffer.remaining() == remaining,
+                    "remaining should not be changed");
+            check(buffer.position() == position,
+                    "buffers position should not be changed");
+
+            /* Receive CommUp */
+            channel.receive(buffer, null, handler);
+            check(handler.receivedCommUp(), "should have received COMM_UP");
+
+            /* TEST 8: Send to an invalid preferred SocketAddress */
+            SocketAddress addr = new InetSocketAddress("123.123.123.123", 3456);
+            info = MessageInfo.createOutgoing(addr, 0);
+            debug("sending to " + addr);
+            debug("sending small message: " + buffer);
+            try {
+                sent = channel.send(buffer, info);
+                fail("Invalid address should have thrown an Exception.");
+            } catch (Exception e){
+                pass();
+                debug("OK, caught " + e);
+            }
+        } catch (IOException ioe) {
+            unexpected(ioe);
+        } finally {
+            clientFinishedLatch.countDown();
+            try { serverFinishedLatch.await(10L, TimeUnit.SECONDS); }
+            catch (InterruptedException ie) { unexpected(ie); }
+            if (channel != null) {
+                try { channel.close(); }
+                catch (IOException e) { unexpected (e);}
+            }
+        }
+    }
+
+    class Server implements Runnable
+    {
+        final InetSocketAddress serverAddr;
+        private SctpServerChannel ssc;
+
+        public Server() throws IOException {
+            ssc = SctpServerChannel.open().bind(null);
+            java.util.Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
+            if (addrs.isEmpty())
+                debug("addrs should not be empty");
+
+            serverAddr = (InetSocketAddress) addrs.iterator().next();
+        }
+
+        public void start() {
+            (new Thread(this, "Server-"  + serverAddr.getPort())).start();
+        }
+
+        public InetSocketAddress address() {
+            return serverAddr;
+        }
+
+        @Override
+        public void run() {
+            ByteBuffer buffer = ByteBuffer.allocateDirect(Util.LARGE_BUFFER);
+            SctpChannel sc1 = null, sc2 = null;
+            try {
+                sc1 = ssc.accept();
+
+                /* receive a small message */
+                MessageInfo info;
+                do {
+                    info = sc1.receive(buffer, null, null);
+                    if (info == null) {
+                        fail("Server: unexpected null from receive");
+                            return;
+                    }
+                } while (!info.isComplete());
+
+                buffer.flip();
+                check(info != null, "info is null");
+                check(info.streamNumber() == 0,
+                        "message not sent on the correct stream");
+                check(info.bytes() == Util.SMALL_MESSAGE.getBytes("ISO-8859-1").
+                      length, "bytes received not equal to message length");
+                check(info.bytes() == buffer.remaining(), "bytes != remaining");
+                check(Util.compare(buffer, Util.SMALL_MESSAGE),
+                  "received message not the same as sent message");
+
+                /* receive a large message */
+                buffer.clear();
+                do {
+                    info = sc1.receive(buffer, null, null);
+                    if (info == null) {
+                        fail("Server: unexpected null from receive");
+                            return;
+                    }
+                } while (!info.isComplete());
+
+                buffer.flip();
+                check(info != null, "info is null");
+                check(info.streamNumber() == handler.maxOutStreams() - 1,
+                        "message not sent on the correct stream");
+                check(info.bytes() == Util.LARGE_MESSAGE.getBytes("ISO-8859-1").
+                      length, "bytes received not equal to message length");
+                check(info.bytes() == buffer.remaining(), "bytes != remaining");
+                check(Util.compare(buffer, Util.LARGE_MESSAGE),
+                  "received message not the same as sent message");
+
+                /* TEST 7 ++ */
+                sc2 = ssc.accept();
+
+                clientFinishedLatch.await(10L, TimeUnit.SECONDS);
+                serverFinishedLatch.countDown();
+            } catch (IOException ioe) {
+                unexpected(ioe);
+            } catch (InterruptedException ie) {
+                unexpected(ie);
+            } finally {
+                try { if (ssc != null) ssc.close(); }
+                catch (IOException  unused) {}
+                try { if (sc1 != null) sc1.close(); }
+                catch (IOException  unused) {}
+                try { if (sc2 != null) sc2.close(); }
+                catch (IOException  unused) {}
+            }
+        }
+    }
+
+    class SendNotificationHandler extends AbstractNotificationHandler<Void>
+    {
+        boolean receivedCommUp;  // false
+        int maxInStreams;
+        int maxOutStreams;
+
+        public boolean receivedCommUp() {
+            return receivedCommUp;
+        }
+
+        public int maxInStreams() {
+            return maxInStreams;
+        }
+
+        public int maxOutStreams(){
+            return maxOutStreams;
+        }
+
+        @Override
+        public HandlerResult handleNotification(
+                Notification notification, Void attachment) {
+            fail("Unknown notification type");
+            return HandlerResult.CONTINUE;
+        }
+
+        @Override
+        public HandlerResult handleNotification(
+                AssociationChangeNotification notification, Void attachment) {
+            AssocChangeEvent event = notification.event();
+            Association association = notification.association();
+            debug("AssociationChangeNotification");
+            debug("  Association: " + notification.association());
+            debug("  Event: " + event);
+
+            if (event.equals(AssocChangeEvent.COMM_UP))
+                receivedCommUp = true;
+
+            this.maxInStreams = association.maxInboundStreams();
+            this.maxOutStreams = association.maxOutboundStreams();
+
+            return HandlerResult.RETURN;
+        }
+    }
+
+        //--------------------- Infrastructure ---------------------------
+    boolean debug = true;
+    volatile int passed = 0, failed = 0;
+    void pass() {passed++;}
+    void fail() {failed++; Thread.dumpStack();}
+    void fail(String msg) {System.err.println(msg); fail();}
+    void unexpected(Throwable t) {failed++; t.printStackTrace();}
+    void check(boolean cond) {if (cond) pass(); else fail();}
+    void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
+    void debug(String message) {if(debug) { System.out.println(message); }  }
+    public static void main(String[] args) throws Throwable {
+        Class<?> k = new Object(){}.getClass().getEnclosingClass();
+        try {k.getMethod("instanceMain",String[].class)
+                .invoke( k.newInstance(), (Object) args);}
+        catch (Throwable e) {throw e.getCause();}}
+    public void instanceMain(String[] args) throws Throwable {
+        try {test(args);} catch (Throwable t) {unexpected(t);}
+        System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
+        if (failed > 0) throw new AssertionError("Some tests failed");}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/SctpChannel/Shutdown.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2009 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 4927640
+ * @summary Tests the SCTP protocol implementation
+ * @author chegar
+ */
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.nio.ByteBuffer;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.NotYetConnectedException;
+import com.sun.nio.sctp.AbstractNotificationHandler;
+import com.sun.nio.sctp.HandlerResult;
+import com.sun.nio.sctp.MessageInfo;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpServerChannel;
+import com.sun.nio.sctp.ShutdownNotification;
+import static java.lang.System.out;
+import static java.lang.System.err;
+
+public class Shutdown {
+    static CountDownLatch finishedLatch = new CountDownLatch(1);
+    static CountDownLatch sentLatch = new CountDownLatch(1);
+
+    void test(String[] args) {
+        SocketAddress address = null;
+        ShutdownServer server = null;
+
+        if (!Util.isSCTPSupported()) {
+            out.println("SCTP protocol is not supported");
+            out.println("Test cannot be run");
+            return;
+        }
+
+        if (args.length == 2) {
+            /* requested to connecct to a specific address */
+            try {
+                int port = Integer.valueOf(args[1]);
+                address = new InetSocketAddress(args[0], port);
+            } catch (NumberFormatException nfe) {
+                err.println(nfe);
+            }
+        } else {
+            /* start server on local machine, default */
+            try {
+                server = new ShutdownServer();
+                server.start();
+                address = server.address();
+                debug("Server started and listening on " + address);
+            } catch (IOException ioe) {
+                ioe.printStackTrace();
+                return;
+            }
+        }
+
+        doTest(address);
+    }
+
+    void doTest(SocketAddress peerAddress) {
+        SctpChannel channel = null;
+        ByteBuffer buffer = ByteBuffer.allocate(Util.SMALL_BUFFER);
+        MessageInfo info;
+
+        try {
+            channel = SctpChannel.open();
+
+            /* TEST 1: Verify NotYetConnectedException thrown */
+            debug("Test 1: NotYetConnectedException");
+            try {
+                channel.shutdown();
+                fail("shutdown not throwing expected NotYetConnectedException");
+            } catch (NotYetConnectedException unused) {
+                pass();
+            }  catch (IOException ioe) {
+                unexpected(ioe);
+            }
+
+            channel.connect(peerAddress);
+            sentLatch.await();
+            channel.shutdown();
+
+            /* TEST 2: receive data sent before shutdown */
+            do {
+                debug("Test 2: invoking receive");
+                info = channel.receive(buffer, null, null);
+                if (info == null) {
+                    fail("unexpected null from receive");
+                    return;
+                }
+            } while (!info.isComplete());
+
+            buffer.flip();
+            check(info != null, "info is null");
+            check(info.bytes() == Util.SMALL_MESSAGE.getBytes("ISO-8859-1").
+                  length, "bytes received not equal to message length");
+            check(info.bytes() == buffer.remaining(), "bytes != remaining");
+            check(Util.compare(buffer, Util.SMALL_MESSAGE),
+                  "received message not the same as sent message");
+
+            buffer.clear();
+
+            /* TEST 3: receive notifications on the SCTP stack */
+            debug("Test 3: receive notifications");
+            while ((info = channel.receive(buffer, null, null )) != null &&
+                    info.bytes() != -1 );
+
+
+            /* TEST 4: If the channel is already shutdown then invoking this
+             * method has no effect. */
+            debug("Test 4: no-op");
+            try {
+                channel.shutdown();
+                pass();
+            } catch (IOException ioe) {
+                unexpected(ioe);
+            }
+
+            /* TEST 5: Further sends will throw ClosedChannelException */
+            debug("Test 5: ClosedChannelException");
+            info = MessageInfo.createOutgoing(null, 1);
+            try {
+                channel.send(buffer, info);
+                fail("shutdown not throwing expected ClosedChannelException");
+            } catch (ClosedChannelException unused) {
+                pass();
+            } catch (IOException ioe) {
+                unexpected(ioe);
+            }
+        } catch (IOException ioe) {
+            unexpected(ioe);
+        } catch (InterruptedException ie) {
+            unexpected(ie);
+        }finally {
+            finishedLatch.countDown();
+            try { if (channel != null) channel.close(); }
+            catch (IOException e) { unexpected(e);}
+        }
+    }
+
+    class ShutdownServer implements Runnable
+    {
+        final InetSocketAddress serverAddr;
+        private SctpServerChannel ssc;
+
+        public ShutdownServer() throws IOException {
+            ssc = SctpServerChannel.open().bind(null);
+            //serverAddr = (InetSocketAddress)(ssc.getAllLocalAddresses().iterator().next());
+
+            java.util.Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
+            if (addrs.isEmpty())
+                debug("addrs should not be empty");
+
+            serverAddr = (InetSocketAddress) addrs.iterator().next();
+
+        }
+
+        public void start() {
+            (new Thread(this, "ShutdownServer-"  + serverAddr.getPort())).start();
+        }
+
+        public InetSocketAddress address() {
+            return serverAddr;
+        }
+
+        @Override
+        public void run() {
+            SctpChannel sc = null;
+            try {
+                sc = ssc.accept();
+
+                /* send a message */
+                MessageInfo info = MessageInfo.createOutgoing(null, 1);
+                ByteBuffer buf = ByteBuffer.allocateDirect(Util.SMALL_BUFFER);
+                buf.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1"));
+                buf.flip();
+                sc.send(buf, info);
+
+                /* notify client that the data has been sent */
+                sentLatch.countDown();
+
+                /* wait until after the client has finished its tests */
+                finishedLatch.await();
+
+                buf.clear();
+                ShutdownNotificationHandler handler =
+                        new ShutdownNotificationHandler();
+                BooleanWrapper bool = new BooleanWrapper();
+                sc.configureBlocking(false);
+                sc.receive(buf, bool, handler);
+                check(bool.booleanValue(), "SHUTDOWN not received on Server");
+
+            } catch (IOException ioe) {
+                ioe.printStackTrace();
+            } catch (InterruptedException ie) {
+                ie.printStackTrace();
+            } finally {
+                try { if (ssc != null) ssc.close(); }
+                catch (IOException  ioe) { unexpected(ioe); }
+                try { if (sc != null) sc.close(); }
+                catch (IOException  ioe) { unexpected(ioe); }
+            }
+        }
+    }
+
+    class BooleanWrapper {
+        boolean bool;
+
+        boolean booleanValue() {
+            return bool;
+        }
+
+        void booleanValue(boolean value) {
+            bool = value;
+        }
+    }
+
+    class ShutdownNotificationHandler extends AbstractNotificationHandler<BooleanWrapper>
+    {
+        @Override
+        public HandlerResult handleNotification(
+                ShutdownNotification sn, BooleanWrapper bool)
+        {
+            bool.booleanValue(true);
+            debug(sn.toString());
+            return HandlerResult.RETURN;
+        }
+    }
+
+        //--------------------- Infrastructure ---------------------------
+    boolean debug = true;
+    volatile int passed = 0, failed = 0;
+    void pass() {passed++;}
+    void fail() {failed++; Thread.dumpStack();}
+    void fail(String msg) {System.err.println(msg); fail();}
+    void unexpected(Throwable t) {failed++; t.printStackTrace();}
+    void check(boolean cond) {if (cond) pass(); else fail();}
+    void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
+    void debug(String message) {if(debug) { System.out.println(message); }  }
+    public static void main(String[] args) throws Throwable {
+        Class<?> k = new Object(){}.getClass().getEnclosingClass();
+        try {k.getMethod("instanceMain",String[].class)
+                .invoke( k.newInstance(), (Object) args);}
+        catch (Throwable e) {throw e.getCause();}}
+    public void instanceMain(String[] args) throws Throwable {
+        try {test(args);} catch (Throwable t) {unexpected(t);}
+        System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
+        if (failed > 0) throw new AssertionError("Some tests failed");}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/SctpChannel/SocketOptionTests.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2009 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 4927640
+ * @summary Tests the SCTP protocol implementation
+ * @author chegar
+ */
+
+import java.io.IOException;
+import java.util.Set;
+import java.util.List;
+import java.util.Arrays;
+import java.nio.channels.ClosedChannelException;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpSocketOption;
+import static com.sun.nio.sctp.SctpStandardSocketOption.*;
+import static java.lang.System.out;
+
+public class SocketOptionTests {
+    <T> void checkOption(SctpChannel sc, SctpSocketOption<T> name,
+            T expectedValue) throws IOException {
+        T value = sc.getOption(name);
+        check(value.equals(expectedValue), name + ": value (" + value +
+                ") not as expected (" + expectedValue + ")");
+       }
+
+    <T> void optionalSupport(SctpChannel sc, SctpSocketOption<T> name,
+            T value) {
+        try {
+            sc.setOption(name, value);
+            checkOption(sc, name, value);
+        } catch (IOException e) {
+            /* Informational only, not all options have native support */
+            out.println(name + " not supported. " + e);
+        }
+    }
+
+    void test(String[] args) {
+        if (!Util.isSCTPSupported()) {
+            out.println("SCTP protocol is not supported");
+            out.println("Test cannot be run");
+            return;
+        }
+
+        try {
+            SctpChannel sc = SctpChannel.open();
+
+            /* check supported options */
+            Set<SctpSocketOption<?>> options = sc.supportedOptions();
+            List<? extends SctpSocketOption<?>> expected = Arrays.<SctpSocketOption<?>>asList(
+                    SCTP_DISABLE_FRAGMENTS, SCTP_EXPLICIT_COMPLETE,
+                    SCTP_FRAGMENT_INTERLEAVE, SCTP_INIT_MAXSTREAMS,
+                    SCTP_NODELAY, SCTP_PRIMARY_ADDR, SCTP_SET_PEER_PRIMARY_ADDR,
+                    SO_SNDBUF, SO_RCVBUF, SO_LINGER);
+
+            for (SctpSocketOption opt: expected) {
+                if (!options.contains(opt))
+                    fail(opt.name() + " should be supported");
+            }
+
+            InitMaxStreams streams = InitMaxStreams.create(1024, 1024);
+            sc.setOption(SCTP_INIT_MAXSTREAMS, streams);
+            checkOption(sc, SCTP_INIT_MAXSTREAMS, streams);
+            streams = sc.getOption(SCTP_INIT_MAXSTREAMS);
+            check(streams.maxInStreams() == 1024, "Max in streams: value: "
+                    + streams.maxInStreams() + ", expected 1024 ");
+            check(streams.maxOutStreams() == 1024, "Max out streams: value: "
+                    + streams.maxOutStreams() + ", expected 1024 ");
+
+            optionalSupport(sc, SCTP_DISABLE_FRAGMENTS, true);
+            optionalSupport(sc, SCTP_EXPLICIT_COMPLETE, true);
+            optionalSupport(sc, SCTP_FRAGMENT_INTERLEAVE, 1);
+
+
+            //TODO: SCTP_PRIMARY_ADDR
+            //sc.bind(null);
+            //connect
+            //InetSocketAddress addr = new InetSocketAddress(0);
+            //sc.setOption(SCTP_PRIMARY_ADDR, addr);
+
+            sc.setOption(SCTP_NODELAY, true);
+            checkOption(sc, SCTP_NODELAY, true);
+            sc.setOption(SO_SNDBUF, 16*1024);
+            sc.setOption(SO_RCVBUF, 16*1024);
+            checkOption(sc, SO_LINGER, -1);  /* default should be negative */
+            sc.setOption(SO_LINGER, 2000);
+            checkOption(sc, SO_LINGER, 2000);
+
+
+            /* NullPointerException */
+            try {
+                sc.setOption(null, "value");
+                fail("NullPointerException not thrown for setOption");
+            } catch (NullPointerException unused) {
+                pass();
+            }
+            try {
+               sc.getOption(null);
+               fail("NullPointerException not thrown for getOption");
+            } catch (NullPointerException unused) {
+               pass();
+            }
+
+            /* ClosedChannelException */
+            sc.close();
+            try {
+               sc.setOption(SCTP_INIT_MAXSTREAMS, streams);
+               fail("ClosedChannelException not thrown");
+            } catch (ClosedChannelException unused) {
+                pass();
+            }
+        } catch (IOException ioe) {
+            unexpected(ioe);
+        }
+    }
+
+            //--------------------- Infrastructure ---------------------------
+    boolean debug = true;
+    volatile int passed = 0, failed = 0;
+    void pass() {passed++;}
+    void fail() {failed++; Thread.dumpStack();}
+    void fail(String msg) {System.err.println(msg); fail();}
+    void unexpected(Throwable t) {failed++; t.printStackTrace();}
+    void check(boolean cond) {if (cond) pass(); else fail();}
+    void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
+    void debug(String message) {if(debug) { System.out.println(message); }  }
+    public static void main(String[] args) throws Throwable {
+        Class<?> k = new Object(){}.getClass().getEnclosingClass();
+        try {k.getMethod("instanceMain",String[].class)
+                .invoke( k.newInstance(), (Object) args);}
+        catch (Throwable e) {throw e.getCause();}}
+    public void instanceMain(String[] args) throws Throwable {
+        try {test(args);} catch (Throwable t) {unexpected(t);}
+        System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
+        if (failed > 0) throw new AssertionError("Some tests failed");}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/SctpChannel/Util.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2009 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.NetworkInterface;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.SocketException;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.nio.ByteBuffer;
+import com.sun.nio.sctp.SctpChannel;
+import static java.lang.System.out;
+
+public class Util {
+    static final int SMALL_BUFFER = 128;
+    static final String SMALL_MESSAGE =
+      "Under the bridge and over the dam, looking for berries, berries for jam";
+
+    static final int LARGE_BUFFER = 32768;
+    static final String LARGE_MESSAGE;
+
+    static {
+        StringBuffer sb = new StringBuffer(LARGE_BUFFER);
+        for (int i=0; i<460; i++)
+          sb.append(SMALL_MESSAGE);
+
+        LARGE_MESSAGE = sb.toString();
+    }
+
+    static boolean isSCTPSupported() {
+        try {
+            SctpChannel c = SctpChannel.open();
+            c.close();
+            return true;
+        } catch (IOException ioe) {
+            ioe.printStackTrace();
+        } catch (UnsupportedOperationException e) {
+            out.println(e);
+        }
+
+        return false;
+    }
+    /**
+     * Returns a list of all the addresses on the system.
+     * @param  inclLoopback
+     *         if {@code true}, include the loopback addresses
+     * @param  ipv4Only
+     *         it {@code true}, only IPv4 addresses will be included
+     */
+    static List<InetAddress> getAddresses(boolean inclLoopback,
+                                          boolean ipv4Only)
+        throws SocketException {
+        ArrayList<InetAddress> list = new ArrayList<InetAddress>();
+        Enumeration<NetworkInterface> nets =
+                 NetworkInterface.getNetworkInterfaces();
+        for (NetworkInterface netInf : Collections.list(nets)) {
+            Enumeration<InetAddress> addrs = netInf.getInetAddresses();
+            for (InetAddress addr : Collections.list(addrs)) {
+                if (!list.contains(addr) &&
+                        (inclLoopback ? true : !addr.isLoopbackAddress()) &&
+                        (ipv4Only ? (addr instanceof Inet4Address) : true)) {
+                    list.add(addr);
+                }
+            }
+        }
+
+        return list;
+    }
+
+    static void dumpAddresses(SctpChannel channel,
+                              PrintStream printStream)
+        throws IOException {
+        Set<SocketAddress> addrs = channel.getAllLocalAddresses();
+        printStream.println("Local Addresses: ");
+        for (Iterator<SocketAddress> it = addrs.iterator(); it.hasNext(); ) {
+            InetSocketAddress addr = (InetSocketAddress)it.next();
+            printStream.println("\t" + addr);
+        }
+    }
+
+    /**
+     * Compare the contents of the given ByteBuffer with the contens of the
+     * given byte array. true if, and only if, the contents are the same.
+     */
+    static boolean compare(ByteBuffer bb, byte[] message) {
+        if (message.length != bb.remaining()) {
+            out.println("Compare failed, byte array length != to buffer remaining");
+            return false;
+        }
+
+        for (int i=0; i<message.length; i++) {
+            byte b = bb.get();
+            if (message[i] != b) {
+                out.println("Position " + i + ": " + message[i] + " != " + b);
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    static boolean compare(ByteBuffer bb, String str) {
+        try{
+            return Util.compare(bb, str.getBytes("ISO-8859-1"));
+        } catch (UnsupportedEncodingException unsupported) {
+            throw new AssertionError(unsupported);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/SctpMultiChannel/Send.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,320 @@
+/*
+ * Copyright 2009 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 4927640
+ * @summary Tests the SCTP protocol implementation
+ * @author chegar
+ */
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.io.IOException;
+import java.util.Set;
+import java.util.Iterator;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.nio.ByteBuffer;
+import com.sun.nio.sctp.Association;
+import com.sun.nio.sctp.InvalidStreamException;
+import com.sun.nio.sctp.MessageInfo;
+import com.sun.nio.sctp.SctpMultiChannel;
+import static java.lang.System.out;
+import static java.lang.System.err;
+
+public class Send {
+    /* Latches used to synchronize between the client and server so that
+     * connections without any IO may not be closed without being accepted */
+    final CountDownLatch clientFinishedLatch = new CountDownLatch(1);
+    final CountDownLatch serverFinishedLatch = new CountDownLatch(1);
+
+    void test(String[] args) {
+        SocketAddress address = null;
+        Server server = null;
+
+        if (!Util.isSCTPSupported()) {
+            out.println("SCTP protocol is not supported");
+            out.println("Test cannot be run");
+            return;
+        }
+
+        if (args.length == 2) {
+            /* requested to connecct to a specific address */
+            try {
+                int port = Integer.valueOf(args[1]);
+                address = new InetSocketAddress(args[0], port);
+            } catch (NumberFormatException nfe) {
+                err.println(nfe);
+            }
+        } else {
+            /* start server on local machine, default */
+            try {
+                server = new Server();
+                server.start();
+                address = server.address();
+                debug("Server started and listening on " + address);
+            } catch (IOException ioe) {
+                ioe.printStackTrace();
+                return;
+            }
+        }
+
+        doTest(address);
+    }
+
+    void doTest(SocketAddress peerAddress) {
+        SctpMultiChannel channel = null;
+        ByteBuffer buffer = ByteBuffer.allocate(Util.LARGE_BUFFER);
+        MessageInfo info = MessageInfo.createOutgoing(null, 0);
+
+        try {
+            channel = SctpMultiChannel.open();
+
+            /* TEST 1: send small message */
+            int streamNumber = 0;
+            debug("sending to " + peerAddress + " on stream number: " + streamNumber);
+            info = MessageInfo.createOutgoing(peerAddress, streamNumber);
+            buffer.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1"));
+            buffer.flip();
+            int position = buffer.position();
+            int remaining = buffer.remaining();
+
+            debug("sending small message: " + buffer);
+            int sent = channel.send(buffer, info);
+
+            check(sent == remaining, "sent should be equal to remaining");
+            check(buffer.position() == (position + sent),
+                    "buffers position should have been incremented by sent");
+
+            /* TEST 2: receive the echoed message */
+            buffer.clear();
+            info = channel.receive(buffer, null, null);
+            buffer.flip();
+            check(info != null, "info is null");
+            check(info.streamNumber() == streamNumber,
+                    "message not sent on the correct stream");
+            check(info.bytes() == Util.SMALL_MESSAGE.getBytes("ISO-8859-1").
+                  length, "bytes received not equal to message length");
+            check(info.bytes() == buffer.remaining(), "bytes != remaining");
+            check(Util.compare(buffer, Util.SMALL_MESSAGE),
+              "received message not the same as sent message");
+
+
+            /* TEST 3: send large message */
+            Set<Association> assocs = channel.associations();
+            check(assocs.size() == 1, "there should be only one association");
+            Iterator<Association> it = assocs.iterator();
+            check(it.hasNext());
+            Association assoc = it.next();
+            streamNumber = assoc.maxOutboundStreams() - 1;
+
+            debug("sending on stream number: " + streamNumber);
+            info = MessageInfo.createOutgoing(assoc, null, streamNumber);
+            buffer.clear();
+            buffer.put(Util.LARGE_MESSAGE.getBytes("ISO-8859-1"));
+            buffer.flip();
+            position = buffer.position();
+            remaining = buffer.remaining();
+
+            debug("sending large message: " + buffer);
+            sent = channel.send(buffer, info);
+
+            check(sent == remaining, "sent should be equal to remaining");
+            check(buffer.position() == (position + sent),
+                    "buffers position should have been incremented by sent");
+
+            /* TEST 4: receive the echoed message */
+            buffer.clear();
+            info = channel.receive(buffer, null, null);
+            buffer.flip();
+            check(info != null, "info is null");
+            check(info.streamNumber() == streamNumber,
+                    "message not sent on the correct stream");
+            check(info.bytes() == Util.LARGE_MESSAGE.getBytes("ISO-8859-1").
+                  length, "bytes received not equal to message length");
+            check(info.bytes() == buffer.remaining(), "bytes != remaining");
+            check(Util.compare(buffer, Util.LARGE_MESSAGE),
+              "received message not the same as sent message");
+
+
+            /* TEST 5: InvalidStreamExcepton */
+            streamNumber = assoc.maxOutboundStreams() + 1;
+            info = MessageInfo.createOutgoing(assoc, null, streamNumber);
+            buffer.clear();
+            buffer.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1"));
+            buffer.flip();
+            position = buffer.position();
+            remaining = buffer.remaining();
+
+            debug("sending on stream number: " + streamNumber);
+            debug("sending small message: " + buffer);
+            try {
+                sent = channel.send(buffer, info);
+                fail("should have thrown InvalidStreamExcepton");
+            } catch (InvalidStreamException ise){
+                pass();
+            } catch (IOException ioe) {
+                unexpected(ioe);
+            }
+            check(buffer.remaining() == remaining,
+                    "remaining should not be changed");
+            check(buffer.position() == position,
+                    "buffers position should not be changed");
+
+
+            /* TEST 5: getRemoteAddresses(Association) */
+            channel.getRemoteAddresses(assoc);
+
+        } catch (IOException ioe) {
+            unexpected(ioe);
+        } finally {
+            clientFinishedLatch.countDown();
+            try { serverFinishedLatch.await(10L, TimeUnit.SECONDS); }
+            catch (InterruptedException ie) { unexpected(ie); }
+            if (channel != null) {
+                try { channel.close(); }
+                catch (IOException e) { unexpected (e);}
+            }
+        }
+    }
+
+    class Server implements Runnable
+    {
+        final InetSocketAddress serverAddr;
+        private SctpMultiChannel serverChannel;
+
+        public Server() throws IOException {
+            serverChannel = SctpMultiChannel.open().bind(null);
+            java.util.Set<SocketAddress> addrs = serverChannel.getAllLocalAddresses();
+            if (addrs.isEmpty())
+                debug("addrs should not be empty");
+
+            serverAddr = (InetSocketAddress) addrs.iterator().next();
+        }
+
+        public void start() {
+            (new Thread(this, "Server-"  + serverAddr.getPort())).start();
+        }
+
+        public InetSocketAddress address() {
+            return serverAddr;
+        }
+
+        @Override
+        public void run() {
+            ByteBuffer buffer = ByteBuffer.allocateDirect(Util.LARGE_BUFFER);
+            try {
+                MessageInfo info;
+
+                /* receive a small message */
+                do {
+                    info = serverChannel.receive(buffer, null, null);
+                    if (info == null) {
+                        fail("Server: unexpected null from receive");
+                            return;
+                    }
+                } while (!info.isComplete());
+
+                buffer.flip();
+                check(info != null, "info is null");
+                check(info.streamNumber() == 0,
+                        "message not sent on the correct stream");
+                check(info.bytes() == Util.SMALL_MESSAGE.getBytes("ISO-8859-1").
+                      length, "bytes received not equal to message length");
+                check(info.bytes() == buffer.remaining(), "bytes != remaining");
+                check(Util.compare(buffer, Util.SMALL_MESSAGE),
+                  "received message not the same as sent message");
+
+                check(info != null, "info is null");
+                Set<Association> assocs = serverChannel.associations();
+                check(assocs.size() == 1, "there should be only one association");
+                Iterator<Association> it = assocs.iterator();
+                check(it.hasNext());
+                Association assoc = it.next();
+
+                /* echo the message */
+                debug("Server: echoing first message");
+                buffer.flip();
+                int bytes = serverChannel.send(buffer, info);
+                debug("Server: sent " + bytes + "bytes");
+
+                /* receive a large message */
+                buffer.clear();
+                do {
+                    info = serverChannel.receive(buffer, null, null);
+                    if (info == null) {
+                        fail("Server: unexpected null from receive");
+                            return;
+                    }
+                } while (!info.isComplete());
+
+                buffer.flip();
+
+                check(info.streamNumber() == assoc.maxInboundStreams() - 1,
+                        "message not sent on the correct stream");
+                check(info.bytes() == Util.LARGE_MESSAGE.getBytes("ISO-8859-1").
+                      length, "bytes received not equal to message length");
+                check(info.bytes() == buffer.remaining(), "bytes != remaining");
+                check(Util.compare(buffer, Util.LARGE_MESSAGE),
+                  "received message not the same as sent message");
+
+                /* echo the message */
+                debug("Server: echoing second message");
+                buffer.flip();
+                bytes = serverChannel.send(buffer, info);
+                debug("Server: sent " + bytes + "bytes");
+
+                clientFinishedLatch.await(10L, TimeUnit.SECONDS);
+                serverFinishedLatch.countDown();
+            } catch (IOException ioe) {
+                unexpected(ioe);
+            } catch (InterruptedException ie) {
+                unexpected(ie);
+            } finally {
+                try { if (serverChannel != null) serverChannel.close(); }
+                catch (IOException  unused) {}
+            }
+        }
+    }
+
+        //--------------------- Infrastructure ---------------------------
+    boolean debug = true;
+    volatile int passed = 0, failed = 0;
+    void pass() {passed++;}
+    void fail() {failed++; Thread.dumpStack();}
+    void fail(String msg) {System.err.println(msg); fail();}
+    void unexpected(Throwable t) {failed++; t.printStackTrace();}
+    void check(boolean cond) {if (cond) pass(); else fail();}
+    void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
+    void debug(String message) {if(debug) { System.out.println(message); }  }
+    public static void main(String[] args) throws Throwable {
+        Class<?> k = new Object(){}.getClass().getEnclosingClass();
+        try {k.getMethod("instanceMain",String[].class)
+                .invoke( k.newInstance(), (Object) args);}
+        catch (Throwable e) {throw e.getCause();}}
+    public void instanceMain(String[] args) throws Throwable {
+        try {test(args);} catch (Throwable t) {unexpected(t);}
+        System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
+        if (failed > 0) throw new AssertionError("Some tests failed");}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/SctpMultiChannel/Util.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2009 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.NetworkInterface;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.SocketException;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.nio.ByteBuffer;
+import com.sun.nio.sctp.SctpChannel;
+import static java.lang.System.out;
+
+public class Util {
+    static final int SMALL_BUFFER = 128;
+    static final String SMALL_MESSAGE =
+      "Under the bridge and over the dam, looking for berries, berries for jam";
+
+    static final int LARGE_BUFFER = 32768;
+    static final String LARGE_MESSAGE;
+
+    static {
+        StringBuffer sb = new StringBuffer(LARGE_BUFFER);
+        for (int i=0; i<460; i++)
+          sb.append(SMALL_MESSAGE);
+
+        LARGE_MESSAGE = sb.toString();
+    }
+
+    static boolean isSCTPSupported() {
+        try {
+            SctpChannel c = SctpChannel.open();
+            c.close();
+            return true;
+        } catch (IOException ioe) {
+            ioe.printStackTrace();
+        } catch (UnsupportedOperationException e) {
+            out.println(e);
+        }
+
+        return false;
+    }
+    /**
+     * Returns a list of all the addresses on the system.
+     * @param  inclLoopback
+     *         if {@code true}, include the loopback addresses
+     * @param  ipv4Only
+     *         it {@code true}, only IPv4 addresses will be included
+     */
+    static List<InetAddress> getAddresses(boolean inclLoopback,
+                                          boolean ipv4Only)
+        throws SocketException {
+        ArrayList<InetAddress> list = new ArrayList<InetAddress>();
+        Enumeration<NetworkInterface> nets =
+                 NetworkInterface.getNetworkInterfaces();
+        for (NetworkInterface netInf : Collections.list(nets)) {
+            Enumeration<InetAddress> addrs = netInf.getInetAddresses();
+            for (InetAddress addr : Collections.list(addrs)) {
+                if (!list.contains(addr) &&
+                        (inclLoopback ? true : !addr.isLoopbackAddress()) &&
+                        (ipv4Only ? (addr instanceof Inet4Address) : true)) {
+                    list.add(addr);
+                }
+            }
+        }
+
+        return list;
+    }
+
+    static void dumpAddresses(SctpChannel channel,
+                              PrintStream printStream)
+        throws IOException {
+        Set<SocketAddress> addrs = channel.getAllLocalAddresses();
+        printStream.println("Local Addresses: ");
+        for (Iterator<SocketAddress> it = addrs.iterator(); it.hasNext(); ) {
+            InetSocketAddress addr = (InetSocketAddress)it.next();
+            printStream.println("\t" + addr);
+        }
+    }
+
+    /**
+     * Compare the contents of the given ByteBuffer with the contens of the
+     * given byte array. true if, and only if, the contents are the same.
+     */
+    static boolean compare(ByteBuffer bb, byte[] message) {
+        if (message.length != bb.remaining()) {
+            out.println("Compare failed, byte array length != to buffer remaining");
+            return false;
+        }
+
+        for (int i=0; i<message.length; i++) {
+            byte b = bb.get();
+            if (message[i] != b) {
+                out.println("Position " + i + ": " + message[i] + " != " + b);
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    static boolean compare(ByteBuffer bb, String str) {
+        try{
+            return Util.compare(bb, str.getBytes("ISO-8859-1"));
+        } catch (UnsupportedEncodingException unsupported) {
+            throw new AssertionError(unsupported);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/SctpServerChannel/Accept.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2009 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 4927640
+ * @summary Tests the SCTP protocol implementation
+ * @author chegar
+ */
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.nio.channels.AlreadyConnectedException;
+import java.nio.channels.AsynchronousCloseException;
+import java.nio.channels.NotYetBoundException;
+import java.nio.channels.ClosedByInterruptException;
+import java.nio.channels.ClosedChannelException;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpServerChannel;
+import static java.lang.System.out;
+import static java.lang.System.err;
+
+public class Accept {
+    static CountDownLatch acceptLatch = new CountDownLatch(1);
+    static CountDownLatch closeByIntLatch = new CountDownLatch(1);
+    static CountDownLatch asyncCloseLatch = new CountDownLatch(1);
+    AcceptServer server = null;
+
+    void test(String[] args) {
+        SocketAddress address = null;
+
+        if (!Util.isSCTPSupported()) {
+            out.println("SCTP protocol is not supported");
+            out.println("Test cannot be run");
+            return;
+        }
+
+        if (args.length == 2) {
+            /* requested to connecct to a specific address */
+            try {
+                int port = Integer.valueOf(args[1]);
+                address = new InetSocketAddress(args[0], port);
+            } catch (NumberFormatException nfe) {
+                err.println(nfe);
+            }
+        } else {
+            /* start server on local machine, default */
+            try {
+                server = new AcceptServer();
+                server.start();
+                address = server.address();
+                debug("Server started and listening on " + address);
+            } catch (IOException ioe) {
+                ioe.printStackTrace();
+                return;
+            }
+        }
+
+        doClient(address);
+    }
+
+    void doClient(SocketAddress peerAddress) {
+        SctpChannel channel = null;
+
+        try {
+            channel = SctpChannel.open(peerAddress, 0, 0);
+            acceptLatch.await();
+
+            /* for test 4 */
+            closeByIntLatch.await();
+            sleep(500);
+            server.thread().interrupt();
+
+            /* for test 5 */
+            asyncCloseLatch.await();
+            sleep(500);
+            server.channel().close();
+
+            /* wait for the server thread to finish */
+            join(server.thread(), 10000);
+        } catch (IOException ioe) {
+            unexpected(ioe);
+        } catch (InterruptedException ie) {
+            unexpected(ie);
+        } finally {
+            try { if (channel != null) channel.close(); }
+            catch (IOException e) { unexpected(e);}
+        }
+    }
+
+    class AcceptServer implements Runnable
+    {
+        final InetSocketAddress serverAddr;
+        private SctpServerChannel ssc;
+        private Thread serverThread;
+
+        public AcceptServer() throws IOException {
+            ssc = SctpServerChannel.open();
+
+            /* TEST 1: NotYetBoundException */
+            debug("TEST 1: NotYetBoundException");
+            try {
+                ssc.accept();
+                fail();
+            } catch (NotYetBoundException nybe) {
+                debug("  caught NotYetBoundException");
+                pass();
+            } catch (IOException ioe) {
+                unexpected(ioe);
+            }
+
+            ssc.bind(null);
+            java.util.Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
+            if (addrs.isEmpty())
+                debug("addrs should not be empty");
+
+            serverAddr = (InetSocketAddress) addrs.iterator().next();
+
+            /* TEST 2: null if this channel is in non-blocking mode and no
+             *         association is available to be accepted  */
+            ssc.configureBlocking(false);
+            debug("TEST 2: non-blocking mode null");
+            try {
+                SctpChannel sc = ssc.accept();
+                check(sc == null, "non-blocking mode should return null");
+            } catch (IOException ioe) {
+                unexpected(ioe);
+            } finally {
+                ssc.configureBlocking(true);
+            }
+        }
+
+        void start() {
+            serverThread = new Thread(this, "AcceptServer-"  +
+                                              serverAddr.getPort());
+            serverThread.start();
+        }
+
+        InetSocketAddress address() {
+            return serverAddr;
+        }
+
+        SctpServerChannel channel() {
+            return ssc;
+        }
+
+        Thread thread() {
+            return serverThread;
+        }
+
+        @Override
+        public void run() {
+            SctpChannel sc = null;
+            try {
+                /* TEST 3: accepted channel */
+                debug("TEST 3: accepted channel");
+                sc = ssc.accept();
+
+                checkAcceptedChannel(sc);
+                acceptLatch.countDown();
+
+                /* TEST 4: ClosedByInterruptException */
+                debug("TEST 4: ClosedByInterruptException");
+                try {
+                    closeByIntLatch.countDown();
+                    ssc.accept();
+                    fail();
+                } catch (ClosedByInterruptException unused) {
+                    debug("  caught ClosedByInterruptException");
+                    pass();
+                }
+
+                /* TEST 5: AsynchronousCloseException */
+                debug("TEST 5: AsynchronousCloseException");
+                /* reset thread interrupt status */
+                Thread.currentThread().interrupted();
+
+                ssc = SctpServerChannel.open().bind(null);
+                try {
+                    asyncCloseLatch.countDown();
+                    ssc.accept();
+                    fail();
+                } catch (AsynchronousCloseException unused) {
+                    debug("  caught AsynchronousCloseException");
+                    pass();
+                }
+
+                /* TEST 6: ClosedChannelException */
+                debug("TEST 6: ClosedChannelException");
+                try {
+                    ssc.accept();
+                    fail();
+                } catch (ClosedChannelException unused) {
+                    debug("  caught ClosedChannelException");
+                    pass();
+                }
+                ssc = null;
+            } catch (IOException ioe) {
+                ioe.printStackTrace();
+            } finally {
+                try { if (ssc != null) ssc.close(); }
+                catch (IOException  ioe) { unexpected(ioe); }
+                try { if (sc != null) sc.close(); }
+                catch (IOException  ioe) { unexpected(ioe); }
+            }
+        }
+    }
+
+    void checkAcceptedChannel(SctpChannel sc) {
+        try {
+            debug("Checking accepted SctpChannel");
+            check(sc.association() != null,
+                  "accepted channel should have an association");
+            check(!(sc.getRemoteAddresses().isEmpty()),
+                  "accepted channel should be connected");
+            check(!(sc.isConnectionPending()),
+                  "accepted channel should not have a connection pending");
+            check(sc.isBlocking(),
+                  "accepted channel should be blocking");
+            try { sc.connect(new TestSocketAddress()); fail(); }
+            catch (AlreadyConnectedException unused) { pass(); }
+            try { sc.bind(new TestSocketAddress()); fail(); }
+            catch (AlreadyConnectedException unused) { pass(); }
+        } catch (IOException unused) { fail(); }
+    }
+
+    static class TestSocketAddress extends SocketAddress {}
+
+        //--------------------- Infrastructure ---------------------------
+    boolean debug = true;
+    volatile int passed = 0, failed = 0;
+    void pass() {passed++;}
+    void fail() {failed++; Thread.dumpStack();}
+    void fail(String msg) {err.println(msg); fail();}
+    void unexpected(Throwable t) {failed++; t.printStackTrace();}
+    void check(boolean cond) {if (cond) pass(); else fail();}
+    void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
+    void debug(String message) {if(debug) { out.println(message); }  }
+    void sleep(long millis) { try { Thread.currentThread().sleep(millis); }
+                          catch(InterruptedException ie) { unexpected(ie); }}
+    void join(Thread thread, long millis) { try { thread.join(millis); }
+                          catch(InterruptedException ie) { unexpected(ie); }}
+    public static void main(String[] args) throws Throwable {
+        Class<?> k = new Object(){}.getClass().getEnclosingClass();
+        try {k.getMethod("instanceMain",String[].class)
+                .invoke( k.newInstance(), (Object) args);}
+        catch (Throwable e) {throw e.getCause();}}
+    public void instanceMain(String[] args) throws Throwable {
+        try {test(args);} catch (Throwable t) {unexpected(t);}
+        out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
+        if (failed > 0) throw new AssertionError("Some tests failed");}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/SctpServerChannel/NonBlockingAccept.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2009 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 4927640
+ * @summary Tests the SCTP protocol implementation
+ * @author chegar
+ */
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.nio.channels.AlreadyConnectedException;
+import java.nio.channels.Selector;
+import java.nio.channels.SelectionKey;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpServerChannel;
+import static java.lang.System.out;
+import static java.lang.System.err;
+
+public class NonBlockingAccept {
+    static CountDownLatch acceptLatch = new CountDownLatch(1);
+    static final int SEL_TIMEOUT = 10000;
+    static final int NUM_TEST_CONNECTIONS = 10;
+
+    void test(String[] args) {
+        SocketAddress address = null;
+        NonblockingServer server;
+
+        if (!Util.isSCTPSupported()) {
+            out.println("SCTP protocol is not supported");
+            out.println("Test cannot be run");
+            return;
+        }
+
+        if (args.length == 2) {
+            /* requested to connecct to a specific address */
+            try {
+                int port = Integer.valueOf(args[1]);
+                address = new InetSocketAddress(args[0], port);
+            } catch (NumberFormatException nfe) {
+                err.println(nfe);
+            }
+        } else {
+            /* start server on local machine, default */
+            try {
+                server = new NonblockingServer();
+                server.start();
+                address = server.address();
+                debug("Server started and listening on " + address);
+            } catch (IOException ioe) {
+                ioe.printStackTrace();
+                return;
+            }
+        }
+
+        doClient(address);
+    }
+
+    void doClient(SocketAddress peerAddress) {
+        Set<SctpChannel> channels = new HashSet<SctpChannel>(NUM_TEST_CONNECTIONS);
+
+        try {
+            for (int i=0; i<NUM_TEST_CONNECTIONS;) {
+                debug("connecting " + ++i);
+                channels.add(SctpChannel.open(peerAddress, 0, 0));
+                sleep(100);
+            }
+
+            /* don't close the channels until they have been accepted */
+            acceptLatch.await();
+
+            for(SctpChannel sc: channels)
+                sc.close();
+        } catch (IOException ioe) {
+            unexpected(ioe);
+        } catch (InterruptedException ie) {
+            unexpected(ie);
+        }
+    }
+
+    class NonblockingServer implements Runnable
+    {
+        final InetSocketAddress serverAddr;
+        private SctpServerChannel ssc;
+        private Thread serverThread;
+
+        public NonblockingServer() throws IOException {
+            ssc = SctpServerChannel.open().bind(null);
+            java.util.Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
+            if (addrs.isEmpty())
+                debug("addrs should not be empty");
+
+            serverAddr = (InetSocketAddress) addrs.iterator().next();
+        }
+
+        void start() {
+            serverThread = new Thread(this, "NonblockingServer-"  +
+                                              serverAddr.getPort());
+            serverThread.start();
+        }
+
+        InetSocketAddress address () {
+            return serverAddr;
+        }
+
+        @Override
+        public void run() {
+            Selector acceptSelector = null;
+            SelectionKey acceptKey = null;
+
+            try {
+                acceptSelector = Selector.open();
+                ssc.configureBlocking(false);
+                check(ssc.isBlocking() == false, "Should be in non-blocking mode");
+                acceptKey = ssc.register(acceptSelector, SelectionKey.OP_ACCEPT);
+
+                int connectionsAccepted = 0;
+                while (connectionsAccepted < NUM_TEST_CONNECTIONS) {
+                    int keysAdded = acceptSelector.select(SEL_TIMEOUT);
+                    if (keysAdded > 0) {
+                        Set<SelectionKey> keys = acceptSelector.selectedKeys();
+                        Iterator<SelectionKey> i = keys.iterator();
+                        while(i.hasNext()) {
+                            SelectionKey sk = i.next();
+                            i.remove();
+                            SctpServerChannel nextReady =
+                                (SctpServerChannel)sk.channel();
+                            check(nextReady.equals(ssc),
+                                    "channels should be equal");
+                            check(sk.isAcceptable(),
+                                    "key should be acceptable");
+                            check(!sk.isReadable(),
+                                    "key should not be readable");
+                            check(!sk.isWritable(),
+                                    "key should not be writable");
+                            check(!sk.isConnectable(),
+                                    "key should not be connectable");
+                            SctpChannel acceptsc = nextReady.accept();
+                            connectionsAccepted++;
+                            debug("Accepted " + connectionsAccepted + " connections");
+                            check(acceptsc != null,
+                                    "Accepted channel should not be null");
+                            if (acceptsc != null) {
+                                checkAcceptedChannel(acceptsc);
+                                acceptsc.close();
+                            }
+                        } /* while */
+                    } /* if */
+                } /* while */
+            } catch (IOException ioe) {
+                ioe.printStackTrace();
+            } finally {
+                acceptLatch.countDown();
+                if (acceptKey != null) acceptKey.cancel();
+                try { if (acceptSelector != null) acceptSelector.close(); }
+                catch (IOException  ioe) { unexpected(ioe); }
+                try { if (ssc != null) ssc.close(); }
+                catch (IOException  ioe) { unexpected(ioe); }
+            }
+        }
+    }
+
+    void checkAcceptedChannel(SctpChannel sc) {
+        try {
+            debug("Checking accepted SctpChannel");
+            check(sc.association() != null,
+                  "accepted channel should have an association");
+            check(!(sc.getRemoteAddresses().isEmpty()),
+                  "accepted channel should be connected");
+            check(!(sc.isConnectionPending()),
+                  "accepted channel should not have a connection pending");
+            check(sc.isBlocking(),
+                  "accepted channel should be blocking");
+            try { sc.connect(new TestSocketAddress()); fail(); }
+            catch (AlreadyConnectedException unused) { pass(); }
+            try { sc.bind(new TestSocketAddress()); fail(); }
+            catch (AlreadyConnectedException unused) { pass(); }
+        } catch (IOException unused) { fail(); }
+    }
+
+    static class TestSocketAddress extends SocketAddress {}
+
+        //--------------------- Infrastructure ---------------------------
+    boolean debug = true;
+    volatile int passed = 0, failed = 0;
+    void pass() {passed++;}
+    void fail() {failed++; Thread.dumpStack();}
+    void fail(String msg) {err.println(msg); fail();}
+    void unexpected(Throwable t) {failed++; t.printStackTrace();}
+    void check(boolean cond) {if (cond) pass(); else fail();}
+    void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
+    void debug(String message) {if(debug) { out.println(message); }  }
+    void sleep(long millis) { try { Thread.currentThread().sleep(millis); }
+                          catch(InterruptedException ie) { unexpected(ie); }}
+    public static void main(String[] args) throws Throwable {
+        Class<?> k = new Object(){}.getClass().getEnclosingClass();
+        try {k.getMethod("instanceMain",String[].class)
+                .invoke( k.newInstance(), (Object) args);}
+        catch (Throwable e) {throw e.getCause();}}
+    public void instanceMain(String[] args) throws Throwable {
+        try {test(args);} catch (Throwable t) {unexpected(t);}
+        out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
+        if (failed > 0) throw new AssertionError("Some tests failed");}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/SctpServerChannel/Util.java	Thu Apr 16 17:42:00 2009 +0100
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2009 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.NetworkInterface;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.SocketException;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.nio.ByteBuffer;
+import com.sun.nio.sctp.SctpChannel;
+import static java.lang.System.out;
+
+public class Util {
+    static final int SMALL_BUFFER = 128;
+    static final String SMALL_MESSAGE =
+      "Under the bridge and over the dam, looking for berries, berries for jam";
+
+    static final int LARGE_BUFFER = 32768;
+    static final String LARGE_MESSAGE;
+
+    static {
+        StringBuffer sb = new StringBuffer(LARGE_BUFFER);
+        for (int i=0; i<460; i++)
+          sb.append(SMALL_MESSAGE);
+
+        LARGE_MESSAGE = sb.toString();
+    }
+
+    static boolean isSCTPSupported() {
+        try {
+            SctpChannel c = SctpChannel.open();
+            c.close();
+            return true;
+        } catch (IOException ioe) {
+            ioe.printStackTrace();
+        } catch (UnsupportedOperationException e) {
+            out.println(e);
+        }
+
+        return false;
+    }
+    /**
+     * Returns a list of all the addresses on the system.
+     * @param  inclLoopback
+     *         if {@code true}, include the loopback addresses
+     * @param  ipv4Only
+     *         it {@code true}, only IPv4 addresses will be included
+     */
+    static List<InetAddress> getAddresses(boolean inclLoopback,
+                                          boolean ipv4Only)
+        throws SocketException {
+        ArrayList<InetAddress> list = new ArrayList<InetAddress>();
+        Enumeration<NetworkInterface> nets =
+                 NetworkInterface.getNetworkInterfaces();
+        for (NetworkInterface netInf : Collections.list(nets)) {
+            Enumeration<InetAddress> addrs = netInf.getInetAddresses();
+            for (InetAddress addr : Collections.list(addrs)) {
+                if (!list.contains(addr) &&
+                        (inclLoopback ? true : !addr.isLoopbackAddress()) &&
+                        (ipv4Only ? (addr instanceof Inet4Address) : true)) {
+                    list.add(addr);
+                }
+            }
+        }
+
+        return list;
+    }
+
+    static void dumpAddresses(SctpChannel channel,
+                              PrintStream printStream)
+        throws IOException {
+        Set<SocketAddress> addrs = channel.getAllLocalAddresses();
+        printStream.println("Local Addresses: ");
+        for (Iterator<SocketAddress> it = addrs.iterator(); it.hasNext(); ) {
+            InetSocketAddress addr = (InetSocketAddress)it.next();
+            printStream.println("\t" + addr);
+        }
+    }
+
+    /**
+     * Compare the contents of the given ByteBuffer with the contens of the
+     * given byte array. true if, and only if, the contents are the same.
+     */
+    static boolean compare(ByteBuffer bb, byte[] message) {
+        if (message.length != bb.remaining()) {
+            out.println("Compare failed, byte array length != to buffer remaining");
+            return false;
+        }
+
+        for (int i=0; i<message.length; i++) {
+            byte b = bb.get();
+            if (message[i] != b) {
+                out.println("Position " + i + ": " + message[i] + " != " + b);
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    static boolean compare(ByteBuffer bb, String str) {
+        try{
+            return Util.compare(bb, str.getBytes("ISO-8859-1"));
+        } catch (UnsupportedEncodingException unsupported) {
+            throw new AssertionError(unsupported);
+        }
+    }
+}