--- a/jdk/make/com/sun/nio/sctp/mapfile-vers Mon Jun 29 13:29:05 2009 +0100
+++ b/jdk/make/com/sun/nio/sctp/mapfile-vers Mon Jun 29 14:53:10 2009 +0100
@@ -27,6 +27,7 @@
global:
Java_sun_nio_ch_SctpNet_socket0;
Java_sun_nio_ch_SctpNet_bindx;
+ Java_sun_nio_ch_SctpNet_branch0;
Java_sun_nio_ch_SctpNet_getLocalAddresses0;
Java_sun_nio_ch_SctpNet_getRemoteAddresses0;
Java_sun_nio_ch_SctpNet_getPrimAddrOption0;
--- a/jdk/src/solaris/classes/sun/nio/ch/SctpChannelImpl.java Mon Jun 29 13:29:05 2009 +0100
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpChannelImpl.java Mon Jun 29 14:53:10 2009 +0100
@@ -26,6 +26,7 @@
import java.net.InetAddress;
import java.net.SocketAddress;
+import java.net.SocketException;
import java.net.InetSocketAddress;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -122,6 +123,8 @@
private Association association;
+ private Set<SocketAddress> remoteAddresses = Collections.EMPTY_SET;
+
/* -- End of fields protected by stateLock -- */
private SctpResultContainer commUpResultContainer; /* null */
@@ -142,18 +145,32 @@
*/
public SctpChannelImpl(SelectorProvider provider, FileDescriptor fd)
throws IOException {
+ this(provider, fd, null);
+ }
+
+ /**
+ * Constructor for sockets obtained from branching
+ */
+ public SctpChannelImpl(SelectorProvider provider,
+ FileDescriptor fd,
+ Association association)
+ 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);
+ if (association != null) { /* branched */
+ this.association = association;
+ } else { /* obtained from server channel */
+ /* Receive COMM_UP */
+ ByteBuffer buf = Util.getTemporaryDirectBuffer(50);
+ try {
+ receive(buf, null, null, true);
+ } finally {
+ Util.releaseTemporaryDirectBuffer(buf);
+ }
}
}
@@ -391,6 +408,12 @@
} finally {
Util.releaseTemporaryDirectBuffer(buf);
}
+
+ /* cache remote addresses */
+ try {
+ remoteAddresses = getRemoteAddresses();
+ } catch (IOException unused) { /* swallow exception */ }
+
return true;
}
} else {
@@ -414,6 +437,7 @@
int maxOutStreams,
int maxInStreams)
throws IOException {
+ ensureOpenAndUnconnected();
return setOption(SCTP_INIT_MAXSTREAMS, InitMaxStreams.
create(maxInStreams, maxOutStreams)).connect(endpoint);
@@ -512,6 +536,12 @@
} finally {
Util.releaseTemporaryDirectBuffer(buf);
}
+
+ /* cache remote addresses */
+ try {
+ remoteAddresses = getRemoteAddresses();
+ } catch (IOException unused) { /* swallow exception */ }
+
return true;
}
}
@@ -966,7 +996,7 @@
int pos = src.position();
int lim = src.limit();
- assert (pos <= lim && streamNumber > 0);
+ assert (pos <= lim && streamNumber >= 0);
int rem = (pos <= lim ? lim - pos : 0);
if (src instanceof DirectBuffer)
@@ -1043,10 +1073,15 @@
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
- if (!isConnected())
+ if (!isConnected() || isShutdown)
return Collections.EMPTY_SET;
- return SctpNet.getRemoteAddresses(fdVal, 0/*unused*/);
+ try {
+ return SctpNet.getRemoteAddresses(fdVal, 0/*unused*/);
+ } catch (SocketException unused) {
+ /* an open connected channel should always have remote addresses */
+ return remoteAddresses;
+ }
}
}
--- a/jdk/src/solaris/classes/sun/nio/ch/SctpMultiChannelImpl.java Mon Jun 29 13:29:05 2009 +0100
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpMultiChannelImpl.java Mon Jun 29 14:53:10 2009 +0100
@@ -26,6 +26,7 @@
import java.net.InetAddress;
import java.net.SocketAddress;
+import java.net.SocketException;
import java.net.InetSocketAddress;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -398,8 +399,8 @@
if (!isOpen())
throw new ClosedChannelException();
- SctpNet.setSocketOption(fdVal, name, value,
- association.associationID());
+ int assocId = association == null ? 0 : association.associationID();
+ SctpNet.setSocketOption(fdVal, name, value, assocId);
}
return this;
}
@@ -414,12 +415,15 @@
throw new UnsupportedOperationException("'" + name + "' not supported");
synchronized (stateLock) {
- checkAssociation(association);
+ if (association != null && (name.equals(SCTP_PRIMARY_ADDR) ||
+ name.equals(SCTP_SET_PEER_PRIMARY_ADDR))) {
+ checkAssociation(association);
+ }
if (!isOpen())
throw new ClosedChannelException();
- return (T)SctpNet.getSocketOption(fdVal, name,
- association.associationID());
+ int assocId = association == null ? 0 : association.associationID();
+ return (T)SctpNet.getSocketOption(fdVal, name, assocId);
}
}
@@ -626,15 +630,19 @@
case ASSOCIATION_CHANGED :
result = absHandler.handleNotification(
resultContainer.getAssociationChanged(), attachment);
+ break;
case PEER_ADDRESS_CHANGED :
result = absHandler.handleNotification(
resultContainer.getPeerAddressChanged(), attachment);
+ break;
case SEND_FAILED :
result = absHandler.handleNotification(
resultContainer.getSendFailed(), attachment);
+ break;
case SHUTDOWN :
result = absHandler.handleNotification(
resultContainer.getShutdown(), attachment);
+ break;
default :
/* implementation specific handlers */
result = absHandler.handleNotification(
@@ -836,7 +844,7 @@
int ppid = messageInfo.payloadProtocolID();
int pos = src.position();
int lim = src.limit();
- assert (pos <= lim && streamNumber > 0);
+ assert (pos <= lim && streamNumber >= 0);
int rem = (pos <= lim ? lim - pos : 0);
if (src instanceof DirectBuffer)
@@ -914,7 +922,13 @@
if (!isOpen())
throw new ClosedChannelException();
- return SctpNet.getRemoteAddresses(fdVal, association.associationID());
+ try {
+ return SctpNet.getRemoteAddresses(fdVal, association.associationID());
+ } catch (SocketException se) {
+ /* a valid association should always have remote addresses */
+ Set<SocketAddress> addrs = associationMap.get(association);
+ return addrs != null ? addrs : Collections.EMPTY_SET;
+ }
}
}
@@ -922,7 +936,16 @@
public SctpChannel branch(Association association)
throws IOException {
synchronized (stateLock) {
- return null; //TODO: implement
+ checkAssociation(association);
+ if (!isOpen())
+ throw new ClosedChannelException();
+
+ FileDescriptor bFd = SctpNet.branch(fdVal,
+ association.associationID());
+ /* successfully branched, we can now remove it from assoc list */
+ removeAssociation(association);
+
+ return new SctpChannelImpl(provider(), bFd, association);
}
}
--- a/jdk/src/solaris/classes/sun/nio/ch/SctpNet.java Mon Jun 29 13:29:05 2009 +0100
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpNet.java Mon Jun 29 14:53:10 2009 +0100
@@ -232,6 +232,11 @@
shutdown0(fd, assocId);
}
+ static FileDescriptor branch(int fd, int assocId) throws IOException {
+ int nativefd = branch0(fd, assocId);
+ return IOUtil.newFD(nativefd);
+ }
+
/* Native Methods */
static native int socket0(boolean oneToOne) throws IOException;
@@ -248,6 +253,8 @@
static native SocketAddress[] getRemoteAddresses0(int fd, int assocId)
throws IOException;
+ static native int branch0(int fd, int assocId) throws IOException;
+
static native void setPrimAddrOption0(int fd, int assocId, InetAddress ia,
int port) throws IOException;
--- a/jdk/src/solaris/classes/sun/nio/ch/SctpResultContainer.java Mon Jun 29 13:29:05 2009 +0100
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpResultContainer.java Mon Jun 29 14:53:10 2009 +0100
@@ -121,6 +121,8 @@
case SHUTDOWN: sb.append("SHUTDOWN"); break;
default : sb.append("Unknown result type");
}
- return sb.append(", Value: ").append(value.toString()).toString();
+ sb.append(", Value: ");
+ sb.append((value == null) ? "null" : value.toString());
+ return sb.toString();
}
}
--- a/jdk/src/solaris/classes/sun/nio/ch/SctpServerChannelImpl.java Mon Jun 29 13:29:05 2009 +0100
+++ b/jdk/src/solaris/classes/sun/nio/ch/SctpServerChannelImpl.java Mon Jun 29 14:53:10 2009 +0100
@@ -407,7 +407,7 @@
if (!isOpen())
throw new ClosedChannelException();
if (!isBound())
- return null;
+ return Collections.EMPTY_SET;
return SctpNet.getLocalAddresses(fdVal);
}
--- a/jdk/src/solaris/native/sun/nio/ch/Sctp.h Mon Jun 29 13:29:05 2009 +0100
+++ b/jdk/src/solaris/native/sun/nio/ch/Sctp.h Mon Jun 29 14:53:10 2009 +0100
@@ -63,6 +63,8 @@
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);
+typedef int sctp_peeloff_func(int sock, sctp_assoc_t id);
+
#else /* __linux__ */
@@ -315,6 +317,8 @@
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);
+typedef int sctp_peeloff_func(int sock, sctp_assoc_t id);
+
#endif /* __linux__ */
@@ -323,6 +327,7 @@
sctp_getpaddrs_func* nio_sctp_getpaddrs;
sctp_freepaddrs_func* nio_sctp_freepaddrs;
sctp_bindx_func* nio_sctp_bindx;
+sctp_peeloff_func* nio_sctp_peeloff;
jboolean loadSocketExtensionFuncs(JNIEnv* env);
--- a/jdk/src/solaris/native/sun/nio/ch/SctpChannelImpl.c Mon Jun 29 13:29:05 2009 +0100
+++ b/jdk/src/solaris/native/sun/nio/ch/SctpChannelImpl.c Mon Jun 29 14:53:10 2009 +0100
@@ -254,7 +254,6 @@
if (remaining > 0) {
if ((rv = recvmsg(fd, msg, 0)) < 0) {
- fprintf(stdout, "\nNative: handleSFN: recvmsg failed: errno = %d ", errno);
handleSocketError(env, errno);
return;
}
@@ -269,7 +268,7 @@
/* create SctpSendFailed */
resultObj = (*env)->NewObject(env, ssf_class, ssf_ctrID, ssf->ssf_assoc_id,
- sri->sinfo_stream, ssf->ssf_error, isaObj, bufferObj);
+ isaObj, bufferObj, ssf->ssf_error, sri->sinfo_stream);
CHECK_NULL(resultObj);
(*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj);
(*env)->SetIntField(env, resultContainerObj, src_typeID,
--- a/jdk/src/solaris/native/sun/nio/ch/SctpNet.c Mon Jun 29 13:29:05 2009 +0100
+++ b/jdk/src/solaris/native/sun/nio/ch/SctpNet.c Mon Jun 29 14:53:10 2009 +0100
@@ -96,6 +96,13 @@
return JNI_FALSE;
}
+ if ((nio_sctp_peeloff = (sctp_peeloff_func*)
+ dlsym(RTLD_DEFAULT, "sctp_peeloff")) == NULL) {
+ JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
+ dlerror());
+ return JNI_FALSE;
+ }
+
funcsLoaded = JNI_TRUE;
return JNI_TRUE;
}
@@ -440,12 +447,10 @@
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);
+ struct sockaddr* sap = (struct sockaddr*)&prim.ssp_addr;
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",
@@ -453,7 +458,7 @@
return NULL;
}
- return SockAddrToInetSocketAddress(env, (struct sockaddr*)&ss);
+ return SockAddrToInetSocketAddress(env, sap);
}
/*
@@ -464,16 +469,15 @@
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);
+ struct sockaddr* sap = (struct sockaddr*)&prim.ssp_addr;
+ int sap_len;
- if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&ss,
- &ss_len, JNI_TRUE) != 0) {
+ if (NET_InetAddressToSockaddr(env, iaObj, port, sap,
+ &sap_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",
@@ -607,3 +611,17 @@
}
}
+/*
+ * Class: sun_nio_ch_SctpNet
+ * Method: branch
+ * Signature: (II)I
+ */
+JNIEXPORT int JNICALL Java_sun_nio_ch_SctpNet_branch0
+ (JNIEnv *env, jclass klass, jint fd, jint assocId) {
+ int newfd = 0;
+ if ((newfd = nio_sctp_peeloff(fd, assocId)) < 0) {
+ handleSocketError(env, errno);
+ }
+
+ return newfd;
+}
--- a/jdk/test/com/sun/nio/sctp/SctpChannel/Connect.java Mon Jun 29 13:29:05 2009 +0100
+++ b/jdk/test/com/sun/nio/sctp/SctpChannel/Connect.java Mon Jun 29 14:53:10 2009 +0100
@@ -30,8 +30,8 @@
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.io.IOException;
+import java.util.Set;
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;
@@ -48,47 +48,29 @@
* 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);
+ doTest();
}
- void doTest(SocketAddress addr) {
+ void doTest() {
SctpChannel channel = null;
- final SocketAddress peerAddress = addr;
+ SctpServerChannel ssc = null;
try {
+ /* Create a server channel to connect to */
+ ssc = SctpServerChannel.open().bind(null);
+ Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
+ if (addrs.isEmpty())
+ debug("addrs should not be empty");
+ final SocketAddress peerAddress = (InetSocketAddress) addrs.iterator().next();
+
channel = SctpChannel.open();
/* TEST 0.5 Verify default values for new/unconnected channel */
@@ -118,6 +100,9 @@
"finishConnect should have returned true");
}
+ ssc.accept();
+ ssc.close();
+
/* TEST 1.5 Verify after connect */
check(!channel.getRemoteAddresses().isEmpty(),
"empty set for connected channel");
@@ -136,6 +121,16 @@
unexpected(ioe);
}
+ /* TEST 2.5: Verify AlreadyConnectedException thrown */
+ try {
+ channel.connect(peerAddress, 5, 5);
+ fail("should have thrown AlreadyConnectedException");
+ } catch (AlreadyConnectedException unused) {
+ pass();
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ }
+
/* TEST 3: UnresolvedAddressException */
channel.close();
channel = SctpChannel.open();
@@ -200,9 +195,10 @@
} catch (IOException ioe) {
unexpected(ioe);
} finally {
- finishedLatch.countDown();
try { if (channel != null) channel.close(); }
- catch (IOException e) { unexpected(e);}
+ catch (IOException unused) {}
+ try { if (ssc != null) ssc.close(); }
+ catch (IOException unused) {}
}
}
@@ -219,47 +215,6 @@
}
}
- 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;
--- a/jdk/test/com/sun/nio/sctp/SctpChannel/Shutdown.java Mon Jun 29 13:29:05 2009 +0100
+++ b/jdk/test/com/sun/nio/sctp/SctpChannel/Shutdown.java Mon Jun 29 14:53:10 2009 +0100
@@ -151,6 +151,16 @@
} catch (IOException ioe) {
unexpected(ioe);
}
+
+ /* TEST 6: getRemoteAddresses */
+ debug("Test 6: getRemoteAddresses");
+ try {
+ java.util.Set<SocketAddress> remoteAddrs = channel.getRemoteAddresses();
+ check(remoteAddrs.isEmpty(),
+ "A shutdown channel should not have remote addresses");
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ }
} catch (IOException ioe) {
unexpected(ioe);
} catch (InterruptedException ie) {
--- a/jdk/test/com/sun/nio/sctp/SctpChannel/SocketOptionTests.java Mon Jun 29 13:29:05 2009 +0100
+++ b/jdk/test/com/sun/nio/sctp/SctpChannel/SocketOptionTests.java Mon Jun 29 14:53:10 2009 +0100
@@ -29,15 +29,24 @@
import java.io.IOException;
import java.util.Set;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
import java.util.List;
import java.util.Arrays;
+import java.util.Iterator;
import java.nio.channels.ClosedChannelException;
import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpServerChannel;
import com.sun.nio.sctp.SctpSocketOption;
+import java.security.AccessController;
+import sun.security.action.GetPropertyAction;
import static com.sun.nio.sctp.SctpStandardSocketOption.*;
import static java.lang.System.out;
public class SocketOptionTests {
+ final String osName = AccessController.doPrivileged(
+ new GetPropertyAction("os.name"));
+
<T> void checkOption(SctpChannel sc, SctpSocketOption<T> name,
T expectedValue) throws IOException {
T value = sc.getOption(name);
@@ -92,13 +101,6 @@
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);
@@ -107,6 +109,8 @@
sc.setOption(SO_LINGER, 2000);
checkOption(sc, SO_LINGER, 2000);
+ /* SCTP_PRIMARY_ADDR */
+ sctpPrimaryAddr();
/* NullPointerException */
try {
@@ -135,6 +139,60 @@
}
}
+ /* SCTP_PRIMARY_ADDR */
+ void sctpPrimaryAddr() throws IOException {
+ SocketAddress addrToSet = null;;
+
+ System.out.println("TESTING SCTP_PRIMARY_ADDR");
+ SctpChannel sc = SctpChannel.open();
+ SctpServerChannel ssc = SctpServerChannel.open().bind(null);
+ Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
+ if (addrs.isEmpty())
+ debug("addrs should not be empty");
+ debug("Listening on " + addrs);
+
+ InetSocketAddress serverAddr = (InetSocketAddress) addrs.iterator().next();
+ debug("connecting to " + serverAddr);
+ sc.connect(serverAddr);
+ SctpChannel peerChannel = ssc.accept();
+ ssc.close();
+ Set<SocketAddress> peerAddrs = peerChannel.getAllLocalAddresses();
+ debug("Peer local Addresses: ");
+ for (Iterator<SocketAddress> it = peerAddrs.iterator(); it.hasNext(); ) {
+ InetSocketAddress addr = (InetSocketAddress)it.next();
+ debug("\t" + addr);
+ addrToSet = addr; // any of the peer addresses will do!
+ }
+
+ /* retrieval of SCTP_PRIMARY_ADDR is not supported on Solaris */
+ if ("SunOS".equals(osName)) {
+ /* For now do not set this option. There is a bug on Solaris 10 pre Update 5
+ * where setting this option returns Invalid argument */
+ //debug("Set SCTP_PRIMARY_ADDR with " + addrToSet);
+ //sc.setOption(SCTP_PRIMARY_ADDR, addrToSet);
+ return;
+ } else { /* Linux */
+ SocketAddress primaryAddr = sc.getOption(SCTP_PRIMARY_ADDR);
+ System.out.println("SCTP_PRIMARY_ADDR returned: " + primaryAddr);
+ /* Verify that this is one of the peer addresses */
+ boolean found = false;
+ addrToSet = primaryAddr; // may not have more than one addr
+ for (Iterator<SocketAddress> it = peerAddrs.iterator(); it.hasNext(); ) {
+ InetSocketAddress addr = (InetSocketAddress)it.next();
+ if (addr.equals(primaryAddr)) {
+ found = true;
+ }
+ addrToSet = addr;
+ }
+ check(found, "SCTP_PRIMARY_ADDR returned bogus address!");
+
+ sc.setOption(SCTP_PRIMARY_ADDR, addrToSet);
+ System.out.println("SCTP_PRIMARY_ADDR set to: " + addrToSet);
+ primaryAddr = sc.getOption(SCTP_PRIMARY_ADDR);
+ System.out.println("SCTP_PRIMARY_ADDR returned: " + primaryAddr);
+ check(addrToSet.equals(primaryAddr),"SCTP_PRIMARY_ADDR not set correctly");
+ }
+ }
//--------------------- Infrastructure ---------------------------
boolean debug = true;
volatile int passed = 0, failed = 0;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/nio/sctp/SctpMultiChannel/Branch.java Mon Jun 29 14:53:10 2009 +0100
@@ -0,0 +1,289 @@
+/*
+ * 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.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.SctpChannel;
+import com.sun.nio.sctp.SctpMultiChannel;
+import com.sun.nio.sctp.ShutdownNotification;
+import static java.lang.System.out;
+import static java.lang.System.err;
+
+public class Branch {
+ /* 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();
+
+ /* setup an association implicitly by sending a 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");
+
+ /* Receive the COMM_UP */
+ buffer.clear();
+ BranchNotificationHandler handler = new BranchNotificationHandler();
+ channel.configureBlocking(false);
+ info = channel.receive(buffer, null, handler);
+ check(handler.receivedCommUp(), "COMM_UP no received");
+ Set<Association> associations = channel.associations();
+ check(!associations.isEmpty(),"There should be some associations");
+ Association bassoc = associations.iterator().next();
+
+ /* TEST 1: branch */
+ SctpChannel bchannel = channel.branch(bassoc);
+
+ check(!bchannel.getAllLocalAddresses().isEmpty(),
+ "branched channel should be bound");
+ check(!bchannel.getRemoteAddresses().isEmpty(),
+ "branched channel should be connected");
+ check(channel.associations().isEmpty(),
+ "there should be no associations since the only one was branched off");
+
+ buffer.clear();
+ info = bchannel.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");
+
+ } 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");
+
+ /* echo the message */
+ debug("Server: echoing first message");
+ buffer.flip();
+ int 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) {}
+ }
+ }
+ }
+
+ class BranchNotificationHandler extends AbstractNotificationHandler<Object>
+ {
+ boolean receivedCommUp; // false
+
+ boolean receivedCommUp() {
+ return receivedCommUp;
+ }
+
+ @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;
+
+ return HandlerResult.RETURN;
+ }
+
+ /* A ShutdownNotification handler is provided to ensure that no
+ * shutdown notification are being handled since we don't expect
+ * to receive them. This is not part of branch testing, it just
+ * fits here to test another bug. */
+ @Override
+ public HandlerResult handleNotification(
+ ShutdownNotification notification, Object attachment) {
+ debug("ShutdownNotification");
+ debug(" Association: " + notification.association());
+
+ fail("Shutdown should not be received");
+
+ 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/SctpMultiChannel/SocketOptionTests.java Mon Jun 29 14:53:10 2009 +0100
@@ -0,0 +1,273 @@
+/*
+ * 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.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.List;
+import java.util.Arrays;
+import java.nio.ByteBuffer;
+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.MessageInfo;
+import com.sun.nio.sctp.SctpChannel;
+import com.sun.nio.sctp.SctpMultiChannel;
+import com.sun.nio.sctp.SctpServerChannel;
+import com.sun.nio.sctp.SctpSocketOption;
+import java.security.AccessController;
+import sun.security.action.GetPropertyAction;
+import static com.sun.nio.sctp.SctpStandardSocketOption.*;
+import static java.lang.System.out;
+
+public class SocketOptionTests {
+ final String osName = AccessController.doPrivileged(
+ new GetPropertyAction("os.name"));
+
+ <T> void checkOption(SctpMultiChannel smc, SctpSocketOption<T> name,
+ T expectedValue) throws IOException {
+ T value = smc.getOption(name, null);
+ check(value.equals(expectedValue), name + ": value (" + value +
+ ") not as expected (" + expectedValue + ")");
+ }
+
+ <T> void optionalSupport(SctpMultiChannel smc, SctpSocketOption<T> name,
+ T value) {
+ try {
+ smc.setOption(name, value, null);
+ checkOption(smc, 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 {
+ SctpMultiChannel smc = SctpMultiChannel.open();
+
+ /* check supported options */
+ Set<SctpSocketOption<?>> options = smc.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);
+ smc.setOption(SCTP_INIT_MAXSTREAMS, streams, null);
+ checkOption(smc, SCTP_INIT_MAXSTREAMS, streams);
+ streams = smc.getOption(SCTP_INIT_MAXSTREAMS, null);
+ 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(smc, SCTP_DISABLE_FRAGMENTS, true);
+ optionalSupport(smc, SCTP_EXPLICIT_COMPLETE, true);
+ optionalSupport(smc, SCTP_FRAGMENT_INTERLEAVE, 1);
+
+ smc.setOption(SCTP_NODELAY, true, null);
+ checkOption(smc, SCTP_NODELAY, true);
+ smc.setOption(SO_SNDBUF, 16*1024, null);
+ smc.setOption(SO_RCVBUF, 16*1024, null);
+
+ checkOption(smc, SO_LINGER, -1); /* default should be negative */
+
+ /* Setting SO_LINGER not support for one-to-many on Solaris */
+ if (!"SunOS".equals(osName)) {
+ smc.setOption(SO_LINGER, 2000, null);
+ checkOption(smc, SO_LINGER, 2000);
+ }
+
+ /* SCTP_PRIMARY_ADDR */
+ sctpPrimaryAddr();
+
+ /* NullPointerException */
+ try {
+ smc.setOption(null, "value", null);
+ fail("NullPointerException not thrown for setOption");
+ } catch (NullPointerException unused) {
+ pass();
+ }
+ try {
+ smc.getOption(null, null);
+ fail("NullPointerException not thrown for getOption");
+ } catch (NullPointerException unused) {
+ pass();
+ }
+
+ /* ClosedChannelException */
+ smc.close();
+ try {
+ smc.setOption(SCTP_INIT_MAXSTREAMS, streams, null);
+ fail("ClosedChannelException not thrown");
+ } catch (ClosedChannelException unused) {
+ pass();
+ }
+ } catch (IOException ioe) {
+ unexpected(ioe);
+ }
+ }
+
+ /* SCTP_PRIMARY_ADDR */
+ void sctpPrimaryAddr() throws IOException {
+ SocketAddress addrToSet = null;
+ ByteBuffer buffer = ByteBuffer.allocate(Util.SMALL_BUFFER);
+
+ System.out.println("TESTING SCTP_PRIMARY_ADDR");
+
+ /* create listening channel */
+ SctpServerChannel ssc = SctpServerChannel.open().bind(null);
+ Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
+ if (addrs.isEmpty())
+ debug("addrs should not be empty");
+
+ InetSocketAddress serverAddr = (InetSocketAddress) addrs.iterator().next();
+
+ /* setup an association implicitly by sending a small message */
+ int streamNumber = 0;
+ debug("sending to " + serverAddr + " on stream number: " + streamNumber);
+ MessageInfo info = MessageInfo.createOutgoing(serverAddr, streamNumber);
+ buffer.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1"));
+ buffer.flip();
+
+ debug("sending small message: " + buffer);
+ SctpMultiChannel smc = SctpMultiChannel.open();
+ int sent = smc.send(buffer, info);
+
+ /* Receive the COMM_UP */
+ buffer.clear();
+ SOTNotificationHandler handler = new SOTNotificationHandler();
+ smc.configureBlocking(false);
+ info = smc.receive(buffer, null, handler);
+ check(handler.receivedCommUp(), "COMM_UP no received");
+ Set<Association> associations = smc.associations();
+ check(!associations.isEmpty(),"There should be some associations");
+ Association assoc = associations.iterator().next();
+
+ SctpChannel peerChannel = ssc.accept();
+ ssc.close();
+ Set<SocketAddress> peerAddrs = peerChannel.getAllLocalAddresses();
+ debug("Peer local Addresses: ");
+ for (Iterator<SocketAddress> it = peerAddrs.iterator(); it.hasNext(); ) {
+ InetSocketAddress addr = (InetSocketAddress)it.next();
+ debug("\t" + addr);
+ addrToSet = addr; // any of the peer addresses will do!
+ }
+
+ /* retrieval of SCTP_PRIMARY_ADDR is not supported on Solaris */
+ if ("SunOS".equals(osName)) {
+ /* For now do not set this option. There is a bug on Solaris 10 pre Update 5
+ * where setting this option returns Invalid argument */
+ //debug("Set SCTP_PRIMARY_ADDR with " + addrToSet);
+ //smc.setOption(SCTP_PRIMARY_ADDR, addrToSet, assoc);
+ return;
+ } else { /* Linux */
+ SocketAddress primaryAddr = smc.getOption(SCTP_PRIMARY_ADDR, assoc);
+ System.out.println("SCTP_PRIMARY_ADDR returned: " + primaryAddr);
+ /* Verify that this is one of the peer addresses */
+ boolean found = false;
+ addrToSet = primaryAddr; // may not have more than one addr
+ for (Iterator<SocketAddress> it = peerAddrs.iterator(); it.hasNext(); ) {
+ InetSocketAddress addr = (InetSocketAddress)it.next();
+ if (addr.equals(primaryAddr)) {
+ found = true;
+ }
+ addrToSet = addr;
+ }
+ check(found, "SCTP_PRIMARY_ADDR returned bogus address!");
+
+ smc.setOption(SCTP_PRIMARY_ADDR, addrToSet, assoc);
+ System.out.println("SCTP_PRIMARY_ADDR set to: " + addrToSet);
+ primaryAddr = smc.getOption(SCTP_PRIMARY_ADDR, assoc);
+ System.out.println("SCTP_PRIMARY_ADDR returned: " + primaryAddr);
+ check(addrToSet.equals(primaryAddr),"SCTP_PRIMARY_ADDR not set correctly");
+ }
+ }
+
+ class SOTNotificationHandler extends AbstractNotificationHandler<Object>
+ {
+ boolean receivedCommUp; // false
+
+ boolean receivedCommUp() {
+ return receivedCommUp;
+ }
+
+ @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;
+
+ 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");}
+}