1 /*
2  * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /* @test
25  * @bug 4927640
26  * @summary Tests the SCTP protocol implementation
27  * @author chegar
28  */
29 
30 import java.net.InetSocketAddress;
31 import java.net.SocketAddress;
32 import java.io.IOException;
33 import java.util.concurrent.CountDownLatch;
34 import java.nio.ByteBuffer;
35 import java.nio.channels.ClosedChannelException;
36 import java.nio.channels.NotYetConnectedException;
37 import com.sun.nio.sctp.AbstractNotificationHandler;
38 import com.sun.nio.sctp.HandlerResult;
39 import com.sun.nio.sctp.MessageInfo;
40 import com.sun.nio.sctp.SctpChannel;
41 import com.sun.nio.sctp.SctpServerChannel;
42 import com.sun.nio.sctp.ShutdownNotification;
43 import static java.lang.System.out;
44 import static java.lang.System.err;
45 
46 public class Shutdown {
47     static CountDownLatch finishedLatch = new CountDownLatch(1);
48     static CountDownLatch sentLatch = new CountDownLatch(1);
49 
test(String[] args)50     void test(String[] args) {
51         SocketAddress address = null;
52         ShutdownServer server = null;
53 
54         if (!Util.isSCTPSupported()) {
55             out.println("SCTP protocol is not supported");
56             out.println("Test cannot be run");
57             return;
58         }
59 
60         if (args.length == 2) {
61             /* requested to connecct to a specific address */
62             try {
63                 int port = Integer.valueOf(args[1]);
64                 address = new InetSocketAddress(args[0], port);
65             } catch (NumberFormatException nfe) {
66                 err.println(nfe);
67             }
68         } else {
69             /* start server on local machine, default */
70             try {
71                 server = new ShutdownServer();
72                 server.start();
73                 address = server.address();
74                 debug("Server started and listening on " + address);
75             } catch (IOException ioe) {
76                 ioe.printStackTrace();
77                 return;
78             }
79         }
80 
81         doTest(address);
82     }
83 
doTest(SocketAddress peerAddress)84     void doTest(SocketAddress peerAddress) {
85         SctpChannel channel = null;
86         ByteBuffer buffer = ByteBuffer.allocate(Util.SMALL_BUFFER);
87         MessageInfo info;
88 
89         try {
90             channel = SctpChannel.open();
91 
92             /* TEST 1: Verify NotYetConnectedException thrown */
93             debug("Test 1: NotYetConnectedException");
94             try {
95                 channel.shutdown();
96                 fail("shutdown not throwing expected NotYetConnectedException");
97             } catch (NotYetConnectedException unused) {
98                 pass();
99             }  catch (IOException ioe) {
100                 unexpected(ioe);
101             }
102 
103             channel.connect(peerAddress);
104             sentLatch.await();
105             channel.shutdown();
106 
107             /* TEST 2: receive data sent before shutdown */
108             do {
109                 debug("Test 2: invoking receive");
110                 info = channel.receive(buffer, null, null);
111                 if (info == null) {
112                     fail("unexpected null from receive");
113                     return;
114                 }
115             } while (!info.isComplete());
116 
117             buffer.flip();
118             check(info != null, "info is null");
119             check(info.bytes() == Util.SMALL_MESSAGE.getBytes("ISO-8859-1").
120                   length, "bytes received not equal to message length");
121             check(info.bytes() == buffer.remaining(), "bytes != remaining");
122             check(Util.compare(buffer, Util.SMALL_MESSAGE),
123                   "received message not the same as sent message");
124 
125             buffer.clear();
126 
127             /* TEST 3: receive notifications on the SCTP stack */
128             debug("Test 3: receive notifications");
129             while ((info = channel.receive(buffer, null, null )) != null &&
130                     info.bytes() != -1 );
131 
132 
133             /* TEST 4: If the channel is already shutdown then invoking this
134              * method has no effect. */
135             debug("Test 4: no-op");
136             try {
137                 channel.shutdown();
138                 pass();
139             } catch (IOException ioe) {
140                 unexpected(ioe);
141             }
142 
143             /* TEST 5: Further sends will throw ClosedChannelException */
144             debug("Test 5: ClosedChannelException");
145             info = MessageInfo.createOutgoing(null, 1);
146             try {
147                 channel.send(buffer, info);
148                 fail("shutdown not throwing expected ClosedChannelException");
149             } catch (ClosedChannelException unused) {
150                 pass();
151             } catch (IOException ioe) {
152                 unexpected(ioe);
153             }
154 
155             /* TEST 6: getRemoteAddresses */
156             debug("Test 6: getRemoteAddresses");
157             try {
158                 java.util.Set<SocketAddress> remoteAddrs = channel.getRemoteAddresses();
159                 check(remoteAddrs.isEmpty(),
160                          "A shutdown channel should not have remote addresses");
161             } catch (IOException ioe) {
162                 unexpected(ioe);
163             }
164         } catch (IOException ioe) {
165             unexpected(ioe);
166         } catch (InterruptedException ie) {
167             unexpected(ie);
168         }finally {
169             finishedLatch.countDown();
170             try { if (channel != null) channel.close(); }
171             catch (IOException e) { unexpected(e);}
172         }
173     }
174 
175     class ShutdownServer implements Runnable
176     {
177         final InetSocketAddress serverAddr;
178         private SctpServerChannel ssc;
179 
ShutdownServer()180         public ShutdownServer() throws IOException {
181             ssc = SctpServerChannel.open().bind(null);
182             //serverAddr = (InetSocketAddress)(ssc.getAllLocalAddresses().iterator().next());
183 
184             java.util.Set<SocketAddress> addrs = ssc.getAllLocalAddresses();
185             if (addrs.isEmpty())
186                 debug("addrs should not be empty");
187 
188             serverAddr = (InetSocketAddress) addrs.iterator().next();
189 
190         }
191 
start()192         public void start() {
193             (new Thread(this, "ShutdownServer-"  + serverAddr.getPort())).start();
194         }
195 
address()196         public InetSocketAddress address() {
197             return serverAddr;
198         }
199 
200         @Override
run()201         public void run() {
202             SctpChannel sc = null;
203             try {
204                 sc = ssc.accept();
205 
206                 /* send a message */
207                 MessageInfo info = MessageInfo.createOutgoing(null, 1);
208                 ByteBuffer buf = ByteBuffer.allocateDirect(Util.SMALL_BUFFER);
209                 buf.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1"));
210                 buf.flip();
211                 sc.send(buf, info);
212 
213                 /* notify client that the data has been sent */
214                 sentLatch.countDown();
215 
216                 /* wait until after the client has finished its tests */
217                 finishedLatch.await();
218 
219                 buf.clear();
220                 ShutdownNotificationHandler handler =
221                         new ShutdownNotificationHandler();
222                 BooleanWrapper bool = new BooleanWrapper();
223                 sc.configureBlocking(false);
224                 sc.receive(buf, bool, handler);
225                 check(bool.booleanValue(), "SHUTDOWN not received on Server");
226 
227             } catch (IOException ioe) {
228                 ioe.printStackTrace();
229             } catch (InterruptedException ie) {
230                 ie.printStackTrace();
231             } finally {
232                 try { if (ssc != null) ssc.close(); }
233                 catch (IOException  ioe) { unexpected(ioe); }
234                 try { if (sc != null) sc.close(); }
235                 catch (IOException  ioe) { unexpected(ioe); }
236             }
237         }
238     }
239 
240     class BooleanWrapper {
241         boolean bool;
242 
booleanValue()243         boolean booleanValue() {
244             return bool;
245         }
246 
booleanValue(boolean value)247         void booleanValue(boolean value) {
248             bool = value;
249         }
250     }
251 
252     class ShutdownNotificationHandler extends AbstractNotificationHandler<BooleanWrapper>
253     {
254         @Override
handleNotification( ShutdownNotification sn, BooleanWrapper bool)255         public HandlerResult handleNotification(
256                 ShutdownNotification sn, BooleanWrapper bool)
257         {
258             bool.booleanValue(true);
259             debug(sn.toString());
260             return HandlerResult.RETURN;
261         }
262     }
263 
264         //--------------------- Infrastructure ---------------------------
265     boolean debug = true;
266     volatile int passed = 0, failed = 0;
pass()267     void pass() {passed++;}
fail()268     void fail() {failed++; Thread.dumpStack();}
fail(String msg)269     void fail(String msg) {System.err.println(msg); fail();}
unexpected(Throwable t)270     void unexpected(Throwable t) {failed++; t.printStackTrace();}
check(boolean cond)271     void check(boolean cond) {if (cond) pass(); else fail();}
check(boolean cond, String failMessage)272     void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
debug(String message)273     void debug(String message) {if(debug) { System.out.println(message); }  }
main(String[] args)274     public static void main(String[] args) throws Throwable {
275         Class<?> k = new Object(){}.getClass().getEnclosingClass();
276         try {k.getMethod("instanceMain",String[].class)
277                 .invoke( k.newInstance(), (Object) args);}
278         catch (Throwable e) {throw e.getCause();}}
instanceMain(String[] args)279     public void instanceMain(String[] args) throws Throwable {
280         try {test(args);} catch (Throwable t) {unexpected(t);}
281         System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
282         if (failed > 0) throw new AssertionError("Some tests failed");}
283 
284 }
285