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