1/*
2 * Copyright (c) 2005, 2012, 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 4924577
26 * @summary When the current RMIFailureHandler (if any) is invoked
27 * because of a server socket accept failure, if it returns false,
28 * then (in addition to the accept loop terminating) the associated
29 * server socket should be closed.  The server socket should also be
30 * closed if the accept loop terminates because of an unexpected
31 * exception for which it doesn't even consult the RMIFailureHandler.
32 * @author Peter Jones
33 *
34 * @run main/othervm CloseServerSocketOnTermination
35 */
36
37import java.io.IOException;
38import java.net.ServerSocket;
39import java.net.Socket;
40import java.rmi.Remote;
41import java.rmi.server.RMIFailureHandler;
42import java.rmi.server.RMIServerSocketFactory;
43import java.rmi.server.RMISocketFactory;
44import java.rmi.server.UnicastRemoteObject;
45import java.util.concurrent.CountDownLatch;
46import java.util.concurrent.TimeUnit;
47
48public class CloseServerSocketOnTermination {
49
50    private static long TIMEOUT = 5000;
51
52    public static void main(String[] args) throws Exception {
53        System.err.println("\nRegression test for bug 4924577\n");
54
55        RMISocketFactory.setFailureHandler(new RMIFailureHandler() {
56            public boolean failure(Exception e) { return false; }
57        });
58
59        tryWith(new IOException());
60        tryWith(new NullPointerException());
61        tryWith(new OutOfMemoryError());
62        tryWith(new NoClassDefFoundError());
63        tryWith(new InternalError());
64        tryWith(new Throwable());
65
66        System.err.println("TEST PASSED");
67    }
68
69    private static void tryWith(Throwable t) throws Exception {
70        Remote impl = new Remote() { };
71        try {
72            CountDownLatch latch = new CountDownLatch(1);
73            UnicastRemoteObject.exportObject(impl, 0, null, new SSF(t, latch));
74            if (!latch.await(TIMEOUT, TimeUnit.MILLISECONDS)) {
75                throw new Error("server socket not closed");
76            }
77        } finally {
78            UnicastRemoteObject.unexportObject(impl, true);
79        }
80    }
81
82    private static class SSF implements RMIServerSocketFactory {
83        private final Throwable acceptFailure;
84        private final CountDownLatch closedLatch;
85        SSF(Throwable acceptFailure, CountDownLatch closedLatch) {
86            this.acceptFailure = acceptFailure;
87            this.closedLatch = closedLatch;
88        }
89        public ServerSocket createServerSocket(int port) throws IOException {
90            return new ServerSocket(port) {
91                private int acceptInvocations = 0;
92                public synchronized Socket accept() throws IOException {
93                    if (acceptInvocations++ == 0) {
94                        throwException(acceptFailure);
95                    }
96                    return super.accept();
97                }
98                public void close() throws IOException {
99                    closedLatch.countDown();
100                    super.close();
101                }
102            };
103        }
104
105        // hack to throw an arbitrary (possibly checked) Throwable
106        private static void throwException(Throwable t) {
107            try {
108                toThrow.set(t);
109                Thrower.class.newInstance();
110            } catch (IllegalAccessException e) {
111                throw new AssertionError();
112            } catch (InstantiationException e) {
113                throw new AssertionError();
114            } finally {
115                toThrow.remove();
116            }
117        }
118        private static ThreadLocal<Throwable> toThrow =
119            new ThreadLocal<Throwable>();
120        private static class Thrower {
121            Thrower() throws Throwable { throw toThrow.get(); }
122        }
123    }
124}
125