1/*
2 * Copyright (c) 2011, 2017, 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 */
23import java.io.BufferedInputStream;
24import java.io.IOException;
25import java.io.OutputStreamWriter;
26import java.io.PrintWriter;
27import java.net.ServerSocket;
28import java.net.Socket;
29import java.nio.charset.StandardCharsets;
30import java.util.ConcurrentModificationException;
31import java.util.Hashtable;
32import javax.naming.Context;
33import javax.naming.InitialContext;
34import javax.naming.NamingException;
35import javax.naming.event.EventContext;
36import javax.naming.event.NamingEvent;
37import javax.naming.event.NamingExceptionEvent;
38import javax.naming.event.NamingListener;
39import javax.naming.event.ObjectChangeListener;
40
41/**
42 * @test
43 * @bug 8176192
44 * @summary Incorrect usage of Iterator in Java 8 In com.sun.jndi.ldap.
45 * EventSupport.removeNamingListener
46 * @modules java.naming
47 * @run main RemoveNamingListenerTest
48 */
49public class RemoveNamingListenerTest {
50
51    private static volatile Exception exception;
52
53    public static void main(String args[]) throws Exception {
54        // start the LDAP server
55        TestLDAPServer server = new TestLDAPServer();
56        server.start();
57
58        // Set up environment for creating initial context
59        Hashtable<String, Object> env = new Hashtable<>(3);
60        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
61        env.put(Context.PROVIDER_URL, "ldap://localhost:" + server.getPort() + "/o=example");
62        env.put("com.sun.jndi.ldap.connect.timeout", "2000");
63        EventContext ctx = null;
64
65        try {
66            ctx = (EventContext) (new InitialContext(env).lookup(""));
67            String target = "cn=Vyom Tewari";
68
69            // Create listeners
70            NamingListener oneListener = new SampleListener();
71            NamingListener objListener = new SampleListener();
72            NamingListener subListener = new SampleListener();
73
74            // Register listeners using different scopes
75            ctx.addNamingListener(target, EventContext.ONELEVEL_SCOPE, oneListener);
76            ctx.addNamingListener(target, EventContext.OBJECT_SCOPE, objListener);
77            ctx.addNamingListener(target, EventContext.SUBTREE_SCOPE, subListener);
78
79            //remove a listener in different thread
80            Thread t = new Thread(new RemoveNamingListener(ctx, subListener));
81            t.start();
82            t.join();
83
84            if (exception != null) {
85                throw exception;
86            }
87            System.out.println("Test run OK!!!");
88        } finally {
89            if (ctx != null) {
90                ctx.close();
91            }
92            server.stopServer();
93        }
94    }
95
96    /**
97     * Helper thread that removes the naming listener.
98     */
99    static class RemoveNamingListener implements Runnable {
100
101        final EventContext ctx;
102        final NamingListener listener;
103
104        RemoveNamingListener(EventContext ctx, NamingListener listener) {
105            this.ctx = ctx;
106            this.listener = listener;
107        }
108
109        @Override
110        public void run() {
111            try {
112                ctx.removeNamingListener(listener);
113            } catch (NamingException | ConcurrentModificationException ex) {
114                exception = ex;
115            }
116        }
117    }
118
119    static class SampleListener implements ObjectChangeListener {
120
121        @Override
122        public void objectChanged(NamingEvent ne) {
123            //do nothing
124        }
125
126        @Override
127        public void namingExceptionThrown(NamingExceptionEvent nee) {
128            //do nothing
129        }
130    }
131}
132
133class TestLDAPServer extends Thread {
134
135    private final int LDAP_PORT;
136    private final ServerSocket serverSocket;
137    private volatile boolean isRunning;
138
139    TestLDAPServer() throws IOException {
140        serverSocket = new ServerSocket(0);
141        isRunning = true;
142        LDAP_PORT = serverSocket.getLocalPort();
143        setDaemon(true);
144    }
145
146    public int getPort() {
147        return LDAP_PORT;
148    }
149
150    public void stopServer() {
151        isRunning = false;
152        if (serverSocket != null && !serverSocket.isClosed()) {
153            try {
154                // this will cause ServerSocket.accept() to throw SocketException.
155                serverSocket.close();
156            } catch (IOException ignored) {
157            }
158        }
159    }
160
161    @Override
162    public void run() {
163        try {
164            while (isRunning) {
165                Socket clientSocket = serverSocket.accept();
166                Thread handler = new Thread(new LDAPServerHandler(clientSocket));
167                handler.setDaemon(true);
168                handler.start();
169            }
170        } catch (IOException iOException) {
171            //do not throw exception if server is not running.
172            if (isRunning) {
173                throw new RuntimeException(iOException);
174            }
175        } finally {
176            stopServer();
177        }
178    }
179}
180
181class LDAPServerHandler implements Runnable {
182
183    private final Socket clientSocket;
184
185    public LDAPServerHandler(final Socket clientSocket) {
186        this.clientSocket = clientSocket;
187    }
188
189    @Override
190    public void run() {
191        BufferedInputStream in = null;
192        PrintWriter out = null;
193        byte[] bindResponse = {0x30, 0x0C, 0x02, 0x01, 0x01, 0x61, 0x07, 0x0A, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00};
194        byte[] searchResponse = {0x30, 0x0C, 0x02, 0x01, 0x02, 0x65, 0x07, 0x0A, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00};
195        try {
196            in = new BufferedInputStream(clientSocket.getInputStream());
197            out = new PrintWriter(new OutputStreamWriter(
198                    clientSocket.getOutputStream(), StandardCharsets.UTF_8), true);
199            while (true) {
200
201                // Read the LDAP BindRequest
202                while (in.read() != -1) {
203                    in.skip(in.available());
204                    break;
205                }
206
207                // Write an LDAP BindResponse
208                out.write(new String(bindResponse));
209                out.flush();
210
211                // Read the LDAP SearchRequest
212                while (in.read() != -1) {
213                    in.skip(in.available());
214                    break;
215                }
216
217                // Write an LDAP SearchResponse
218                out.write(new String(searchResponse));
219                out.flush();
220            }
221        } catch (IOException iOException) {
222            throw new RuntimeException(iOException);
223        } finally {
224            if (in != null) {
225                try {
226                    in.close();
227                } catch (IOException ignored) {
228                }
229            }
230            if (out != null) {
231                out.close();
232            }
233            if (clientSocket != null) {
234                try {
235                    clientSocket.close();
236                } catch (IOException ignored) {
237                }
238            }
239        }
240    }
241}
242