author | coffeys |
Thu, 03 Mar 2011 16:51:03 +0000 | |
changeset 8564 | d99f879a35ab |
parent 5506 | 202f599c92aa |
child 10324 | e28265130e4f |
permissions | -rw-r--r-- |
2 | 1 |
/* |
8564
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2 |
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. |
2 | 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 |
|
5506 | 7 |
* published by the Free Software Foundation. Oracle designates this |
2 | 8 |
* particular file as subject to the "Classpath" exception as provided |
5506 | 9 |
* by Oracle in the LICENSE file that accompanied this code. |
2 | 10 |
* |
11 |
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 |
* version 2 for more details (a copy is included in the LICENSE file that |
|
15 |
* accompanied this code). |
|
16 |
* |
|
17 |
* You should have received a copy of the GNU General Public License version |
|
18 |
* 2 along with this work; if not, write to the Free Software Foundation, |
|
19 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 |
* |
|
5506 | 21 |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
22 |
* or visit www.oracle.com if you need additional information or have any |
|
23 |
* questions. |
|
2 | 24 |
*/ |
25 |
||
26 |
package com.sun.jndi.ldap; |
|
27 |
||
28 |
import javax.naming.*; |
|
29 |
import javax.naming.directory.*; |
|
30 |
import javax.naming.spi.*; |
|
31 |
import javax.naming.event.*; |
|
32 |
import javax.naming.ldap.*; |
|
33 |
import javax.naming.ldap.LdapName; |
|
34 |
import javax.naming.ldap.Rdn; |
|
35 |
||
36 |
import java.util.Vector; |
|
37 |
import java.util.Hashtable; |
|
38 |
import java.util.List; |
|
39 |
import java.util.StringTokenizer; |
|
40 |
import java.util.Enumeration; |
|
41 |
||
42 |
import java.io.IOException; |
|
43 |
import java.io.OutputStream; |
|
44 |
||
45 |
import com.sun.jndi.toolkit.ctx.*; |
|
46 |
import com.sun.jndi.toolkit.dir.HierMemDirCtx; |
|
47 |
import com.sun.jndi.toolkit.dir.SearchFilter; |
|
48 |
import com.sun.jndi.ldap.ext.StartTlsResponseImpl; |
|
49 |
||
50 |
/** |
|
51 |
* The LDAP context implementation. |
|
52 |
* |
|
53 |
* Implementation is not thread-safe. Caller must sync as per JNDI spec. |
|
54 |
* Members that are used directly or indirectly by internal worker threads |
|
55 |
* (Connection, EventQueue, NamingEventNotifier) must be thread-safe. |
|
56 |
* Connection - calls LdapClient.processUnsolicited(), which in turn calls |
|
57 |
* LdapCtx.convertControls() and LdapCtx.fireUnsolicited(). |
|
58 |
* convertControls() - no sync; reads envprops and 'this' |
|
59 |
* fireUnsolicited() - sync on eventSupport for all references to 'unsolicited' |
|
60 |
* (even those in other methods); don't sync on LdapCtx in case caller |
|
61 |
* is already sync'ing on it - this would prevent Unsol events from firing |
|
62 |
* and the Connection thread to block (thus preventing any other data |
|
63 |
* from being read from the connection) |
|
64 |
* References to 'eventSupport' need not be sync'ed because these |
|
65 |
* methods can only be called after eventSupport has been set first |
|
66 |
* (via addNamingListener()). |
|
67 |
* EventQueue - no direct or indirect calls to LdapCtx |
|
68 |
* NamingEventNotifier - calls newInstance() to get instance for run() to use; |
|
69 |
* no sync needed for methods invoked on new instance; |
|
70 |
* |
|
71 |
* LdapAttribute links to LdapCtx in order to process getAttributeDefinition() |
|
72 |
* and getAttributeSyntaxDefinition() calls. It invokes LdapCtx.getSchema(), |
|
73 |
* which uses schemaTrees (a Hashtable - already sync). Potential conflict |
|
74 |
* of duplicating construction of tree for same subschemasubentry |
|
75 |
* but no inconsistency problems. |
|
76 |
* |
|
77 |
* NamingEnumerations link to LdapCtx for the following: |
|
78 |
* 1. increment/decrement enum count so that ctx doesn't close the |
|
79 |
* underlying connection |
|
80 |
* 2. LdapClient handle to get next batch of results |
|
81 |
* 3. Sets LdapCtx's response controls |
|
82 |
* 4. Process return code |
|
83 |
* 5. For narrowing response controls (using ctx's factories) |
|
84 |
* Since processing of NamingEnumeration by client is treated the same as method |
|
85 |
* invocation on LdapCtx, caller is responsible for locking. |
|
86 |
* |
|
87 |
* @author Vincent Ryan |
|
88 |
* @author Rosanna Lee |
|
89 |
*/ |
|
90 |
||
91 |
final public class LdapCtx extends ComponentDirContext |
|
92 |
implements EventDirContext, LdapContext { |
|
93 |
||
94 |
/* |
|
95 |
* Used to store arguments to the search method. |
|
96 |
*/ |
|
97 |
final static class SearchArgs { |
|
98 |
Name name; |
|
99 |
String filter; |
|
100 |
SearchControls cons; |
|
101 |
String[] reqAttrs; // those attributes originally requested |
|
102 |
||
103 |
SearchArgs(Name name, String filter, SearchControls cons, String[] ra) { |
|
104 |
this.name = name; |
|
105 |
this.filter = filter; |
|
106 |
this.cons = cons; |
|
107 |
this.reqAttrs = ra; |
|
108 |
} |
|
109 |
} |
|
110 |
||
111 |
private static final boolean debug = false; |
|
112 |
||
113 |
private static final boolean HARD_CLOSE = true; |
|
114 |
private static final boolean SOFT_CLOSE = false; |
|
115 |
||
116 |
// ----------------- Constants ----------------- |
|
117 |
||
118 |
public static final int DEFAULT_PORT = 389; |
|
119 |
public static final int DEFAULT_SSL_PORT = 636; |
|
120 |
public static final String DEFAULT_HOST = "localhost"; |
|
121 |
||
122 |
private static final boolean DEFAULT_DELETE_RDN = true; |
|
123 |
private static final boolean DEFAULT_TYPES_ONLY = false; |
|
124 |
private static final int DEFAULT_DEREF_ALIASES = 3; // always deref |
|
125 |
private static final int DEFAULT_LDAP_VERSION = LdapClient.LDAP_VERSION3_VERSION2; |
|
126 |
private static final int DEFAULT_BATCH_SIZE = 1; |
|
127 |
private static final int DEFAULT_REFERRAL_MODE = LdapClient.LDAP_REF_IGNORE; |
|
128 |
private static final char DEFAULT_REF_SEPARATOR = '#'; |
|
129 |
||
130 |
// Used by LdapPoolManager |
|
131 |
static final String DEFAULT_SSL_FACTORY = |
|
132 |
"javax.net.ssl.SSLSocketFactory"; // use Sun's SSL |
|
133 |
private static final int DEFAULT_REFERRAL_LIMIT = 10; |
|
134 |
private static final String STARTTLS_REQ_OID = "1.3.6.1.4.1.1466.20037"; |
|
135 |
||
136 |
// schema operational and user attributes |
|
137 |
private static final String[] SCHEMA_ATTRIBUTES = |
|
138 |
{ "objectClasses", "attributeTypes", "matchingRules", "ldapSyntaxes" }; |
|
139 |
||
140 |
// --------------- Environment property names ---------- |
|
141 |
||
142 |
// LDAP protocol version: "2", "3" |
|
143 |
private static final String VERSION = "java.naming.ldap.version"; |
|
144 |
||
145 |
// Binary-valued attributes. Space separated string of attribute names. |
|
146 |
private static final String BINARY_ATTRIBUTES = |
|
147 |
"java.naming.ldap.attributes.binary"; |
|
148 |
||
149 |
// Delete old RDN during modifyDN: "true", "false" |
|
150 |
private static final String DELETE_RDN = "java.naming.ldap.deleteRDN"; |
|
151 |
||
152 |
// De-reference aliases: "never", "searching", "finding", "always" |
|
153 |
private static final String DEREF_ALIASES = "java.naming.ldap.derefAliases"; |
|
154 |
||
155 |
// Return only attribute types (no values) |
|
156 |
private static final String TYPES_ONLY = "java.naming.ldap.typesOnly"; |
|
157 |
||
158 |
// Separator character for encoding Reference's RefAddrs; default is '#' |
|
159 |
private static final String REF_SEPARATOR = "java.naming.ldap.ref.separator"; |
|
160 |
||
161 |
// Socket factory |
|
162 |
private static final String SOCKET_FACTORY = "java.naming.ldap.factory.socket"; |
|
163 |
||
164 |
// Bind Controls (used by LdapReferralException) |
|
165 |
static final String BIND_CONTROLS = "java.naming.ldap.control.connect"; |
|
166 |
||
167 |
private static final String REFERRAL_LIMIT = |
|
168 |
"java.naming.ldap.referral.limit"; |
|
169 |
||
170 |
// trace BER (java.io.OutputStream) |
|
171 |
private static final String TRACE_BER = "com.sun.jndi.ldap.trace.ber"; |
|
172 |
||
173 |
// Get around Netscape Schema Bugs |
|
174 |
private static final String NETSCAPE_SCHEMA_BUG = |
|
175 |
"com.sun.jndi.ldap.netscape.schemaBugs"; |
|
176 |
// deprecated |
|
177 |
private static final String OLD_NETSCAPE_SCHEMA_BUG = |
|
178 |
"com.sun.naming.netscape.schemaBugs"; // for backward compatability |
|
179 |
||
180 |
// Timeout for socket connect |
|
181 |
private static final String CONNECT_TIMEOUT = |
|
182 |
"com.sun.jndi.ldap.connect.timeout"; |
|
183 |
||
184 |
// Timeout for reading responses |
|
185 |
private static final String READ_TIMEOUT = |
|
186 |
"com.sun.jndi.ldap.read.timeout"; |
|
187 |
||
188 |
// Environment property for connection pooling |
|
189 |
private static final String ENABLE_POOL = "com.sun.jndi.ldap.connect.pool"; |
|
190 |
||
191 |
// Environment property for the domain name (derived from this context's DN) |
|
192 |
private static final String DOMAIN_NAME = "com.sun.jndi.ldap.domainname"; |
|
193 |
||
8564
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
194 |
// Block until the first search reply is received |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
195 |
private static final String WAIT_FOR_REPLY = |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
196 |
"com.sun.jndi.ldap.search.waitForReply"; |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
197 |
|
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
198 |
// Size of the queue of unprocessed search replies |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
199 |
private static final String REPLY_QUEUE_SIZE = |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
200 |
"com.sun.jndi.ldap.search.replyQueueSize"; |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
201 |
|
2 | 202 |
// ----------------- Fields that don't change ----------------------- |
203 |
private static final NameParser parser = new LdapNameParser(); |
|
204 |
||
205 |
// controls that Provider needs |
|
206 |
private static final ControlFactory myResponseControlFactory = |
|
207 |
new DefaultResponseControlFactory(); |
|
208 |
private static final Control manageReferralControl = |
|
209 |
new ManageReferralControl(false); |
|
210 |
||
211 |
private static final HierMemDirCtx EMPTY_SCHEMA = new HierMemDirCtx(); |
|
212 |
static { |
|
213 |
EMPTY_SCHEMA.setReadOnly( |
|
214 |
new SchemaViolationException("Cannot update schema object")); |
|
215 |
} |
|
216 |
||
217 |
// ------------ Package private instance variables ---------------- |
|
218 |
// Cannot be private; used by enums |
|
219 |
||
220 |
// ------- Inherited by derived context instances |
|
221 |
||
222 |
int port_number; // port number of server |
|
223 |
String hostname = null; // host name of server (no brackets |
|
224 |
// for IPv6 literals) |
|
225 |
LdapClient clnt = null; // connection handle |
|
226 |
Hashtable envprops = null; // environment properties of context |
|
227 |
int handleReferrals = DEFAULT_REFERRAL_MODE; // how referral is handled |
|
228 |
boolean hasLdapsScheme = false; // true if the context was created |
|
229 |
// using an LDAPS URL. |
|
230 |
||
231 |
// ------- Not inherited by derived context instances |
|
232 |
||
233 |
String currentDN; // DN of this context |
|
234 |
Name currentParsedDN; // DN of this context |
|
235 |
Vector respCtls = null; // Response controls read |
|
236 |
Control[] reqCtls = null; // Controls to be sent with each request |
|
237 |
||
238 |
||
239 |
// ------------- Private instance variables ------------------------ |
|
240 |
||
241 |
// ------- Inherited by derived context instances |
|
242 |
||
243 |
private OutputStream trace = null; // output stream for BER debug output |
|
244 |
private boolean netscapeSchemaBug = false; // workaround |
|
245 |
private Control[] bindCtls = null; // Controls to be sent with LDAP "bind" |
|
246 |
private int referralHopLimit = DEFAULT_REFERRAL_LIMIT; // max referral |
|
247 |
private Hashtable schemaTrees = null; // schema root of this context |
|
248 |
private int batchSize = DEFAULT_BATCH_SIZE; // batch size for search results |
|
249 |
private boolean deleteRDN = DEFAULT_DELETE_RDN; // delete the old RDN when modifying DN |
|
250 |
private boolean typesOnly = DEFAULT_TYPES_ONLY; // return attribute types (no values) |
|
251 |
private int derefAliases = DEFAULT_DEREF_ALIASES;// de-reference alias entries during searching |
|
252 |
private char addrEncodingSeparator = DEFAULT_REF_SEPARATOR; // encoding RefAddr |
|
253 |
||
254 |
private Hashtable binaryAttrs = null; // attr values returned as byte[] |
|
255 |
private int connectTimeout = -1; // no timeout value |
|
256 |
private int readTimeout = -1; // no timeout value |
|
8564
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
257 |
private boolean waitForReply = true; // wait for search response |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
258 |
private int replyQueueSize = -1; // unlimited queue size |
2 | 259 |
private boolean useSsl = false; // true if SSL protocol is active |
260 |
private boolean useDefaultPortNumber = false; // no port number was supplied |
|
261 |
||
262 |
// ------- Not inherited by derived context instances |
|
263 |
||
264 |
// True if this context was created by another LdapCtx. |
|
265 |
private boolean parentIsLdapCtx = false; // see composeName() |
|
266 |
||
267 |
private int hopCount = 1; // current referral hop count |
|
268 |
private String url = null; // URL of context; see getURL() |
|
269 |
private EventSupport eventSupport; // Event support helper for this ctx |
|
270 |
private boolean unsolicited = false; // if there unsolicited listeners |
|
271 |
private boolean sharable = true; // can share connection with other ctx |
|
272 |
||
273 |
// -------------- Constructors ----------------------------------- |
|
274 |
||
275 |
public LdapCtx(String dn, String host, int port_number, Hashtable props, |
|
276 |
boolean useSsl) throws NamingException { |
|
277 |
||
278 |
this.useSsl = this.hasLdapsScheme = useSsl; |
|
279 |
||
280 |
if (props != null) { |
|
281 |
envprops = (Hashtable) props.clone(); |
|
282 |
||
283 |
// SSL env prop overrides the useSsl argument |
|
284 |
if ("ssl".equals(envprops.get(Context.SECURITY_PROTOCOL))) { |
|
285 |
this.useSsl = true; |
|
286 |
} |
|
287 |
||
288 |
// %%% These are only examined when the context is created |
|
289 |
// %%% because they are only for debugging or workaround purposes. |
|
290 |
trace = (OutputStream)envprops.get(TRACE_BER); |
|
291 |
||
292 |
if (props.get(NETSCAPE_SCHEMA_BUG) != null || |
|
293 |
props.get(OLD_NETSCAPE_SCHEMA_BUG) != null) { |
|
294 |
netscapeSchemaBug = true; |
|
295 |
} |
|
296 |
} |
|
297 |
||
298 |
currentDN = (dn != null) ? dn : ""; |
|
299 |
currentParsedDN = parser.parse(currentDN); |
|
300 |
||
301 |
hostname = (host != null && host.length() > 0) ? host : DEFAULT_HOST; |
|
302 |
if (hostname.charAt(0) == '[') { |
|
303 |
hostname = hostname.substring(1, hostname.length() - 1); |
|
304 |
} |
|
305 |
||
306 |
if (port_number > 0) { |
|
307 |
this.port_number = port_number; |
|
308 |
} else { |
|
309 |
this.port_number = this.useSsl ? DEFAULT_SSL_PORT : DEFAULT_PORT; |
|
310 |
this.useDefaultPortNumber = true; |
|
311 |
} |
|
312 |
||
313 |
schemaTrees = new Hashtable(11, 0.75f); |
|
314 |
initEnv(); |
|
2605
e8d0473c25e8
6717680: LdapCtx does not close the connection if initialization fails
weijun
parents:
2
diff
changeset
|
315 |
try { |
e8d0473c25e8
6717680: LdapCtx does not close the connection if initialization fails
weijun
parents:
2
diff
changeset
|
316 |
connect(false); |
e8d0473c25e8
6717680: LdapCtx does not close the connection if initialization fails
weijun
parents:
2
diff
changeset
|
317 |
} catch (NamingException e) { |
e8d0473c25e8
6717680: LdapCtx does not close the connection if initialization fails
weijun
parents:
2
diff
changeset
|
318 |
try { |
e8d0473c25e8
6717680: LdapCtx does not close the connection if initialization fails
weijun
parents:
2
diff
changeset
|
319 |
close(); |
e8d0473c25e8
6717680: LdapCtx does not close the connection if initialization fails
weijun
parents:
2
diff
changeset
|
320 |
} catch (Exception e2) { |
e8d0473c25e8
6717680: LdapCtx does not close the connection if initialization fails
weijun
parents:
2
diff
changeset
|
321 |
// Nothing |
e8d0473c25e8
6717680: LdapCtx does not close the connection if initialization fails
weijun
parents:
2
diff
changeset
|
322 |
} |
e8d0473c25e8
6717680: LdapCtx does not close the connection if initialization fails
weijun
parents:
2
diff
changeset
|
323 |
throw e; |
e8d0473c25e8
6717680: LdapCtx does not close the connection if initialization fails
weijun
parents:
2
diff
changeset
|
324 |
} |
2 | 325 |
} |
326 |
||
327 |
LdapCtx(LdapCtx existing, String newDN) throws NamingException { |
|
328 |
useSsl = existing.useSsl; |
|
329 |
hasLdapsScheme = existing.hasLdapsScheme; |
|
330 |
useDefaultPortNumber = existing.useDefaultPortNumber; |
|
331 |
||
332 |
hostname = existing.hostname; |
|
333 |
port_number = existing.port_number; |
|
334 |
currentDN = newDN; |
|
335 |
if (existing.currentDN == currentDN) { |
|
336 |
currentParsedDN = existing.currentParsedDN; |
|
337 |
} else { |
|
338 |
currentParsedDN = parser.parse(currentDN); |
|
339 |
} |
|
340 |
||
341 |
envprops = existing.envprops; |
|
342 |
schemaTrees = existing.schemaTrees; |
|
343 |
||
344 |
clnt = existing.clnt; |
|
345 |
clnt.incRefCount(); |
|
346 |
||
347 |
parentIsLdapCtx = ((newDN == null || newDN.equals(existing.currentDN)) |
|
348 |
? existing.parentIsLdapCtx |
|
349 |
: true); |
|
350 |
||
351 |
// inherit these debugging/workaround flags |
|
352 |
trace = existing.trace; |
|
353 |
netscapeSchemaBug = existing.netscapeSchemaBug; |
|
354 |
||
355 |
initEnv(); |
|
356 |
} |
|
357 |
||
358 |
public LdapContext newInstance(Control[] reqCtls) throws NamingException { |
|
359 |
||
360 |
LdapContext clone = new LdapCtx(this, currentDN); |
|
361 |
||
362 |
// Connection controls are inherited from environment |
|
363 |
||
364 |
// Set clone's request controls |
|
365 |
// setRequestControls() will clone reqCtls |
|
366 |
clone.setRequestControls(reqCtls); |
|
367 |
return clone; |
|
368 |
} |
|
369 |
||
370 |
// --------------- Namespace Updates --------------------- |
|
371 |
// -- bind/rebind/unbind |
|
372 |
// -- rename |
|
373 |
// -- createSubcontext/destroySubcontext |
|
374 |
||
375 |
protected void c_bind(Name name, Object obj, Continuation cont) |
|
376 |
throws NamingException { |
|
377 |
c_bind(name, obj, null, cont); |
|
378 |
} |
|
379 |
||
380 |
/* |
|
381 |
* attrs == null |
|
382 |
* if obj is DirContext, attrs = obj.getAttributes() |
|
383 |
* if attrs == null && obj == null |
|
384 |
* disallow (cannot determine objectclass to use) |
|
385 |
* if obj == null |
|
386 |
* just create entry using attrs |
|
387 |
* else |
|
388 |
* objAttrs = create attributes for representing obj |
|
389 |
* attrs += objAttrs |
|
390 |
* create entry using attrs |
|
391 |
*/ |
|
392 |
protected void c_bind(Name name, Object obj, Attributes attrs, |
|
393 |
Continuation cont) |
|
394 |
throws NamingException { |
|
395 |
||
396 |
cont.setError(this, name); |
|
397 |
||
398 |
Attributes inputAttrs = attrs; // Attributes supplied by caller |
|
399 |
try { |
|
400 |
ensureOpen(); |
|
401 |
||
402 |
if (obj == null) { |
|
403 |
if (attrs == null) { |
|
404 |
throw new IllegalArgumentException( |
|
405 |
"cannot bind null object with no attributes"); |
|
406 |
} |
|
407 |
} else { |
|
408 |
attrs = Obj.determineBindAttrs(addrEncodingSeparator, obj, attrs, |
|
409 |
false, name, this, envprops); // not cloned |
|
410 |
} |
|
411 |
||
412 |
String newDN = fullyQualifiedName(name); |
|
413 |
attrs = addRdnAttributes(newDN, attrs, inputAttrs != attrs); |
|
414 |
LdapEntry entry = new LdapEntry(newDN, attrs); |
|
415 |
||
416 |
LdapResult answer = clnt.add(entry, reqCtls); |
|
417 |
respCtls = answer.resControls; // retrieve response controls |
|
418 |
||
419 |
if (answer.status != LdapClient.LDAP_SUCCESS) { |
|
420 |
processReturnCode(answer, name); |
|
421 |
} |
|
422 |
||
423 |
} catch (LdapReferralException e) { |
|
424 |
if (handleReferrals == LdapClient.LDAP_REF_THROW) |
|
425 |
throw cont.fillInException(e); |
|
426 |
||
427 |
// process the referrals sequentially |
|
428 |
while (true) { |
|
429 |
||
430 |
LdapReferralContext refCtx = |
|
431 |
(LdapReferralContext)e.getReferralContext(envprops, bindCtls); |
|
432 |
||
433 |
// repeat the original operation at the new context |
|
434 |
try { |
|
435 |
||
436 |
refCtx.bind(name, obj, inputAttrs); |
|
437 |
return; |
|
438 |
||
439 |
} catch (LdapReferralException re) { |
|
440 |
e = re; |
|
441 |
continue; |
|
442 |
||
443 |
} finally { |
|
444 |
// Make sure we close referral context |
|
445 |
refCtx.close(); |
|
446 |
} |
|
447 |
} |
|
448 |
||
449 |
} catch (IOException e) { |
|
450 |
NamingException e2 = new CommunicationException(e.getMessage()); |
|
451 |
e2.setRootCause(e); |
|
452 |
throw cont.fillInException(e2); |
|
453 |
||
454 |
} catch (NamingException e) { |
|
455 |
throw cont.fillInException(e); |
|
456 |
} |
|
457 |
} |
|
458 |
||
459 |
protected void c_rebind(Name name, Object obj, Continuation cont) |
|
460 |
throws NamingException { |
|
461 |
c_rebind(name, obj, null, cont); |
|
462 |
} |
|
463 |
||
464 |
||
465 |
/* |
|
466 |
* attrs == null |
|
467 |
* if obj is DirContext, attrs = obj.getAttributes(). |
|
468 |
* if attrs == null |
|
469 |
* leave any existing attributes alone |
|
470 |
* (set attrs = {objectclass=top} if object doesn't exist) |
|
471 |
* else |
|
472 |
* replace all existing attributes with attrs |
|
473 |
* if obj == null |
|
474 |
* just create entry using attrs |
|
475 |
* else |
|
476 |
* objAttrs = create attributes for representing obj |
|
477 |
* attrs += objAttrs |
|
478 |
* create entry using attrs |
|
479 |
*/ |
|
480 |
protected void c_rebind(Name name, Object obj, Attributes attrs, |
|
481 |
Continuation cont) throws NamingException { |
|
482 |
||
483 |
cont.setError(this, name); |
|
484 |
||
485 |
Attributes inputAttrs = attrs; |
|
486 |
||
487 |
try { |
|
488 |
Attributes origAttrs = null; |
|
489 |
||
490 |
// Check if name is bound |
|
491 |
try { |
|
492 |
origAttrs = c_getAttributes(name, null, cont); |
|
493 |
} catch (NameNotFoundException e) {} |
|
494 |
||
495 |
// Name not bound, just add it |
|
496 |
if (origAttrs == null) { |
|
497 |
c_bind(name, obj, attrs, cont); |
|
498 |
return; |
|
499 |
} |
|
500 |
||
501 |
// there's an object there already, need to figure out |
|
502 |
// what to do about its attributes |
|
503 |
||
504 |
if (attrs == null && obj instanceof DirContext) { |
|
505 |
attrs = ((DirContext)obj).getAttributes(""); |
|
506 |
} |
|
507 |
Attributes keepAttrs = (Attributes)origAttrs.clone(); |
|
508 |
||
509 |
if (attrs == null) { |
|
510 |
// we're not changing any attrs, leave old attributes alone |
|
511 |
||
512 |
// Remove Java-related object classes from objectclass attribute |
|
513 |
Attribute origObjectClass = |
|
514 |
origAttrs.get(Obj.JAVA_ATTRIBUTES[Obj.OBJECT_CLASS]); |
|
515 |
||
516 |
if (origObjectClass != null) { |
|
517 |
// clone so that keepAttrs is not affected |
|
518 |
origObjectClass = (Attribute)origObjectClass.clone(); |
|
519 |
for (int i = 0; i < Obj.JAVA_OBJECT_CLASSES.length; i++) { |
|
520 |
origObjectClass.remove(Obj.JAVA_OBJECT_CLASSES_LOWER[i]); |
|
521 |
origObjectClass.remove(Obj.JAVA_OBJECT_CLASSES[i]); |
|
522 |
} |
|
523 |
// update; |
|
524 |
origAttrs.put(origObjectClass); |
|
525 |
} |
|
526 |
||
527 |
// remove all Java-related attributes except objectclass |
|
528 |
for (int i = 1; i < Obj.JAVA_ATTRIBUTES.length; i++) { |
|
529 |
origAttrs.remove(Obj.JAVA_ATTRIBUTES[i]); |
|
530 |
} |
|
531 |
||
532 |
attrs = origAttrs; |
|
533 |
} |
|
534 |
if (obj != null) { |
|
535 |
attrs = |
|
536 |
Obj.determineBindAttrs(addrEncodingSeparator, obj, attrs, |
|
537 |
inputAttrs != attrs, name, this, envprops); |
|
538 |
} |
|
539 |
||
540 |
String newDN = fullyQualifiedName(name); |
|
541 |
// remove entry |
|
542 |
LdapResult answer = clnt.delete(newDN, reqCtls); |
|
543 |
respCtls = answer.resControls; // retrieve response controls |
|
544 |
||
545 |
if (answer.status != LdapClient.LDAP_SUCCESS) { |
|
546 |
processReturnCode(answer, name); |
|
547 |
return; |
|
548 |
} |
|
549 |
||
550 |
Exception addEx = null; |
|
551 |
try { |
|
552 |
attrs = addRdnAttributes(newDN, attrs, inputAttrs != attrs); |
|
553 |
||
554 |
// add it back using updated attrs |
|
555 |
LdapEntry entry = new LdapEntry(newDN, attrs); |
|
556 |
answer = clnt.add(entry, reqCtls); |
|
557 |
if (answer.resControls != null) { |
|
558 |
respCtls = appendVector(respCtls, answer.resControls); |
|
559 |
} |
|
560 |
} catch (NamingException ae) { |
|
561 |
addEx = ae; |
|
562 |
} catch (IOException ae) { |
|
563 |
addEx = ae; |
|
564 |
} |
|
565 |
||
566 |
if ((addEx != null && !(addEx instanceof LdapReferralException)) || |
|
567 |
answer.status != LdapClient.LDAP_SUCCESS) { |
|
568 |
// Attempt to restore old entry |
|
569 |
LdapResult answer2 = |
|
570 |
clnt.add(new LdapEntry(newDN, keepAttrs), reqCtls); |
|
571 |
if (answer2.resControls != null) { |
|
572 |
respCtls = appendVector(respCtls, answer2.resControls); |
|
573 |
} |
|
574 |
||
575 |
if (addEx == null) { |
|
576 |
processReturnCode(answer, name); |
|
577 |
} |
|
578 |
} |
|
579 |
||
580 |
// Rethrow exception |
|
581 |
if (addEx instanceof NamingException) { |
|
582 |
throw (NamingException)addEx; |
|
583 |
} else if (addEx instanceof IOException) { |
|
584 |
throw (IOException)addEx; |
|
585 |
} |
|
586 |
||
587 |
} catch (LdapReferralException e) { |
|
588 |
if (handleReferrals == LdapClient.LDAP_REF_THROW) |
|
589 |
throw cont.fillInException(e); |
|
590 |
||
591 |
// process the referrals sequentially |
|
592 |
while (true) { |
|
593 |
||
594 |
LdapReferralContext refCtx = |
|
595 |
(LdapReferralContext)e.getReferralContext(envprops, bindCtls); |
|
596 |
||
597 |
// repeat the original operation at the new context |
|
598 |
try { |
|
599 |
||
600 |
refCtx.rebind(name, obj, inputAttrs); |
|
601 |
return; |
|
602 |
||
603 |
} catch (LdapReferralException re) { |
|
604 |
e = re; |
|
605 |
continue; |
|
606 |
||
607 |
} finally { |
|
608 |
// Make sure we close referral context |
|
609 |
refCtx.close(); |
|
610 |
} |
|
611 |
} |
|
612 |
||
613 |
} catch (IOException e) { |
|
614 |
NamingException e2 = new CommunicationException(e.getMessage()); |
|
615 |
e2.setRootCause(e); |
|
616 |
throw cont.fillInException(e2); |
|
617 |
||
618 |
} catch (NamingException e) { |
|
619 |
throw cont.fillInException(e); |
|
620 |
} |
|
621 |
} |
|
622 |
||
623 |
protected void c_unbind(Name name, Continuation cont) |
|
624 |
throws NamingException { |
|
625 |
cont.setError(this, name); |
|
626 |
||
627 |
try { |
|
628 |
ensureOpen(); |
|
629 |
||
630 |
String fname = fullyQualifiedName(name); |
|
631 |
LdapResult answer = clnt.delete(fname, reqCtls); |
|
632 |
respCtls = answer.resControls; // retrieve response controls |
|
633 |
||
634 |
adjustDeleteStatus(fname, answer); |
|
635 |
||
636 |
if (answer.status != LdapClient.LDAP_SUCCESS) { |
|
637 |
processReturnCode(answer, name); |
|
638 |
} |
|
639 |
||
640 |
} catch (LdapReferralException e) { |
|
641 |
if (handleReferrals == LdapClient.LDAP_REF_THROW) |
|
642 |
throw cont.fillInException(e); |
|
643 |
||
644 |
// process the referrals sequentially |
|
645 |
while (true) { |
|
646 |
||
647 |
LdapReferralContext refCtx = |
|
648 |
(LdapReferralContext)e.getReferralContext(envprops, bindCtls); |
|
649 |
||
650 |
// repeat the original operation at the new context |
|
651 |
try { |
|
652 |
||
653 |
refCtx.unbind(name); |
|
654 |
return; |
|
655 |
||
656 |
} catch (LdapReferralException re) { |
|
657 |
e = re; |
|
658 |
continue; |
|
659 |
||
660 |
} finally { |
|
661 |
// Make sure we close referral context |
|
662 |
refCtx.close(); |
|
663 |
} |
|
664 |
} |
|
665 |
||
666 |
} catch (IOException e) { |
|
667 |
NamingException e2 = new CommunicationException(e.getMessage()); |
|
668 |
e2.setRootCause(e); |
|
669 |
throw cont.fillInException(e2); |
|
670 |
||
671 |
} catch (NamingException e) { |
|
672 |
throw cont.fillInException(e); |
|
673 |
} |
|
674 |
} |
|
675 |
||
676 |
protected void c_rename(Name oldName, Name newName, Continuation cont) |
|
677 |
throws NamingException |
|
678 |
{ |
|
679 |
Name oldParsed, newParsed; |
|
680 |
Name oldParent, newParent; |
|
681 |
String newRDN = null; |
|
682 |
String newSuperior = null; |
|
683 |
||
684 |
// assert (oldName instanceOf CompositeName); |
|
685 |
||
686 |
cont.setError(this, oldName); |
|
687 |
||
688 |
try { |
|
689 |
ensureOpen(); |
|
690 |
||
691 |
// permit oldName to be empty (for processing referral contexts) |
|
692 |
if (oldName.isEmpty()) { |
|
693 |
oldParent = parser.parse(""); |
|
694 |
} else { |
|
695 |
oldParsed = parser.parse(oldName.get(0)); // extract DN & parse |
|
696 |
oldParent = oldParsed.getPrefix(oldParsed.size() - 1); |
|
697 |
} |
|
698 |
||
699 |
if (newName instanceof CompositeName) { |
|
700 |
newParsed = parser.parse(newName.get(0)); // extract DN & parse |
|
701 |
} else { |
|
702 |
newParsed = newName; // CompoundName/LdapName is already parsed |
|
703 |
} |
|
704 |
newParent = newParsed.getPrefix(newParsed.size() - 1); |
|
705 |
||
706 |
if(!oldParent.equals(newParent)) { |
|
707 |
if (!clnt.isLdapv3) { |
|
708 |
throw new InvalidNameException( |
|
709 |
"LDAPv2 doesn't support changing " + |
|
710 |
"the parent as a result of a rename"); |
|
711 |
} else { |
|
712 |
newSuperior = fullyQualifiedName(newParent.toString()); |
|
713 |
} |
|
714 |
} |
|
715 |
||
716 |
newRDN = newParsed.get(newParsed.size() - 1); |
|
717 |
||
718 |
LdapResult answer = clnt.moddn(fullyQualifiedName(oldName), |
|
719 |
newRDN, |
|
720 |
deleteRDN, |
|
721 |
newSuperior, |
|
722 |
reqCtls); |
|
723 |
respCtls = answer.resControls; // retrieve response controls |
|
724 |
||
725 |
if (answer.status != LdapClient.LDAP_SUCCESS) { |
|
726 |
processReturnCode(answer, oldName); |
|
727 |
} |
|
728 |
||
729 |
} catch (LdapReferralException e) { |
|
730 |
||
731 |
// Record the new RDN (for use after the referral is followed). |
|
732 |
e.setNewRdn(newRDN); |
|
733 |
||
734 |
// Cannot continue when a referral has been received and a |
|
735 |
// newSuperior name was supplied (because the newSuperior is |
|
736 |
// relative to a naming context BEFORE the referral is followed). |
|
737 |
if (newSuperior != null) { |
|
738 |
PartialResultException pre = new PartialResultException( |
|
739 |
"Cannot continue referral processing when newSuperior is " + |
|
740 |
"nonempty: " + newSuperior); |
|
741 |
pre.setRootCause(cont.fillInException(e)); |
|
742 |
throw cont.fillInException(pre); |
|
743 |
} |
|
744 |
||
745 |
if (handleReferrals == LdapClient.LDAP_REF_THROW) |
|
746 |
throw cont.fillInException(e); |
|
747 |
||
748 |
// process the referrals sequentially |
|
749 |
while (true) { |
|
750 |
||
751 |
LdapReferralContext refCtx = |
|
752 |
(LdapReferralContext)e.getReferralContext(envprops, bindCtls); |
|
753 |
||
754 |
// repeat the original operation at the new context |
|
755 |
try { |
|
756 |
||
757 |
refCtx.rename(oldName, newName); |
|
758 |
return; |
|
759 |
||
760 |
} catch (LdapReferralException re) { |
|
761 |
e = re; |
|
762 |
continue; |
|
763 |
||
764 |
} finally { |
|
765 |
// Make sure we close referral context |
|
766 |
refCtx.close(); |
|
767 |
} |
|
768 |
} |
|
769 |
||
770 |
} catch (IOException e) { |
|
771 |
NamingException e2 = new CommunicationException(e.getMessage()); |
|
772 |
e2.setRootCause(e); |
|
773 |
throw cont.fillInException(e2); |
|
774 |
||
775 |
} catch (NamingException e) { |
|
776 |
throw cont.fillInException(e); |
|
777 |
} |
|
778 |
} |
|
779 |
||
780 |
protected Context c_createSubcontext(Name name, Continuation cont) |
|
781 |
throws NamingException { |
|
782 |
return c_createSubcontext(name, null, cont); |
|
783 |
} |
|
784 |
||
785 |
protected DirContext c_createSubcontext(Name name, Attributes attrs, |
|
786 |
Continuation cont) |
|
787 |
throws NamingException { |
|
788 |
cont.setError(this, name); |
|
789 |
||
790 |
Attributes inputAttrs = attrs; |
|
791 |
try { |
|
792 |
ensureOpen(); |
|
793 |
if (attrs == null) { |
|
794 |
// add structural objectclass; name needs to have "cn" |
|
795 |
Attribute oc = new BasicAttribute( |
|
796 |
Obj.JAVA_ATTRIBUTES[Obj.OBJECT_CLASS], |
|
797 |
Obj.JAVA_OBJECT_CLASSES[Obj.STRUCTURAL]); |
|
798 |
oc.add("top"); |
|
799 |
attrs = new BasicAttributes(true); // case ignore |
|
800 |
attrs.put(oc); |
|
801 |
} |
|
802 |
String newDN = fullyQualifiedName(name); |
|
803 |
attrs = addRdnAttributes(newDN, attrs, inputAttrs != attrs); |
|
804 |
||
805 |
LdapEntry entry = new LdapEntry(newDN, attrs); |
|
806 |
||
807 |
LdapResult answer = clnt.add(entry, reqCtls); |
|
808 |
respCtls = answer.resControls; // retrieve response controls |
|
809 |
||
810 |
if (answer.status != LdapClient.LDAP_SUCCESS) { |
|
811 |
processReturnCode(answer, name); |
|
812 |
return null; |
|
813 |
} |
|
814 |
||
815 |
// creation successful, get back live object |
|
816 |
return new LdapCtx(this, newDN); |
|
817 |
||
818 |
} catch (LdapReferralException e) { |
|
819 |
if (handleReferrals == LdapClient.LDAP_REF_THROW) |
|
820 |
throw cont.fillInException(e); |
|
821 |
||
822 |
// process the referrals sequentially |
|
823 |
while (true) { |
|
824 |
||
825 |
LdapReferralContext refCtx = |
|
826 |
(LdapReferralContext)e.getReferralContext(envprops, bindCtls); |
|
827 |
||
828 |
// repeat the original operation at the new context |
|
829 |
try { |
|
830 |
||
831 |
return refCtx.createSubcontext(name, inputAttrs); |
|
832 |
||
833 |
} catch (LdapReferralException re) { |
|
834 |
e = re; |
|
835 |
continue; |
|
836 |
||
837 |
} finally { |
|
838 |
// Make sure we close referral context |
|
839 |
refCtx.close(); |
|
840 |
} |
|
841 |
} |
|
842 |
||
843 |
} catch (IOException e) { |
|
844 |
NamingException e2 = new CommunicationException(e.getMessage()); |
|
845 |
e2.setRootCause(e); |
|
846 |
throw cont.fillInException(e2); |
|
847 |
||
848 |
} catch (NamingException e) { |
|
849 |
throw cont.fillInException(e); |
|
850 |
} |
|
851 |
} |
|
852 |
||
853 |
protected void c_destroySubcontext(Name name, Continuation cont) |
|
854 |
throws NamingException { |
|
855 |
cont.setError(this, name); |
|
856 |
||
857 |
try { |
|
858 |
ensureOpen(); |
|
859 |
||
860 |
String fname = fullyQualifiedName(name); |
|
861 |
LdapResult answer = clnt.delete(fname, reqCtls); |
|
862 |
respCtls = answer.resControls; // retrieve response controls |
|
863 |
||
864 |
adjustDeleteStatus(fname, answer); |
|
865 |
||
866 |
if (answer.status != LdapClient.LDAP_SUCCESS) { |
|
867 |
processReturnCode(answer, name); |
|
868 |
} |
|
869 |
||
870 |
} catch (LdapReferralException e) { |
|
871 |
if (handleReferrals == LdapClient.LDAP_REF_THROW) |
|
872 |
throw cont.fillInException(e); |
|
873 |
||
874 |
// process the referrals sequentially |
|
875 |
while (true) { |
|
876 |
||
877 |
LdapReferralContext refCtx = |
|
878 |
(LdapReferralContext)e.getReferralContext(envprops, bindCtls); |
|
879 |
||
880 |
// repeat the original operation at the new context |
|
881 |
try { |
|
882 |
||
883 |
refCtx.destroySubcontext(name); |
|
884 |
return; |
|
885 |
} catch (LdapReferralException re) { |
|
886 |
e = re; |
|
887 |
continue; |
|
888 |
} finally { |
|
889 |
// Make sure we close referral context |
|
890 |
refCtx.close(); |
|
891 |
} |
|
892 |
} |
|
893 |
} catch (IOException e) { |
|
894 |
NamingException e2 = new CommunicationException(e.getMessage()); |
|
895 |
e2.setRootCause(e); |
|
896 |
throw cont.fillInException(e2); |
|
897 |
} catch (NamingException e) { |
|
898 |
throw cont.fillInException(e); |
|
899 |
} |
|
900 |
} |
|
901 |
||
902 |
/** |
|
903 |
* Adds attributes from RDN to attrs if not already present. |
|
904 |
* Note that if attrs already contains an attribute by the same name, |
|
905 |
* or if the distinguished name is empty, then leave attrs unchanged. |
|
906 |
* |
|
907 |
* @param dn The non-null DN of the entry to add |
|
908 |
* @param attrs The non-null attributes of entry to add |
|
909 |
* @param directUpdate Whether attrs can be updated directly |
|
910 |
* @returns Non-null attributes with attributes from the RDN added |
|
911 |
*/ |
|
912 |
private static Attributes addRdnAttributes(String dn, Attributes attrs, |
|
913 |
boolean directUpdate) throws NamingException { |
|
914 |
||
915 |
// Handle the empty name |
|
916 |
if (dn.equals("")) { |
|
917 |
return attrs; |
|
918 |
} |
|
919 |
||
920 |
// Parse string name into list of RDNs |
|
921 |
//List<Rdn> rdnList = (new LdapName(dn)).rdns(); |
|
922 |
List rdnList = (new LdapName(dn)).getRdns(); |
|
923 |
||
924 |
// Get leaf RDN |
|
925 |
//Rdn rdn = rdnList.get(rdnList.size() - 1); |
|
926 |
Rdn rdn = (Rdn) rdnList.get(rdnList.size() - 1); |
|
927 |
Attributes nameAttrs = rdn.toAttributes(); |
|
928 |
||
929 |
// Add attributes of RDN to attrs if not already there |
|
930 |
NamingEnumeration enum_ = nameAttrs.getAll(); |
|
931 |
Attribute nameAttr; |
|
932 |
while (enum_.hasMore()) { |
|
933 |
nameAttr = (Attribute) enum_.next(); |
|
934 |
||
935 |
// If attrs already has the attribute, don't change or add to it |
|
936 |
if (attrs.get(nameAttr.getID()) == null) { |
|
937 |
||
938 |
/** |
|
939 |
* When attrs.isCaseIgnored() is false, attrs.get() will |
|
940 |
* return null when the case mis-matches for otherwise |
|
941 |
* equal attrIDs. |
|
942 |
* As the attrIDs' case is irrelevant for LDAP, ignore |
|
943 |
* the case of attrIDs even when attrs.isCaseIgnored() is |
|
944 |
* false. This is done by explicitly comparing the elements in |
|
945 |
* the enumeration of IDs with their case ignored. |
|
946 |
*/ |
|
947 |
if (!attrs.isCaseIgnored() && |
|
948 |
containsIgnoreCase(attrs.getIDs(), nameAttr.getID())) { |
|
949 |
continue; |
|
950 |
} |
|
951 |
||
952 |
if (!directUpdate) { |
|
953 |
attrs = (Attributes)attrs.clone(); |
|
954 |
directUpdate = true; |
|
955 |
} |
|
956 |
attrs.put(nameAttr); |
|
957 |
} |
|
958 |
} |
|
959 |
||
960 |
return attrs; |
|
961 |
} |
|
962 |
||
963 |
||
964 |
private static boolean containsIgnoreCase(NamingEnumeration enumStr, |
|
965 |
String str) throws NamingException { |
|
966 |
String strEntry; |
|
967 |
||
968 |
while (enumStr.hasMore()) { |
|
969 |
strEntry = (String) enumStr.next(); |
|
970 |
if (strEntry.equalsIgnoreCase(str)) { |
|
971 |
return true; |
|
972 |
} |
|
973 |
} |
|
974 |
return false; |
|
975 |
} |
|
976 |
||
977 |
||
978 |
private void adjustDeleteStatus(String fname, LdapResult answer) { |
|
979 |
if (answer.status == LdapClient.LDAP_NO_SUCH_OBJECT && |
|
980 |
answer.matchedDN != null) { |
|
981 |
try { |
|
982 |
// %%% RL: are there any implications for referrals? |
|
983 |
||
984 |
Name orig = parser.parse(fname); |
|
985 |
Name matched = parser.parse(answer.matchedDN); |
|
986 |
if ((orig.size() - matched.size()) == 1) |
|
987 |
answer.status = LdapClient.LDAP_SUCCESS; |
|
988 |
} catch (NamingException e) {} |
|
989 |
} |
|
990 |
} |
|
991 |
||
992 |
/* |
|
993 |
* Append the the second Vector onto the first Vector |
|
994 |
* (v2 must be non-null) |
|
995 |
*/ |
|
996 |
private static Vector appendVector(Vector v1, Vector v2) { |
|
997 |
if (v1 == null) { |
|
998 |
v1 = v2; |
|
999 |
} else { |
|
1000 |
for (int i = 0; i < v2.size(); i++) { |
|
1001 |
v1.addElement(v2.elementAt(i)); |
|
1002 |
} |
|
1003 |
} |
|
1004 |
return v1; |
|
1005 |
} |
|
1006 |
||
1007 |
// ------------- Lookups and Browsing ------------------------- |
|
1008 |
// lookup/lookupLink |
|
1009 |
// list/listBindings |
|
1010 |
||
1011 |
protected Object c_lookupLink(Name name, Continuation cont) |
|
1012 |
throws NamingException { |
|
1013 |
return c_lookup(name, cont); |
|
1014 |
} |
|
1015 |
||
1016 |
protected Object c_lookup(Name name, Continuation cont) |
|
1017 |
throws NamingException { |
|
1018 |
cont.setError(this, name); |
|
1019 |
Object obj = null; |
|
1020 |
Attributes attrs; |
|
1021 |
||
1022 |
try { |
|
1023 |
SearchControls cons = new SearchControls(); |
|
1024 |
cons.setSearchScope(SearchControls.OBJECT_SCOPE); |
|
1025 |
cons.setReturningAttributes(null); // ask for all attributes |
|
1026 |
cons.setReturningObjFlag(true); // need values to construct obj |
|
1027 |
||
1028 |
LdapResult answer = doSearchOnce(name, "(objectClass=*)", cons, true); |
|
1029 |
respCtls = answer.resControls; // retrieve response controls |
|
1030 |
||
1031 |
// should get back 1 SearchResponse and 1 SearchResult |
|
1032 |
||
1033 |
if (answer.status != LdapClient.LDAP_SUCCESS) { |
|
1034 |
processReturnCode(answer, name); |
|
1035 |
} |
|
1036 |
||
1037 |
if (answer.entries == null || answer.entries.size() != 1) { |
|
1038 |
// found it but got no attributes |
|
1039 |
attrs = new BasicAttributes(LdapClient.caseIgnore); |
|
1040 |
} else { |
|
1041 |
LdapEntry entry = (LdapEntry)answer.entries.elementAt(0); |
|
1042 |
attrs = entry.attributes; |
|
1043 |
||
1044 |
Vector entryCtls = entry.respCtls; // retrieve entry controls |
|
1045 |
if (entryCtls != null) { |
|
1046 |
appendVector(respCtls, entryCtls); // concatenate controls |
|
1047 |
} |
|
1048 |
} |
|
1049 |
||
1050 |
if (attrs.get(Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME]) != null) { |
|
1051 |
// serialized object or object reference |
|
1052 |
obj = Obj.decodeObject(attrs); |
|
1053 |
} |
|
1054 |
if (obj == null) { |
|
1055 |
obj = new LdapCtx(this, fullyQualifiedName(name)); |
|
1056 |
} |
|
1057 |
} catch (LdapReferralException e) { |
|
1058 |
if (handleReferrals == LdapClient.LDAP_REF_THROW) |
|
1059 |
throw cont.fillInException(e); |
|
1060 |
||
1061 |
// process the referrals sequentially |
|
1062 |
while (true) { |
|
1063 |
||
1064 |
LdapReferralContext refCtx = |
|
1065 |
(LdapReferralContext)e.getReferralContext(envprops, bindCtls); |
|
1066 |
// repeat the original operation at the new context |
|
1067 |
try { |
|
1068 |
||
1069 |
return refCtx.lookup(name); |
|
1070 |
||
1071 |
} catch (LdapReferralException re) { |
|
1072 |
e = re; |
|
1073 |
continue; |
|
1074 |
||
1075 |
} finally { |
|
1076 |
// Make sure we close referral context |
|
1077 |
refCtx.close(); |
|
1078 |
} |
|
1079 |
} |
|
1080 |
||
1081 |
} catch (NamingException e) { |
|
1082 |
throw cont.fillInException(e); |
|
1083 |
} |
|
1084 |
||
1085 |
try { |
|
1086 |
return DirectoryManager.getObjectInstance(obj, name, |
|
1087 |
this, envprops, attrs); |
|
1088 |
||
1089 |
} catch (NamingException e) { |
|
1090 |
throw cont.fillInException(e); |
|
1091 |
||
1092 |
} catch (Exception e) { |
|
1093 |
NamingException e2 = new NamingException( |
|
1094 |
"problem generating object using object factory"); |
|
1095 |
e2.setRootCause(e); |
|
1096 |
throw cont.fillInException(e2); |
|
1097 |
} |
|
1098 |
} |
|
1099 |
||
1100 |
protected NamingEnumeration c_list(Name name, Continuation cont) |
|
1101 |
throws NamingException { |
|
1102 |
SearchControls cons = new SearchControls(); |
|
1103 |
String[] classAttrs = new String[2]; |
|
1104 |
||
1105 |
classAttrs[0] = Obj.JAVA_ATTRIBUTES[Obj.OBJECT_CLASS]; |
|
1106 |
classAttrs[1] = Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME]; |
|
1107 |
cons.setReturningAttributes(classAttrs); |
|
1108 |
||
1109 |
// set this flag to override the typesOnly flag |
|
1110 |
cons.setReturningObjFlag(true); |
|
1111 |
||
1112 |
cont.setError(this, name); |
|
1113 |
||
1114 |
LdapResult answer = null; |
|
1115 |
||
1116 |
try { |
|
1117 |
answer = doSearch(name, "(objectClass=*)", cons, true, true); |
|
1118 |
||
1119 |
// list result may contain continuation references |
|
1120 |
if ((answer.status != LdapClient.LDAP_SUCCESS) || |
|
1121 |
(answer.referrals != null)) { |
|
1122 |
processReturnCode(answer, name); |
|
1123 |
} |
|
1124 |
||
1125 |
return new LdapNamingEnumeration(this, answer, name, cont); |
|
1126 |
||
1127 |
} catch (LdapReferralException e) { |
|
1128 |
if (handleReferrals == LdapClient.LDAP_REF_THROW) |
|
1129 |
throw cont.fillInException(e); |
|
1130 |
||
1131 |
// process the referrals sequentially |
|
1132 |
while (true) { |
|
1133 |
||
1134 |
LdapReferralContext refCtx = |
|
1135 |
(LdapReferralContext)e.getReferralContext(envprops, bindCtls); |
|
1136 |
||
1137 |
// repeat the original operation at the new context |
|
1138 |
try { |
|
1139 |
||
1140 |
return refCtx.list(name); |
|
1141 |
||
1142 |
} catch (LdapReferralException re) { |
|
1143 |
e = re; |
|
1144 |
continue; |
|
1145 |
||
1146 |
} finally { |
|
1147 |
// Make sure we close referral context |
|
1148 |
refCtx.close(); |
|
1149 |
} |
|
1150 |
} |
|
1151 |
||
1152 |
} catch (LimitExceededException e) { |
|
1153 |
LdapNamingEnumeration res = |
|
1154 |
new LdapNamingEnumeration(this, answer, name, cont); |
|
1155 |
||
1156 |
res.setNamingException( |
|
1157 |
(LimitExceededException)cont.fillInException(e)); |
|
1158 |
return res; |
|
1159 |
||
1160 |
} catch (PartialResultException e) { |
|
1161 |
LdapNamingEnumeration res = |
|
1162 |
new LdapNamingEnumeration(this, answer, name, cont); |
|
1163 |
||
1164 |
res.setNamingException( |
|
1165 |
(PartialResultException)cont.fillInException(e)); |
|
1166 |
return res; |
|
1167 |
||
1168 |
} catch (NamingException e) { |
|
1169 |
throw cont.fillInException(e); |
|
1170 |
} |
|
1171 |
} |
|
1172 |
||
1173 |
protected NamingEnumeration c_listBindings(Name name, Continuation cont) |
|
1174 |
throws NamingException { |
|
1175 |
||
1176 |
SearchControls cons = new SearchControls(); |
|
1177 |
cons.setReturningAttributes(null); // ask for all attributes |
|
1178 |
cons.setReturningObjFlag(true); // need values to construct obj |
|
1179 |
||
1180 |
cont.setError(this, name); |
|
1181 |
||
1182 |
LdapResult answer = null; |
|
1183 |
||
1184 |
try { |
|
1185 |
answer = doSearch(name, "(objectClass=*)", cons, true, true); |
|
1186 |
||
1187 |
// listBindings result may contain continuation references |
|
1188 |
if ((answer.status != LdapClient.LDAP_SUCCESS) || |
|
1189 |
(answer.referrals != null)) { |
|
1190 |
processReturnCode(answer, name); |
|
1191 |
} |
|
1192 |
||
1193 |
return new LdapBindingEnumeration(this, answer, name, cont); |
|
1194 |
||
1195 |
} catch (LdapReferralException e) { |
|
1196 |
if (handleReferrals == LdapClient.LDAP_REF_THROW) |
|
1197 |
throw cont.fillInException(e); |
|
1198 |
||
1199 |
// process the referrals sequentially |
|
1200 |
while (true) { |
|
1201 |
||
1202 |
LdapReferralContext refCtx = |
|
1203 |
(LdapReferralContext)e.getReferralContext(envprops, bindCtls); |
|
1204 |
||
1205 |
// repeat the original operation at the new context |
|
1206 |
try { |
|
1207 |
||
1208 |
return refCtx.listBindings(name); |
|
1209 |
||
1210 |
} catch (LdapReferralException re) { |
|
1211 |
e = re; |
|
1212 |
continue; |
|
1213 |
||
1214 |
} finally { |
|
1215 |
// Make sure we close referral context |
|
1216 |
refCtx.close(); |
|
1217 |
} |
|
1218 |
} |
|
1219 |
} catch (LimitExceededException e) { |
|
1220 |
LdapBindingEnumeration res = |
|
1221 |
new LdapBindingEnumeration(this, answer, name, cont); |
|
1222 |
||
1223 |
res.setNamingException( |
|
1224 |
(LimitExceededException)cont.fillInException(e)); |
|
1225 |
return res; |
|
1226 |
||
1227 |
} catch (PartialResultException e) { |
|
1228 |
LdapBindingEnumeration res = |
|
1229 |
new LdapBindingEnumeration(this, answer, name, cont); |
|
1230 |
||
1231 |
res.setNamingException( |
|
1232 |
(PartialResultException)cont.fillInException(e)); |
|
1233 |
return res; |
|
1234 |
||
1235 |
} catch (NamingException e) { |
|
1236 |
throw cont.fillInException(e); |
|
1237 |
} |
|
1238 |
} |
|
1239 |
||
1240 |
// --------------- Name-related Methods ----------------------- |
|
1241 |
// -- getNameParser/getNameInNamespace/composeName |
|
1242 |
||
1243 |
protected NameParser c_getNameParser(Name name, Continuation cont) |
|
1244 |
throws NamingException |
|
1245 |
{ |
|
1246 |
// ignore name, always return same parser |
|
1247 |
cont.setSuccess(); |
|
1248 |
return parser; |
|
1249 |
} |
|
1250 |
||
1251 |
public String getNameInNamespace() { |
|
1252 |
return currentDN; |
|
1253 |
} |
|
1254 |
||
1255 |
public Name composeName(Name name, Name prefix) |
|
1256 |
throws NamingException |
|
1257 |
{ |
|
1258 |
Name result; |
|
1259 |
||
1260 |
// Handle compound names. A pair of LdapNames is an easy case. |
|
1261 |
if ((name instanceof LdapName) && (prefix instanceof LdapName)) { |
|
1262 |
result = (Name)(prefix.clone()); |
|
1263 |
result.addAll(name); |
|
1264 |
return new CompositeName().add(result.toString()); |
|
1265 |
} |
|
1266 |
if (!(name instanceof CompositeName)) { |
|
1267 |
name = new CompositeName().add(name.toString()); |
|
1268 |
} |
|
1269 |
if (!(prefix instanceof CompositeName)) { |
|
1270 |
prefix = new CompositeName().add(prefix.toString()); |
|
1271 |
} |
|
1272 |
||
1273 |
int prefixLast = prefix.size() - 1; |
|
1274 |
||
1275 |
if (name.isEmpty() || prefix.isEmpty() || |
|
1276 |
name.get(0).equals("") || prefix.get(prefixLast).equals("")) { |
|
1277 |
return super.composeName(name, prefix); |
|
1278 |
} |
|
1279 |
||
1280 |
result = (Name)(prefix.clone()); |
|
1281 |
result.addAll(name); |
|
1282 |
||
1283 |
if (parentIsLdapCtx) { |
|
1284 |
String ldapComp = concatNames(result.get(prefixLast + 1), |
|
1285 |
result.get(prefixLast)); |
|
1286 |
result.remove(prefixLast + 1); |
|
1287 |
result.remove(prefixLast); |
|
1288 |
result.add(prefixLast, ldapComp); |
|
1289 |
} |
|
1290 |
return result; |
|
1291 |
} |
|
1292 |
||
1293 |
private String fullyQualifiedName(Name rel) { |
|
1294 |
return rel.isEmpty() |
|
1295 |
? currentDN |
|
1296 |
: fullyQualifiedName(rel.get(0)); |
|
1297 |
} |
|
1298 |
||
1299 |
private String fullyQualifiedName(String rel) { |
|
1300 |
return (concatNames(rel, currentDN)); |
|
1301 |
} |
|
1302 |
||
1303 |
// used by LdapSearchEnumeration |
|
1304 |
private static String concatNames(String lesser, String greater) { |
|
1305 |
if (lesser == null || lesser.equals("")) { |
|
1306 |
return greater; |
|
1307 |
} else if (greater == null || greater.equals("")) { |
|
1308 |
return lesser; |
|
1309 |
} else { |
|
1310 |
return (lesser + "," + greater); |
|
1311 |
} |
|
1312 |
} |
|
1313 |
||
1314 |
// --------------- Reading and Updating Attributes |
|
1315 |
// getAttributes/modifyAttributes |
|
1316 |
||
1317 |
protected Attributes c_getAttributes(Name name, String[] attrIds, |
|
1318 |
Continuation cont) |
|
1319 |
throws NamingException { |
|
1320 |
cont.setError(this, name); |
|
1321 |
||
1322 |
SearchControls cons = new SearchControls(); |
|
1323 |
cons.setSearchScope(SearchControls.OBJECT_SCOPE); |
|
1324 |
cons.setReturningAttributes(attrIds); |
|
1325 |
||
1326 |
try { |
|
1327 |
LdapResult answer = |
|
1328 |
doSearchOnce(name, "(objectClass=*)", cons, true); |
|
1329 |
respCtls = answer.resControls; // retrieve response controls |
|
1330 |
||
1331 |
if (answer.status != LdapClient.LDAP_SUCCESS) { |
|
1332 |
processReturnCode(answer, name); |
|
1333 |
} |
|
1334 |
||
1335 |
if (answer.entries == null || answer.entries.size() != 1) { |
|
1336 |
return new BasicAttributes(LdapClient.caseIgnore); |
|
1337 |
} |
|
1338 |
||
1339 |
// get attributes from result |
|
1340 |
LdapEntry entry = (LdapEntry) answer.entries.elementAt(0); |
|
1341 |
||
1342 |
Vector entryCtls = entry.respCtls; // retrieve entry controls |
|
1343 |
if (entryCtls != null) { |
|
1344 |
appendVector(respCtls, entryCtls); // concatenate controls |
|
1345 |
} |
|
1346 |
||
1347 |
// do this so attributes can find their schema |
|
1348 |
setParents(entry.attributes, (Name) name.clone()); |
|
1349 |
||
1350 |
return (entry.attributes); |
|
1351 |
||
1352 |
} catch (LdapReferralException e) { |
|
1353 |
if (handleReferrals == LdapClient.LDAP_REF_THROW) |
|
1354 |
throw cont.fillInException(e); |
|
1355 |
||
1356 |
// process the referrals sequentially |
|
1357 |
while (true) { |
|
1358 |
||
1359 |
LdapReferralContext refCtx = |
|
1360 |
(LdapReferralContext)e.getReferralContext(envprops, bindCtls); |
|
1361 |
||
1362 |
// repeat the original operation at the new context |
|
1363 |
try { |
|
1364 |
||
1365 |
return refCtx.getAttributes(name, attrIds); |
|
1366 |
||
1367 |
} catch (LdapReferralException re) { |
|
1368 |
e = re; |
|
1369 |
continue; |
|
1370 |
||
1371 |
} finally { |
|
1372 |
// Make sure we close referral context |
|
1373 |
refCtx.close(); |
|
1374 |
} |
|
1375 |
} |
|
1376 |
||
1377 |
} catch (NamingException e) { |
|
1378 |
throw cont.fillInException(e); |
|
1379 |
} |
|
1380 |
} |
|
1381 |
||
1382 |
protected void c_modifyAttributes(Name name, int mod_op, Attributes attrs, |
|
1383 |
Continuation cont) |
|
1384 |
throws NamingException { |
|
1385 |
||
1386 |
cont.setError(this, name); |
|
1387 |
||
1388 |
try { |
|
1389 |
ensureOpen(); |
|
1390 |
||
1391 |
if (attrs == null || attrs.size() == 0) { |
|
1392 |
return; // nothing to do |
|
1393 |
} |
|
1394 |
String newDN = fullyQualifiedName(name); |
|
1395 |
int jmod_op = convertToLdapModCode(mod_op); |
|
1396 |
||
1397 |
// construct mod list |
|
1398 |
int[] jmods = new int[attrs.size()]; |
|
1399 |
Attribute[] jattrs = new Attribute[attrs.size()]; |
|
1400 |
||
1401 |
NamingEnumeration ae = attrs.getAll(); |
|
1402 |
for(int i = 0; i < jmods.length && ae.hasMore(); i++) { |
|
1403 |
jmods[i] = jmod_op; |
|
1404 |
jattrs[i] = (Attribute)ae.next(); |
|
1405 |
} |
|
1406 |
||
1407 |
LdapResult answer = clnt.modify(newDN, jmods, jattrs, reqCtls); |
|
1408 |
respCtls = answer.resControls; // retrieve response controls |
|
1409 |
||
1410 |
if (answer.status != LdapClient.LDAP_SUCCESS) { |
|
1411 |
processReturnCode(answer, name); |
|
1412 |
return; |
|
1413 |
} |
|
1414 |
||
1415 |
} catch (LdapReferralException e) { |
|
1416 |
if (handleReferrals == LdapClient.LDAP_REF_THROW) |
|
1417 |
throw cont.fillInException(e); |
|
1418 |
||
1419 |
// process the referrals sequentially |
|
1420 |
while (true) { |
|
1421 |
||
1422 |
LdapReferralContext refCtx = |
|
1423 |
(LdapReferralContext)e.getReferralContext(envprops, bindCtls); |
|
1424 |
||
1425 |
// repeat the original operation at the new context |
|
1426 |
try { |
|
1427 |
||
1428 |
refCtx.modifyAttributes(name, mod_op, attrs); |
|
1429 |
return; |
|
1430 |
||
1431 |
} catch (LdapReferralException re) { |
|
1432 |
e = re; |
|
1433 |
continue; |
|
1434 |
||
1435 |
} finally { |
|
1436 |
// Make sure we close referral context |
|
1437 |
refCtx.close(); |
|
1438 |
} |
|
1439 |
} |
|
1440 |
||
1441 |
} catch (IOException e) { |
|
1442 |
NamingException e2 = new CommunicationException(e.getMessage()); |
|
1443 |
e2.setRootCause(e); |
|
1444 |
throw cont.fillInException(e2); |
|
1445 |
||
1446 |
} catch (NamingException e) { |
|
1447 |
throw cont.fillInException(e); |
|
1448 |
} |
|
1449 |
} |
|
1450 |
||
1451 |
protected void c_modifyAttributes(Name name, ModificationItem[] mods, |
|
1452 |
Continuation cont) |
|
1453 |
throws NamingException { |
|
1454 |
cont.setError(this, name); |
|
1455 |
||
1456 |
try { |
|
1457 |
ensureOpen(); |
|
1458 |
||
1459 |
if (mods == null || mods.length == 0) { |
|
1460 |
return; // nothing to do |
|
1461 |
} |
|
1462 |
String newDN = fullyQualifiedName(name); |
|
1463 |
||
1464 |
// construct mod list |
|
1465 |
int[] jmods = new int[mods.length]; |
|
1466 |
Attribute[] jattrs = new Attribute[mods.length]; |
|
1467 |
ModificationItem mod; |
|
1468 |
for (int i = 0; i < jmods.length; i++) { |
|
1469 |
mod = mods[i]; |
|
1470 |
jmods[i] = convertToLdapModCode(mod.getModificationOp()); |
|
1471 |
jattrs[i] = mod.getAttribute(); |
|
1472 |
} |
|
1473 |
||
1474 |
LdapResult answer = clnt.modify(newDN, jmods, jattrs, reqCtls); |
|
1475 |
respCtls = answer.resControls; // retrieve response controls |
|
1476 |
||
1477 |
if (answer.status != LdapClient.LDAP_SUCCESS) { |
|
1478 |
processReturnCode(answer, name); |
|
1479 |
} |
|
1480 |
||
1481 |
} catch (LdapReferralException e) { |
|
1482 |
if (handleReferrals == LdapClient.LDAP_REF_THROW) |
|
1483 |
throw cont.fillInException(e); |
|
1484 |
||
1485 |
// process the referrals sequentially |
|
1486 |
while (true) { |
|
1487 |
||
1488 |
LdapReferralContext refCtx = |
|
1489 |
(LdapReferralContext)e.getReferralContext(envprops, bindCtls); |
|
1490 |
||
1491 |
// repeat the original operation at the new context |
|
1492 |
try { |
|
1493 |
||
1494 |
refCtx.modifyAttributes(name, mods); |
|
1495 |
return; |
|
1496 |
||
1497 |
} catch (LdapReferralException re) { |
|
1498 |
e = re; |
|
1499 |
continue; |
|
1500 |
||
1501 |
} finally { |
|
1502 |
// Make sure we close referral context |
|
1503 |
refCtx.close(); |
|
1504 |
} |
|
1505 |
} |
|
1506 |
||
1507 |
} catch (IOException e) { |
|
1508 |
NamingException e2 = new CommunicationException(e.getMessage()); |
|
1509 |
e2.setRootCause(e); |
|
1510 |
throw cont.fillInException(e2); |
|
1511 |
||
1512 |
} catch (NamingException e) { |
|
1513 |
throw cont.fillInException(e); |
|
1514 |
} |
|
1515 |
} |
|
1516 |
||
1517 |
private static int convertToLdapModCode(int mod_op) { |
|
1518 |
switch (mod_op) { |
|
1519 |
case DirContext.ADD_ATTRIBUTE: |
|
1520 |
return(LdapClient.ADD); |
|
1521 |
||
1522 |
case DirContext.REPLACE_ATTRIBUTE: |
|
1523 |
return (LdapClient.REPLACE); |
|
1524 |
||
1525 |
case DirContext.REMOVE_ATTRIBUTE: |
|
1526 |
return (LdapClient.DELETE); |
|
1527 |
||
1528 |
default: |
|
1529 |
throw new IllegalArgumentException("Invalid modification code"); |
|
1530 |
} |
|
1531 |
} |
|
1532 |
||
1533 |
// ------------------- Schema ----------------------- |
|
1534 |
||
1535 |
protected DirContext c_getSchema(Name name, Continuation cont) |
|
1536 |
throws NamingException { |
|
1537 |
cont.setError(this, name); |
|
1538 |
try { |
|
1539 |
return getSchemaTree(name); |
|
1540 |
||
1541 |
} catch (NamingException e) { |
|
1542 |
throw cont.fillInException(e); |
|
1543 |
} |
|
1544 |
} |
|
1545 |
||
1546 |
protected DirContext c_getSchemaClassDefinition(Name name, |
|
1547 |
Continuation cont) |
|
1548 |
throws NamingException { |
|
1549 |
cont.setError(this, name); |
|
1550 |
||
1551 |
try { |
|
1552 |
// retrieve the objectClass attribute from LDAP |
|
1553 |
Attribute objectClassAttr = c_getAttributes(name, |
|
1554 |
new String[]{"objectclass"}, cont).get("objectclass"); |
|
1555 |
if (objectClassAttr == null || objectClassAttr.size() == 0) { |
|
1556 |
return EMPTY_SCHEMA; |
|
1557 |
} |
|
1558 |
||
1559 |
// retrieve the root of the ObjectClass schema tree |
|
1560 |
Context ocSchema = (Context) c_getSchema(name, cont).lookup( |
|
1561 |
LdapSchemaParser.OBJECTCLASS_DEFINITION_NAME); |
|
1562 |
||
1563 |
// create a context to hold the schema objects representing the object |
|
1564 |
// classes |
|
1565 |
HierMemDirCtx objectClassCtx = new HierMemDirCtx(); |
|
1566 |
DirContext objectClassDef; |
|
1567 |
String objectClassName; |
|
1568 |
for (Enumeration objectClasses = objectClassAttr.getAll(); |
|
1569 |
objectClasses.hasMoreElements(); ) { |
|
1570 |
objectClassName = (String)objectClasses.nextElement(); |
|
1571 |
// %%% Should we fail if not found, or just continue? |
|
1572 |
objectClassDef = (DirContext)ocSchema.lookup(objectClassName); |
|
1573 |
objectClassCtx.bind(objectClassName, objectClassDef); |
|
1574 |
} |
|
1575 |
||
1576 |
// Make context read-only |
|
1577 |
objectClassCtx.setReadOnly( |
|
1578 |
new SchemaViolationException("Cannot update schema object")); |
|
1579 |
return (DirContext)objectClassCtx; |
|
1580 |
||
1581 |
} catch (NamingException e) { |
|
1582 |
throw cont.fillInException(e); |
|
1583 |
} |
|
1584 |
} |
|
1585 |
||
1586 |
/* |
|
1587 |
* getSchemaTree first looks to see if we have already built a |
|
1588 |
* schema tree for the given entry. If not, it builds a new one and |
|
1589 |
* stores it in our private hash table |
|
1590 |
*/ |
|
1591 |
private DirContext getSchemaTree(Name name) throws NamingException { |
|
1592 |
String subschemasubentry = getSchemaEntry(name, true); |
|
1593 |
||
1594 |
DirContext schemaTree = (DirContext)schemaTrees.get(subschemasubentry); |
|
1595 |
||
1596 |
if(schemaTree==null) { |
|
1597 |
if(debug){System.err.println("LdapCtx: building new schema tree " + this);} |
|
1598 |
schemaTree = buildSchemaTree(subschemasubentry); |
|
1599 |
schemaTrees.put(subschemasubentry, schemaTree); |
|
1600 |
} |
|
1601 |
||
1602 |
return schemaTree; |
|
1603 |
} |
|
1604 |
||
1605 |
/* |
|
1606 |
* buildSchemaTree builds the schema tree corresponding to the |
|
1607 |
* given subschemasubentree |
|
1608 |
*/ |
|
1609 |
private DirContext buildSchemaTree(String subschemasubentry) |
|
1610 |
throws NamingException { |
|
1611 |
||
1612 |
// get the schema entry itself |
|
1613 |
// DO ask for return object here because we need it to |
|
1614 |
// create context. Since asking for all attrs, we won't |
|
1615 |
// be transmitting any specific attrIDs (like Java-specific ones). |
|
1616 |
SearchControls constraints = new |
|
1617 |
SearchControls(SearchControls.OBJECT_SCOPE, |
|
1618 |
0, 0, /* count and time limits */ |
|
1619 |
SCHEMA_ATTRIBUTES /* return schema attrs */, |
|
1620 |
true /* return obj */, |
|
1621 |
false /*deref link */ ); |
|
1622 |
||
1623 |
Name sse = (new CompositeName()).add(subschemasubentry); |
|
1624 |
NamingEnumeration results = |
|
1625 |
searchAux(sse, "(objectClass=subschema)", constraints, |
|
1626 |
false, true, new Continuation()); |
|
1627 |
||
1628 |
if(!results.hasMore()) { |
|
1629 |
throw new OperationNotSupportedException( |
|
1630 |
"Cannot get read subschemasubentry: " + subschemasubentry); |
|
1631 |
} |
|
1632 |
SearchResult result = (SearchResult)results.next(); |
|
1633 |
results.close(); |
|
1634 |
||
1635 |
Object obj = result.getObject(); |
|
1636 |
if(!(obj instanceof LdapCtx)) { |
|
1637 |
throw new NamingException( |
|
1638 |
"Cannot get schema object as DirContext: " + subschemasubentry); |
|
1639 |
} |
|
1640 |
||
1641 |
return LdapSchemaCtx.createSchemaTree(envprops, subschemasubentry, |
|
1642 |
(LdapCtx)obj /* schema entry */, |
|
1643 |
result.getAttributes() /* schema attributes */, |
|
1644 |
netscapeSchemaBug); |
|
1645 |
} |
|
1646 |
||
1647 |
/* |
|
1648 |
* getSchemaEntree returns the DN of the subschemasubentree for the |
|
1649 |
* given entree. It first looks to see if the given entry has |
|
1650 |
* a subschema different from that of the root DIT (by looking for |
|
1651 |
* a "subschemasubentry" attribute). If it doesn't find one, it returns |
|
1652 |
* the one for the root of the DIT (by looking for the root's |
|
1653 |
* "subschemasubentry" attribute). |
|
1654 |
* |
|
1655 |
* This function is called regardless of the server's version, since |
|
1656 |
* an administrator may have setup the server to support client schema |
|
1657 |
* queries. If this function trys a serarch on a v2 server that |
|
1658 |
* doesn't support schema, one of these two things will happen: |
|
1659 |
* 1) It will get an exception when querying the root DSE |
|
1660 |
* 2) It will not find a subschemasubentry on the root DSE |
|
1661 |
* If either of these things occur and the server is not v3, we |
|
1662 |
* throw OperationNotSupported. |
|
1663 |
* |
|
1664 |
* the relative flag tells whether the given name is relative to this |
|
1665 |
* context. |
|
1666 |
*/ |
|
1667 |
private String getSchemaEntry(Name name, boolean relative) |
|
1668 |
throws NamingException { |
|
1669 |
||
1670 |
// Asks for operational attribute "subschemasubentry" |
|
1671 |
SearchControls constraints = new SearchControls(SearchControls.OBJECT_SCOPE, |
|
1672 |
0, 0, /* count and time limits */ |
|
1673 |
new String[]{"subschemasubentry"} /* attr to return */, |
|
1674 |
false /* returning obj */, |
|
1675 |
false /* deref link */); |
|
1676 |
||
1677 |
NamingEnumeration results; |
|
1678 |
try { |
|
1679 |
results = searchAux(name, "objectclass=*", constraints, relative, |
|
1680 |
true, new Continuation()); |
|
1681 |
||
1682 |
} catch (NamingException ne) { |
|
1683 |
if (!clnt.isLdapv3 && currentDN.length() == 0 && name.isEmpty()) { |
|
1684 |
// we got an error looking for a root entry on an ldapv2 |
|
1685 |
// server. The server must not support schema. |
|
1686 |
throw new OperationNotSupportedException( |
|
1687 |
"Cannot get schema information from server"); |
|
1688 |
} else { |
|
1689 |
throw ne; |
|
1690 |
} |
|
1691 |
} |
|
1692 |
||
1693 |
if (!results.hasMoreElements()) { |
|
1694 |
throw new ConfigurationException( |
|
1695 |
"Requesting schema of nonexistent entry: " + name); |
|
1696 |
} |
|
1697 |
||
1698 |
SearchResult result = (SearchResult) results.next(); |
|
1699 |
results.close(); |
|
1700 |
||
1701 |
Attribute schemaEntryAttr = |
|
1702 |
result.getAttributes().get("subschemasubentry"); |
|
1703 |
//System.err.println("schema entry attrs: " + schemaEntryAttr); |
|
1704 |
||
1705 |
if (schemaEntryAttr == null || schemaEntryAttr.size() < 0) { |
|
1706 |
if (currentDN.length() == 0 && name.isEmpty()) { |
|
1707 |
// the server doesn't have a subschemasubentry in its root DSE. |
|
1708 |
// therefore, it doesn't support schema. |
|
1709 |
throw new OperationNotSupportedException( |
|
1710 |
"Cannot read subschemasubentry of root DSE"); |
|
1711 |
} else { |
|
1712 |
return getSchemaEntry(new CompositeName(), false); |
|
1713 |
} |
|
1714 |
} |
|
1715 |
||
1716 |
return (String)(schemaEntryAttr.get()); // return schema entry name |
|
1717 |
} |
|
1718 |
||
1719 |
// package-private; used by search enum. |
|
1720 |
// Set attributes to point to this context in case some one |
|
1721 |
// asked for their schema |
|
1722 |
void setParents(Attributes attrs, Name name) throws NamingException { |
|
1723 |
NamingEnumeration ae = attrs.getAll(); |
|
1724 |
while(ae.hasMore()) { |
|
1725 |
((LdapAttribute) ae.next()).setParent(this, name); |
|
1726 |
} |
|
1727 |
} |
|
1728 |
||
1729 |
/* |
|
1730 |
* Returns the URL associated with this context; used by LdapAttribute |
|
1731 |
* after deserialization to get pointer to this context. |
|
1732 |
*/ |
|
1733 |
String getURL() { |
|
1734 |
if (url == null) { |
|
1735 |
url = LdapURL.toUrlString(hostname, port_number, currentDN, |
|
1736 |
hasLdapsScheme); |
|
1737 |
} |
|
1738 |
||
1739 |
return url; |
|
1740 |
} |
|
1741 |
||
1742 |
// --------------------- Searches ----------------------------- |
|
1743 |
protected NamingEnumeration c_search(Name name, |
|
1744 |
Attributes matchingAttributes, |
|
1745 |
Continuation cont) |
|
1746 |
throws NamingException { |
|
1747 |
return c_search(name, matchingAttributes, null, cont); |
|
1748 |
} |
|
1749 |
||
1750 |
protected NamingEnumeration c_search(Name name, |
|
1751 |
Attributes matchingAttributes, |
|
1752 |
String[] attributesToReturn, |
|
1753 |
Continuation cont) |
|
1754 |
throws NamingException { |
|
1755 |
SearchControls cons = new SearchControls(); |
|
1756 |
cons.setReturningAttributes(attributesToReturn); |
|
1757 |
String filter; |
|
1758 |
try { |
|
1759 |
filter = SearchFilter.format(matchingAttributes); |
|
1760 |
} catch (NamingException e) { |
|
1761 |
cont.setError(this, name); |
|
1762 |
throw cont.fillInException(e); |
|
1763 |
} |
|
1764 |
return c_search(name, filter, cons, cont); |
|
1765 |
} |
|
1766 |
||
1767 |
protected NamingEnumeration c_search(Name name, |
|
1768 |
String filter, |
|
1769 |
SearchControls cons, |
|
1770 |
Continuation cont) |
|
1771 |
throws NamingException { |
|
8564
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
1772 |
return searchAux(name, filter, cloneSearchControls(cons), true, |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
1773 |
waitForReply, cont); |
2 | 1774 |
} |
1775 |
||
1776 |
protected NamingEnumeration c_search(Name name, |
|
1777 |
String filterExpr, |
|
1778 |
Object[] filterArgs, |
|
1779 |
SearchControls cons, |
|
1780 |
Continuation cont) |
|
1781 |
throws NamingException { |
|
1782 |
String strfilter; |
|
1783 |
try { |
|
1784 |
strfilter = SearchFilter.format(filterExpr, filterArgs); |
|
1785 |
} catch (NamingException e) { |
|
1786 |
cont.setError(this, name); |
|
1787 |
throw cont.fillInException(e); |
|
1788 |
} |
|
1789 |
return c_search(name, strfilter, cons, cont); |
|
1790 |
} |
|
1791 |
||
1792 |
// Used by NamingNotifier |
|
1793 |
NamingEnumeration searchAux(Name name, |
|
1794 |
String filter, |
|
1795 |
SearchControls cons, |
|
1796 |
boolean relative, |
|
1797 |
boolean waitForReply, Continuation cont) throws NamingException { |
|
1798 |
||
1799 |
LdapResult answer = null; |
|
1800 |
String[] tokens = new String[2]; // stores ldap compare op. values |
|
1801 |
String[] reqAttrs; // remember what was asked |
|
1802 |
||
1803 |
if (cons == null) { |
|
1804 |
cons = new SearchControls(); |
|
1805 |
} |
|
1806 |
reqAttrs = cons.getReturningAttributes(); |
|
1807 |
||
1808 |
// if objects are requested then request the Java attributes too |
|
1809 |
// so that the objects can be constructed |
|
1810 |
if (cons.getReturningObjFlag()) { |
|
1811 |
if (reqAttrs != null) { |
|
1812 |
||
1813 |
// check for presence of "*" (user attributes wildcard) |
|
1814 |
boolean hasWildcard = false; |
|
1815 |
for (int i = reqAttrs.length - 1; i >= 0; i--) { |
|
1816 |
if (reqAttrs[i].equals("*")) { |
|
1817 |
hasWildcard = true; |
|
1818 |
break; |
|
1819 |
} |
|
1820 |
} |
|
1821 |
if (! hasWildcard) { |
|
1822 |
String[] totalAttrs = |
|
1823 |
new String[reqAttrs.length +Obj.JAVA_ATTRIBUTES.length]; |
|
1824 |
System.arraycopy(reqAttrs, 0, totalAttrs, 0, |
|
1825 |
reqAttrs.length); |
|
1826 |
System.arraycopy(Obj.JAVA_ATTRIBUTES, 0, totalAttrs, |
|
1827 |
reqAttrs.length, Obj.JAVA_ATTRIBUTES.length); |
|
1828 |
||
1829 |
cons.setReturningAttributes(totalAttrs); |
|
1830 |
} |
|
1831 |
} |
|
1832 |
} |
|
1833 |
||
1834 |
LdapCtx.SearchArgs args = |
|
1835 |
new LdapCtx.SearchArgs(name, filter, cons, reqAttrs); |
|
1836 |
||
1837 |
cont.setError(this, name); |
|
1838 |
try { |
|
1839 |
// see if this can be done as a compare, otherwise do a search |
|
1840 |
if (searchToCompare(filter, cons, tokens)){ |
|
1841 |
//System.err.println("compare triggered"); |
|
1842 |
answer = compare(name, tokens[0], tokens[1]); |
|
1843 |
if (! (answer.compareToSearchResult(fullyQualifiedName(name)))){ |
|
1844 |
processReturnCode(answer, name); |
|
1845 |
} |
|
1846 |
} else { |
|
1847 |
answer = doSearch(name, filter, cons, relative, waitForReply); |
|
1848 |
// search result may contain referrals |
|
1849 |
processReturnCode(answer, name); |
|
1850 |
} |
|
1851 |
return new LdapSearchEnumeration(this, answer, |
|
1852 |
fullyQualifiedName(name), args, cont); |
|
1853 |
||
1854 |
} catch (LdapReferralException e) { |
|
1855 |
if (handleReferrals == LdapClient.LDAP_REF_THROW) |
|
1856 |
throw cont.fillInException(e); |
|
1857 |
||
1858 |
// process the referrals sequentially |
|
1859 |
while (true) { |
|
1860 |
||
1861 |
LdapReferralContext refCtx = |
|
1862 |
(LdapReferralContext)e.getReferralContext(envprops, bindCtls); |
|
1863 |
||
1864 |
// repeat the original operation at the new context |
|
1865 |
try { |
|
1866 |
||
1867 |
return refCtx.search(name, filter, cons); |
|
1868 |
||
1869 |
} catch (LdapReferralException re) { |
|
1870 |
e = re; |
|
1871 |
continue; |
|
1872 |
||
1873 |
} finally { |
|
1874 |
// Make sure we close referral context |
|
1875 |
refCtx.close(); |
|
1876 |
} |
|
1877 |
} |
|
1878 |
||
1879 |
} catch (LimitExceededException e) { |
|
1880 |
LdapSearchEnumeration res = |
|
1881 |
new LdapSearchEnumeration(this, answer, fullyQualifiedName(name), |
|
1882 |
args, cont); |
|
1883 |
res.setNamingException(e); |
|
1884 |
return res; |
|
1885 |
||
1886 |
} catch (PartialResultException e) { |
|
1887 |
LdapSearchEnumeration res = |
|
1888 |
new LdapSearchEnumeration(this, answer, fullyQualifiedName(name), |
|
1889 |
args, cont); |
|
1890 |
||
1891 |
res.setNamingException(e); |
|
1892 |
return res; |
|
1893 |
||
1894 |
} catch (IOException e) { |
|
1895 |
NamingException e2 = new CommunicationException(e.getMessage()); |
|
1896 |
e2.setRootCause(e); |
|
1897 |
throw cont.fillInException(e2); |
|
1898 |
||
1899 |
} catch (NamingException e) { |
|
1900 |
throw cont.fillInException(e); |
|
1901 |
} |
|
1902 |
} |
|
1903 |
||
1904 |
||
1905 |
LdapResult getSearchReply(LdapClient eClnt, LdapResult res) |
|
1906 |
throws NamingException { |
|
1907 |
// ensureOpen() won't work here because |
|
1908 |
// session was associated with previous connection |
|
1909 |
||
1910 |
// %%% RL: we can actually allow the enumeration to continue |
|
1911 |
// using the old handle but other weird things might happen |
|
1912 |
// when we hit a referral |
|
1913 |
if (clnt != eClnt) { |
|
1914 |
throw new CommunicationException( |
|
1915 |
"Context's connection changed; unable to continue enumeration"); |
|
1916 |
} |
|
1917 |
||
1918 |
try { |
|
1919 |
return eClnt.getSearchReply(batchSize, res, binaryAttrs); |
|
1920 |
} catch (IOException e) { |
|
1921 |
NamingException e2 = new CommunicationException(e.getMessage()); |
|
1922 |
e2.setRootCause(e); |
|
1923 |
throw e2; |
|
1924 |
} |
|
1925 |
} |
|
1926 |
||
1927 |
// Perform a search. Expect 1 SearchResultEntry and the SearchResultDone. |
|
1928 |
private LdapResult doSearchOnce(Name name, String filter, |
|
1929 |
SearchControls cons, boolean relative) throws NamingException { |
|
1930 |
||
1931 |
int savedBatchSize = batchSize; |
|
1932 |
batchSize = 2; // 2 protocol elements |
|
1933 |
||
1934 |
LdapResult answer = doSearch(name, filter, cons, relative, true); |
|
1935 |
||
1936 |
batchSize = savedBatchSize; |
|
1937 |
return answer; |
|
1938 |
} |
|
1939 |
||
1940 |
private LdapResult doSearch(Name name, String filter, SearchControls cons, |
|
8564
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
1941 |
boolean relative, boolean waitForReply) throws NamingException { |
2 | 1942 |
ensureOpen(); |
1943 |
try { |
|
1944 |
int scope; |
|
1945 |
||
1946 |
switch (cons.getSearchScope()) { |
|
1947 |
case SearchControls.OBJECT_SCOPE: |
|
1948 |
scope = LdapClient.SCOPE_BASE_OBJECT; |
|
1949 |
break; |
|
1950 |
default: |
|
1951 |
case SearchControls.ONELEVEL_SCOPE: |
|
1952 |
scope = LdapClient.SCOPE_ONE_LEVEL; |
|
1953 |
break; |
|
1954 |
case SearchControls.SUBTREE_SCOPE: |
|
1955 |
scope = LdapClient.SCOPE_SUBTREE; |
|
1956 |
break; |
|
1957 |
} |
|
1958 |
||
1959 |
// If cons.getReturningObjFlag() then caller should already |
|
1960 |
// have make sure to request the appropriate attrs |
|
1961 |
||
1962 |
String[] retattrs = cons.getReturningAttributes(); |
|
1963 |
if (retattrs != null && retattrs.length == 0) { |
|
1964 |
// Ldap treats null and empty array the same |
|
1965 |
// need to replace with single element array |
|
1966 |
retattrs = new String[1]; |
|
1967 |
retattrs[0] = "1.1"; |
|
1968 |
} |
|
1969 |
||
1970 |
String nm = (relative |
|
1971 |
? fullyQualifiedName(name) |
|
1972 |
: (name.isEmpty() |
|
1973 |
? "" |
|
1974 |
: name.get(0))); |
|
1975 |
||
1976 |
// JNDI unit is milliseconds, LDAP unit is seconds. |
|
1977 |
// Zero means no limit. |
|
1978 |
int msecLimit = cons.getTimeLimit(); |
|
1979 |
int secLimit = 0; |
|
1980 |
||
1981 |
if (msecLimit > 0) { |
|
1982 |
secLimit = (msecLimit / 1000) + 1; |
|
1983 |
} |
|
1984 |
||
1985 |
LdapResult answer = |
|
1986 |
clnt.search(nm, |
|
1987 |
scope, |
|
1988 |
derefAliases, |
|
1989 |
(int)cons.getCountLimit(), |
|
1990 |
secLimit, |
|
1991 |
cons.getReturningObjFlag() ? false : typesOnly, |
|
1992 |
retattrs, |
|
1993 |
filter, |
|
1994 |
batchSize, |
|
1995 |
reqCtls, |
|
1996 |
binaryAttrs, |
|
8564
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
1997 |
waitForReply, |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
1998 |
replyQueueSize); |
2 | 1999 |
respCtls = answer.resControls; // retrieve response controls |
2000 |
return answer; |
|
2001 |
||
2002 |
} catch (IOException e) { |
|
2003 |
NamingException e2 = new CommunicationException(e.getMessage()); |
|
2004 |
e2.setRootCause(e); |
|
2005 |
throw e2; |
|
2006 |
} |
|
2007 |
} |
|
2008 |
||
2009 |
||
2010 |
/* |
|
2011 |
* Certain simple JNDI searches are automatically converted to |
|
2012 |
* LDAP compare operations by the LDAP service provider. A search |
|
2013 |
* is converted to a compare iff: |
|
2014 |
* |
|
2015 |
* - the scope is set to OBJECT_SCOPE |
|
2016 |
* - the filter string contains a simple assertion: "<type>=<value>" |
|
2017 |
* - the returning attributes list is present but empty |
|
2018 |
*/ |
|
2019 |
||
2020 |
// returns true if a search can be caried out as a compare, and sets |
|
2021 |
// tokens[0] and tokens[1] to the type and value respectively. |
|
2022 |
// e.g. filter "cn=Jon Ruiz" becomes, type "cn" and value "Jon Ruiz" |
|
2023 |
// This function uses the documents JNDI Compare example as a model |
|
2024 |
// for when to turn a search into a compare. |
|
2025 |
||
2026 |
private static boolean searchToCompare( |
|
2027 |
String filter, |
|
2028 |
SearchControls cons, |
|
2029 |
String tokens[]) { |
|
2030 |
||
2031 |
// if scope is not object-scope, it's really a search |
|
2032 |
if (cons.getSearchScope() != SearchControls.OBJECT_SCOPE) { |
|
2033 |
return false; |
|
2034 |
} |
|
2035 |
||
2036 |
// if attributes are to be returned, it's really a search |
|
2037 |
String[] attrs = cons.getReturningAttributes(); |
|
2038 |
if (attrs == null || attrs.length != 0) { |
|
2039 |
return false; |
|
2040 |
} |
|
2041 |
||
2042 |
// if the filter not a simple assertion, it's really a search |
|
2043 |
if (! filterToAssertion(filter, tokens)) { |
|
2044 |
return false; |
|
2045 |
} |
|
2046 |
||
2047 |
// it can be converted to a compare |
|
2048 |
return true; |
|
2049 |
} |
|
2050 |
||
2051 |
// If the supplied filter is a simple assertion i.e. "<type>=<value>" |
|
2052 |
// (enclosing parentheses are permitted) then |
|
2053 |
// filterToAssertion will return true and pass the type and value as |
|
2054 |
// the first and second elements of tokens respectively. |
|
2055 |
// precondition: tokens[] must be initialized and be at least of size 2. |
|
2056 |
||
2057 |
private static boolean filterToAssertion(String filter, String tokens[]) { |
|
2058 |
||
2059 |
// find the left and right half of the assertion |
|
2060 |
StringTokenizer assertionTokenizer = new StringTokenizer(filter, "="); |
|
2061 |
||
2062 |
if (assertionTokenizer.countTokens() != 2) { |
|
2063 |
return false; |
|
2064 |
} |
|
2065 |
||
2066 |
tokens[0] = assertionTokenizer.nextToken(); |
|
2067 |
tokens[1] = assertionTokenizer.nextToken(); |
|
2068 |
||
2069 |
// make sure the value does not contain a wildcard |
|
2070 |
if (tokens[1].indexOf('*') != -1) { |
|
2071 |
return false; |
|
2072 |
} |
|
2073 |
||
2074 |
// test for enclosing parenthesis |
|
2075 |
boolean hasParens = false; |
|
2076 |
int len = tokens[1].length(); |
|
2077 |
||
2078 |
if ((tokens[0].charAt(0) == '(') && |
|
2079 |
(tokens[1].charAt(len - 1) == ')')) { |
|
2080 |
hasParens = true; |
|
2081 |
||
2082 |
} else if ((tokens[0].charAt(0) == '(') || |
|
2083 |
(tokens[1].charAt(len - 1) == ')')) { |
|
2084 |
return false; // unbalanced |
|
2085 |
} |
|
2086 |
||
2087 |
// make sure the left and right half are not expresions themselves |
|
2088 |
StringTokenizer illegalCharsTokenizer = |
|
2089 |
new StringTokenizer(tokens[0], "()&|!=~><*", true); |
|
2090 |
||
2091 |
if (illegalCharsTokenizer.countTokens() != (hasParens ? 2 : 1)) { |
|
2092 |
return false; |
|
2093 |
} |
|
2094 |
||
2095 |
illegalCharsTokenizer = |
|
2096 |
new StringTokenizer(tokens[1], "()&|!=~><*", true); |
|
2097 |
||
2098 |
if (illegalCharsTokenizer.countTokens() != (hasParens ? 2 : 1)) { |
|
2099 |
return false; |
|
2100 |
} |
|
2101 |
||
2102 |
// strip off enclosing parenthesis, if present |
|
2103 |
if (hasParens) { |
|
2104 |
tokens[0] = tokens[0].substring(1); |
|
2105 |
tokens[1] = tokens[1].substring(0, len - 1); |
|
2106 |
} |
|
2107 |
||
2108 |
return true; |
|
2109 |
} |
|
2110 |
||
2111 |
private LdapResult compare(Name name, String type, String value) |
|
2112 |
throws IOException, NamingException { |
|
2113 |
||
2114 |
ensureOpen(); |
|
2115 |
String nm = fullyQualifiedName(name); |
|
2116 |
||
2117 |
LdapResult answer = clnt.compare(nm, type, value, reqCtls); |
|
2118 |
respCtls = answer.resControls; // retrieve response controls |
|
2119 |
||
2120 |
return answer; |
|
2121 |
} |
|
2122 |
||
2123 |
private static SearchControls cloneSearchControls(SearchControls cons) { |
|
2124 |
if (cons == null) { |
|
2125 |
return null; |
|
2126 |
} |
|
2127 |
String[] retAttrs = cons.getReturningAttributes(); |
|
2128 |
if (retAttrs != null) { |
|
2129 |
String[] attrs = new String[retAttrs.length]; |
|
2130 |
System.arraycopy(retAttrs, 0, attrs, 0, retAttrs.length); |
|
2131 |
retAttrs = attrs; |
|
2132 |
} |
|
2133 |
return new SearchControls(cons.getSearchScope(), |
|
2134 |
cons.getCountLimit(), |
|
2135 |
cons.getTimeLimit(), |
|
2136 |
retAttrs, |
|
2137 |
cons.getReturningObjFlag(), |
|
2138 |
cons.getDerefLinkFlag()); |
|
2139 |
} |
|
2140 |
||
2141 |
// -------------- Environment Properties ------------------ |
|
2142 |
||
2143 |
/** |
|
2144 |
* Override with noncloning version. |
|
2145 |
*/ |
|
2146 |
protected Hashtable p_getEnvironment() { |
|
2147 |
return envprops; |
|
2148 |
} |
|
2149 |
||
2150 |
public Hashtable getEnvironment() throws NamingException { |
|
2151 |
return (envprops == null |
|
2152 |
? new Hashtable(5, 0.75f) |
|
2153 |
: (Hashtable)envprops.clone()); |
|
2154 |
} |
|
2155 |
||
2156 |
public Object removeFromEnvironment(String propName) |
|
2157 |
throws NamingException { |
|
2158 |
||
2159 |
// not there; just return |
|
2160 |
if (envprops == null || envprops.get(propName) == null) { |
|
2161 |
return null; |
|
2162 |
} |
|
2163 |
||
2164 |
if (propName.equals(REF_SEPARATOR)) { |
|
2165 |
addrEncodingSeparator = DEFAULT_REF_SEPARATOR; |
|
2166 |
} else if (propName.equals(TYPES_ONLY)) { |
|
2167 |
typesOnly = DEFAULT_TYPES_ONLY; |
|
2168 |
} else if (propName.equals(DELETE_RDN)) { |
|
2169 |
deleteRDN = DEFAULT_DELETE_RDN; |
|
2170 |
} else if (propName.equals(DEREF_ALIASES)) { |
|
2171 |
derefAliases = DEFAULT_DEREF_ALIASES; |
|
2172 |
} else if (propName.equals(Context.BATCHSIZE)) { |
|
2173 |
batchSize = DEFAULT_BATCH_SIZE; |
|
2174 |
} else if (propName.equals(REFERRAL_LIMIT)) { |
|
2175 |
referralHopLimit = DEFAULT_REFERRAL_LIMIT; |
|
2176 |
} else if (propName.equals(Context.REFERRAL)) { |
|
2177 |
setReferralMode(null, true); |
|
2178 |
} else if (propName.equals(BINARY_ATTRIBUTES)) { |
|
2179 |
setBinaryAttributes(null); |
|
2180 |
} else if (propName.equals(CONNECT_TIMEOUT)) { |
|
2181 |
connectTimeout = -1; |
|
2182 |
} else if (propName.equals(READ_TIMEOUT)) { |
|
2183 |
readTimeout = -1; |
|
8564
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2184 |
} else if (propName.equals(WAIT_FOR_REPLY)) { |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2185 |
waitForReply = true; |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2186 |
} else if (propName.equals(REPLY_QUEUE_SIZE)) { |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2187 |
replyQueueSize = -1; |
2 | 2188 |
|
2189 |
// The following properties affect the connection |
|
2190 |
||
2191 |
} else if (propName.equals(Context.SECURITY_PROTOCOL)) { |
|
2192 |
closeConnection(SOFT_CLOSE); |
|
2193 |
// De-activate SSL and reset the context's url and port number |
|
2194 |
if (useSsl && !hasLdapsScheme) { |
|
2195 |
useSsl = false; |
|
2196 |
url = null; |
|
2197 |
if (useDefaultPortNumber) { |
|
2198 |
port_number = DEFAULT_PORT; |
|
2199 |
} |
|
2200 |
} |
|
2201 |
} else if (propName.equals(VERSION) || |
|
2202 |
propName.equals(SOCKET_FACTORY)) { |
|
2203 |
closeConnection(SOFT_CLOSE); |
|
2204 |
} else if(propName.equals(Context.SECURITY_AUTHENTICATION) || |
|
2205 |
propName.equals(Context.SECURITY_PRINCIPAL) || |
|
2206 |
propName.equals(Context.SECURITY_CREDENTIALS)) { |
|
2207 |
sharable = false; |
|
2208 |
} |
|
2209 |
||
2210 |
// Update environment; reconnection will use new props |
|
2211 |
envprops = (Hashtable)envprops.clone(); |
|
2212 |
return envprops.remove(propName); |
|
2213 |
} |
|
2214 |
||
2215 |
public Object addToEnvironment(String propName, Object propVal) |
|
2216 |
throws NamingException { |
|
2217 |
||
2218 |
// If adding null, call remove |
|
2219 |
if (propVal == null) { |
|
2220 |
return removeFromEnvironment(propName); |
|
2221 |
} |
|
2222 |
||
2223 |
if (propName.equals(REF_SEPARATOR)) { |
|
2224 |
setRefSeparator((String)propVal); |
|
2225 |
} else if (propName.equals(TYPES_ONLY)) { |
|
2226 |
setTypesOnly((String)propVal); |
|
2227 |
} else if (propName.equals(DELETE_RDN)) { |
|
2228 |
setDeleteRDN((String)propVal); |
|
2229 |
} else if (propName.equals(DEREF_ALIASES)) { |
|
2230 |
setDerefAliases((String)propVal); |
|
2231 |
} else if (propName.equals(Context.BATCHSIZE)) { |
|
2232 |
setBatchSize((String)propVal); |
|
2233 |
} else if (propName.equals(REFERRAL_LIMIT)) { |
|
2234 |
setReferralLimit((String)propVal); |
|
2235 |
} else if (propName.equals(Context.REFERRAL)) { |
|
2236 |
setReferralMode((String)propVal, true); |
|
2237 |
} else if (propName.equals(BINARY_ATTRIBUTES)) { |
|
2238 |
setBinaryAttributes((String)propVal); |
|
2239 |
} else if (propName.equals(CONNECT_TIMEOUT)) { |
|
2240 |
setConnectTimeout((String)propVal); |
|
2241 |
} else if (propName.equals(READ_TIMEOUT)) { |
|
2242 |
setReadTimeout((String)propVal); |
|
8564
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2243 |
} else if (propName.equals(WAIT_FOR_REPLY)) { |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2244 |
setWaitForReply((String)propVal); |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2245 |
} else if (propName.equals(REPLY_QUEUE_SIZE)) { |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2246 |
setReplyQueueSize((String)propVal); |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2247 |
|
2 | 2248 |
// The following properties affect the connection |
2249 |
||
2250 |
} else if (propName.equals(Context.SECURITY_PROTOCOL)) { |
|
2251 |
closeConnection(SOFT_CLOSE); |
|
2252 |
// Activate SSL and reset the context's url and port number |
|
2253 |
if ("ssl".equals(propVal)) { |
|
2254 |
useSsl = true; |
|
2255 |
url = null; |
|
2256 |
if (useDefaultPortNumber) { |
|
2257 |
port_number = DEFAULT_SSL_PORT; |
|
2258 |
} |
|
2259 |
} |
|
2260 |
} else if (propName.equals(VERSION) || |
|
2261 |
propName.equals(SOCKET_FACTORY)) { |
|
2262 |
closeConnection(SOFT_CLOSE); |
|
2263 |
} else if (propName.equals(Context.SECURITY_AUTHENTICATION) || |
|
2264 |
propName.equals(Context.SECURITY_PRINCIPAL) || |
|
2265 |
propName.equals(Context.SECURITY_CREDENTIALS)) { |
|
2266 |
sharable = false; |
|
2267 |
} |
|
2268 |
||
2269 |
// Update environment; reconnection will use new props |
|
2270 |
envprops = (envprops == null |
|
2271 |
? new Hashtable(5, 0.75f) |
|
2272 |
: (Hashtable)envprops.clone()); |
|
2273 |
return envprops.put(propName, propVal); |
|
2274 |
} |
|
2275 |
||
2276 |
/** |
|
2277 |
* Sets the URL that created the context in the java.naming.provider.url |
|
2278 |
* property. |
|
2279 |
*/ |
|
2280 |
void setProviderUrl(String providerUrl) { // called by LdapCtxFactory |
|
2281 |
if (envprops != null) { |
|
2282 |
envprops.put(Context.PROVIDER_URL, providerUrl); |
|
2283 |
} |
|
2284 |
} |
|
2285 |
||
2286 |
/** |
|
2287 |
* Sets the domain name for the context in the com.sun.jndi.ldap.domainname |
|
2288 |
* property. |
|
2289 |
* Used for hostname verification by Start TLS |
|
2290 |
*/ |
|
2291 |
void setDomainName(String domainName) { // called by LdapCtxFactory |
|
2292 |
if (envprops != null) { |
|
2293 |
envprops.put(DOMAIN_NAME, domainName); |
|
2294 |
} |
|
2295 |
} |
|
2296 |
||
2297 |
private void initEnv() throws NamingException { |
|
2298 |
if (envprops == null) { |
|
2299 |
// Make sure that referrals are to their default |
|
2300 |
setReferralMode(null, false); |
|
2301 |
return; |
|
2302 |
} |
|
2303 |
||
2304 |
// Set batch size |
|
2305 |
setBatchSize((String)envprops.get(Context.BATCHSIZE)); |
|
2306 |
||
2307 |
// Set separator used for encoding RefAddr |
|
2308 |
setRefSeparator((String)envprops.get(REF_SEPARATOR)); |
|
2309 |
||
2310 |
// Set whether RDN is removed when renaming object |
|
2311 |
setDeleteRDN((String)envprops.get(DELETE_RDN)); |
|
2312 |
||
2313 |
// Set whether types are returned only |
|
2314 |
setTypesOnly((String)envprops.get(TYPES_ONLY)); |
|
2315 |
||
2316 |
// Set how aliases are dereferenced |
|
2317 |
setDerefAliases((String)envprops.get(DEREF_ALIASES)); |
|
2318 |
||
2319 |
// Set the limit on referral chains |
|
2320 |
setReferralLimit((String)envprops.get(REFERRAL_LIMIT)); |
|
2321 |
||
2322 |
setBinaryAttributes((String)envprops.get(BINARY_ATTRIBUTES)); |
|
2323 |
||
2324 |
bindCtls = cloneControls((Control[]) envprops.get(BIND_CONTROLS)); |
|
2325 |
||
2326 |
// set referral handling |
|
2327 |
setReferralMode((String)envprops.get(Context.REFERRAL), false); |
|
2328 |
||
2329 |
// Set the connect timeout |
|
2330 |
setConnectTimeout((String)envprops.get(CONNECT_TIMEOUT)); |
|
2331 |
||
2332 |
// Set the read timeout |
|
2333 |
setReadTimeout((String)envprops.get(READ_TIMEOUT)); |
|
2334 |
||
8564
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2335 |
// Set the flag that controls whether to block until the first reply |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2336 |
// is received |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2337 |
setWaitForReply((String)envprops.get(WAIT_FOR_REPLY)); |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2338 |
|
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2339 |
// Set the size of the queue of unprocessed search replies |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2340 |
setReplyQueueSize((String)envprops.get(REPLY_QUEUE_SIZE)); |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2341 |
|
2 | 2342 |
// When connection is created, it will use these and other |
2343 |
// properties from the environment |
|
2344 |
} |
|
2345 |
||
2346 |
private void setDeleteRDN(String deleteRDNProp) { |
|
2347 |
if ((deleteRDNProp != null) && |
|
2348 |
(deleteRDNProp.equalsIgnoreCase("false"))) { |
|
2349 |
deleteRDN = false; |
|
2350 |
} else { |
|
2351 |
deleteRDN = DEFAULT_DELETE_RDN; |
|
2352 |
} |
|
2353 |
} |
|
2354 |
||
2355 |
private void setTypesOnly(String typesOnlyProp) { |
|
2356 |
if ((typesOnlyProp != null) && |
|
2357 |
(typesOnlyProp.equalsIgnoreCase("true"))) { |
|
2358 |
typesOnly = true; |
|
2359 |
} else { |
|
2360 |
typesOnly = DEFAULT_TYPES_ONLY; |
|
2361 |
} |
|
2362 |
} |
|
2363 |
||
2364 |
/** |
|
2365 |
* Sets the batch size of this context; |
|
2366 |
*/ |
|
2367 |
private void setBatchSize(String batchSizeProp) { |
|
2368 |
// set batchsize |
|
2369 |
if (batchSizeProp != null) { |
|
2370 |
batchSize = Integer.parseInt(batchSizeProp); |
|
2371 |
} else { |
|
2372 |
batchSize = DEFAULT_BATCH_SIZE; |
|
2373 |
} |
|
2374 |
} |
|
2375 |
||
2376 |
/** |
|
2377 |
* Sets the referral mode of this context to 'follow', 'throw' or 'ignore'. |
|
2378 |
* If referral mode is 'ignore' then activate the manageReferral control. |
|
2379 |
*/ |
|
2380 |
private void setReferralMode(String ref, boolean update) { |
|
2381 |
// First determine the referral mode |
|
2382 |
if (ref != null) { |
|
2383 |
if (ref.equals("follow")) { |
|
2384 |
handleReferrals = LdapClient.LDAP_REF_FOLLOW; |
|
2385 |
} else if (ref.equals("throw")) { |
|
2386 |
handleReferrals = LdapClient.LDAP_REF_THROW; |
|
2387 |
} else if (ref.equals("ignore")) { |
|
2388 |
handleReferrals = LdapClient.LDAP_REF_IGNORE; |
|
2389 |
} else { |
|
2390 |
throw new IllegalArgumentException( |
|
2391 |
"Illegal value for " + Context.REFERRAL + " property."); |
|
2392 |
} |
|
2393 |
} else { |
|
2394 |
handleReferrals = DEFAULT_REFERRAL_MODE; |
|
2395 |
} |
|
2396 |
||
2397 |
if (handleReferrals == LdapClient.LDAP_REF_IGNORE) { |
|
2398 |
// If ignoring referrals, add manageReferralControl |
|
2399 |
reqCtls = addControl(reqCtls, manageReferralControl); |
|
2400 |
||
2401 |
} else if (update) { |
|
2402 |
||
2403 |
// If we're update an existing context, remove the control |
|
2404 |
reqCtls = removeControl(reqCtls, manageReferralControl); |
|
2405 |
||
2406 |
} // else, leave alone; need not update |
|
2407 |
} |
|
2408 |
||
2409 |
/** |
|
2410 |
* Set whether aliases are derefereced during resolution and searches. |
|
2411 |
*/ |
|
2412 |
private void setDerefAliases(String deref) { |
|
2413 |
if (deref != null) { |
|
2414 |
if (deref.equals("never")) { |
|
2415 |
derefAliases = 0; // never de-reference aliases |
|
2416 |
} else if (deref.equals("searching")) { |
|
2417 |
derefAliases = 1; // de-reference aliases during searching |
|
2418 |
} else if (deref.equals("finding")) { |
|
2419 |
derefAliases = 2; // de-reference during name resolution |
|
2420 |
} else if (deref.equals("always")) { |
|
2421 |
derefAliases = 3; // always de-reference aliases |
|
2422 |
} else { |
|
2423 |
throw new IllegalArgumentException("Illegal value for " + |
|
2424 |
DEREF_ALIASES + " property."); |
|
2425 |
} |
|
2426 |
} else { |
|
2427 |
derefAliases = DEFAULT_DEREF_ALIASES; |
|
2428 |
} |
|
2429 |
} |
|
2430 |
||
2431 |
private void setRefSeparator(String sepStr) throws NamingException { |
|
2432 |
if (sepStr != null && sepStr.length() > 0) { |
|
2433 |
addrEncodingSeparator = sepStr.charAt(0); |
|
2434 |
} else { |
|
2435 |
addrEncodingSeparator = DEFAULT_REF_SEPARATOR; |
|
2436 |
} |
|
2437 |
} |
|
2438 |
||
2439 |
/** |
|
2440 |
* Sets the limit on referral chains |
|
2441 |
*/ |
|
2442 |
private void setReferralLimit(String referralLimitProp) { |
|
2443 |
// set referral limit |
|
2444 |
if (referralLimitProp != null) { |
|
2445 |
referralHopLimit = Integer.parseInt(referralLimitProp); |
|
2446 |
||
2447 |
// a zero setting indicates no limit |
|
2448 |
if (referralHopLimit == 0) |
|
2449 |
referralHopLimit = Integer.MAX_VALUE; |
|
2450 |
} else { |
|
2451 |
referralHopLimit = DEFAULT_REFERRAL_LIMIT; |
|
2452 |
} |
|
2453 |
} |
|
2454 |
||
2455 |
// For counting referral hops |
|
2456 |
void setHopCount(int hopCount) { |
|
2457 |
this.hopCount = hopCount; |
|
2458 |
} |
|
2459 |
||
2460 |
/** |
|
2461 |
* Sets the connect timeout value |
|
2462 |
*/ |
|
2463 |
private void setConnectTimeout(String connectTimeoutProp) { |
|
2464 |
if (connectTimeoutProp != null) { |
|
2465 |
connectTimeout = Integer.parseInt(connectTimeoutProp); |
|
2466 |
} else { |
|
2467 |
connectTimeout = -1; |
|
2468 |
} |
|
2469 |
} |
|
2470 |
||
2471 |
/** |
|
8564
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2472 |
* Sets the size of the queue of unprocessed search replies |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2473 |
*/ |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2474 |
private void setReplyQueueSize(String replyQueueSizeProp) { |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2475 |
if (replyQueueSizeProp != null) { |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2476 |
replyQueueSize = Integer.parseInt(replyQueueSizeProp); |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2477 |
// disallow an empty queue |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2478 |
if (replyQueueSize <= 0) { |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2479 |
replyQueueSize = -1; // unlimited |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2480 |
} |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2481 |
} else { |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2482 |
replyQueueSize = -1; // unlimited |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2483 |
} |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2484 |
} |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2485 |
|
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2486 |
/** |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2487 |
* Sets the flag that controls whether to block until the first search |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2488 |
* reply is received |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2489 |
*/ |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2490 |
private void setWaitForReply(String waitForReplyProp) { |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2491 |
if (waitForReplyProp != null && |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2492 |
(waitForReplyProp.equalsIgnoreCase("false"))) { |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2493 |
waitForReply = false; |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2494 |
} else { |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2495 |
waitForReply = true; |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2496 |
} |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2497 |
} |
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2498 |
|
d99f879a35ab
6750362: Very large LDAP requests throw a OOM on LDAP servers which aren't aware of Paged Results Controls
coffeys
parents:
5506
diff
changeset
|
2499 |
/** |
2 | 2500 |
* Sets the read timeout value |
2501 |
*/ |
|
2502 |
private void setReadTimeout(String readTimeoutProp) { |
|
2503 |
if (readTimeoutProp != null) { |
|
2504 |
readTimeout = Integer.parseInt(readTimeoutProp); |
|
2505 |
} else { |
|
2506 |
readTimeout = -1; |
|
2507 |
} |
|
2508 |
} |
|
2509 |
||
2510 |
/* |
|
2511 |
* Extract URLs from a string. The format of the string is: |
|
2512 |
* |
|
2513 |
* <urlstring > ::= "Referral:" <ldapurls> |
|
2514 |
* <ldapurls> ::= <separator> <ldapurl> | <ldapurls> |
|
2515 |
* <separator> ::= ASCII linefeed character (0x0a) |
|
2516 |
* <ldapurl> ::= LDAP URL format (RFC 1959) |
|
2517 |
*/ |
|
2518 |
private static Vector extractURLs(String refString) { |
|
2519 |
||
2520 |
int separator = 0; |
|
2521 |
int urlCount = 0; |
|
2522 |
||
2523 |
// count the number of URLs |
|
2524 |
while ((separator = refString.indexOf('\n', separator)) >= 0) { |
|
2525 |
separator++; |
|
2526 |
urlCount++; |
|
2527 |
} |
|
2528 |
||
2529 |
Vector referrals = new Vector(urlCount); |
|
2530 |
int iURL; |
|
2531 |
int i = 0; |
|
2532 |
||
2533 |
separator = refString.indexOf('\n'); |
|
2534 |
iURL = separator + 1; |
|
2535 |
while ((separator = refString.indexOf('\n', iURL)) >= 0) { |
|
2536 |
referrals.addElement(refString.substring(iURL, separator)); |
|
2537 |
iURL = separator + 1; |
|
2538 |
} |
|
2539 |
referrals.addElement(refString.substring(iURL)); |
|
2540 |
||
2541 |
return referrals; |
|
2542 |
} |
|
2543 |
||
2544 |
/* |
|
2545 |
* Argument is a space-separated list of attribute IDs |
|
2546 |
* Converts attribute IDs to lowercase before adding to built-in list. |
|
2547 |
*/ |
|
2548 |
private void setBinaryAttributes(String attrIds) { |
|
2549 |
if (attrIds == null) { |
|
2550 |
binaryAttrs = null; |
|
2551 |
} else { |
|
2552 |
binaryAttrs = new Hashtable(11, 0.75f); |
|
2553 |
StringTokenizer tokens = |
|
2554 |
new StringTokenizer(attrIds.toLowerCase(), " "); |
|
2555 |
||
2556 |
while (tokens.hasMoreTokens()) { |
|
2557 |
binaryAttrs.put(tokens.nextToken(), Boolean.TRUE); |
|
2558 |
} |
|
2559 |
} |
|
2560 |
} |
|
2561 |
||
2562 |
// ----------------- Connection --------------------- |
|
2563 |
||
2564 |
protected void finalize() { |
|
2565 |
try { |
|
2566 |
close(); |
|
2567 |
} catch (NamingException e) { |
|
2568 |
// ignore failures |
|
2569 |
} |
|
2570 |
} |
|
2571 |
||
2572 |
synchronized public void close() throws NamingException { |
|
2573 |
if (debug) { |
|
2574 |
System.err.println("LdapCtx: close() called " + this); |
|
2575 |
(new Throwable()).printStackTrace(); |
|
2576 |
} |
|
2577 |
||
2578 |
// Event (normal and unsolicited) |
|
2579 |
if (eventSupport != null) { |
|
2580 |
eventSupport.cleanup(); // idempotent |
|
2581 |
removeUnsolicited(); |
|
2582 |
} |
|
2583 |
||
2584 |
// Enumerations that are keeping the connection alive |
|
2585 |
if (enumCount > 0) { |
|
2586 |
if (debug) |
|
2587 |
System.err.println("LdapCtx: close deferred"); |
|
2588 |
closeRequested = true; |
|
2589 |
return; |
|
2590 |
} |
|
2591 |
closeConnection(SOFT_CLOSE); |
|
2592 |
||
2593 |
// %%%: RL: There is no need to set these to null, as they're just |
|
2594 |
// variables whose contents and references will automatically |
|
2595 |
// be cleaned up when they're no longer referenced. |
|
2596 |
// Also, setting these to null creates problems for the attribute |
|
2597 |
// schema-related methods, which need these to work. |
|
2598 |
/* |
|
2599 |
schemaTrees = null; |
|
2600 |
envprops = null; |
|
2601 |
*/ |
|
2602 |
} |
|
2603 |
||
2604 |
public void reconnect(Control[] connCtls) throws NamingException { |
|
2605 |
// Update environment |
|
2606 |
envprops = (envprops == null |
|
2607 |
? new Hashtable(5, 0.75f) |
|
2608 |
: (Hashtable)envprops.clone()); |
|
2609 |
||
2610 |
if (connCtls == null) { |
|
2611 |
envprops.remove(BIND_CONTROLS); |
|
2612 |
bindCtls = null; |
|
2613 |
} else { |
|
2614 |
envprops.put(BIND_CONTROLS, bindCtls = cloneControls(connCtls)); |
|
2615 |
} |
|
2616 |
||
2617 |
sharable = false; // can't share with existing contexts |
|
2618 |
ensureOpen(); // open or reauthenticated |
|
2619 |
} |
|
2620 |
||
2621 |
private void ensureOpen() throws NamingException { |
|
2622 |
ensureOpen(false); |
|
2623 |
} |
|
2624 |
||
2625 |
private void ensureOpen(boolean startTLS) throws NamingException { |
|
2626 |
||
2627 |
try { |
|
2628 |
if (clnt == null) { |
|
2629 |
if (debug) { |
|
2630 |
System.err.println("LdapCtx: Reconnecting " + this); |
|
2631 |
} |
|
2632 |
||
2633 |
// reset the cache before a new connection is established |
|
2634 |
schemaTrees = new Hashtable(11, 0.75f); |
|
2635 |
connect(startTLS); |
|
2636 |
||
2637 |
} else if (!sharable || startTLS) { |
|
2638 |
||
2639 |
synchronized (clnt) { |
|
2640 |
if (!clnt.isLdapv3 |
|
2641 |
|| clnt.referenceCount > 1 |
|
2642 |
|| clnt.usingSaslStreams()) { |
|
2643 |
closeConnection(SOFT_CLOSE); |
|
2644 |
} |
|
2645 |
} |
|
2646 |
// reset the cache before a new connection is established |
|
2647 |
schemaTrees = new Hashtable(11, 0.75f); |
|
2648 |
connect(startTLS); |
|
2649 |
} |
|
2650 |
||
2651 |
} finally { |
|
2652 |
sharable = true; // connection is now either new or single-use |
|
2653 |
// OK for others to start sharing again |
|
2654 |
} |
|
2655 |
} |
|
2656 |
||
2657 |
private void connect(boolean startTLS) throws NamingException { |
|
2658 |
if (debug) { System.err.println("LdapCtx: Connecting " + this); } |
|
2659 |
||
2660 |
String user = null; // authenticating user |
|
2661 |
Object passwd = null; // password for authenticating user |
|
2662 |
String secProtocol = null; // security protocol (e.g. "ssl") |
|
2663 |
String socketFactory = null; // socket factory |
|
2664 |
String authMechanism = null; // authentication mechanism |
|
2665 |
String ver = null; |
|
2666 |
int ldapVersion; // LDAP protocol version |
|
2667 |
boolean usePool = false; // enable connection pooling |
|
2668 |
||
2669 |
if (envprops != null) { |
|
2670 |
user = (String)envprops.get(Context.SECURITY_PRINCIPAL); |
|
2671 |
passwd = envprops.get(Context.SECURITY_CREDENTIALS); |
|
2672 |
ver = (String)envprops.get(VERSION); |
|
2673 |
secProtocol = |
|
2674 |
useSsl ? "ssl" : (String)envprops.get(Context.SECURITY_PROTOCOL); |
|
2675 |
socketFactory = (String)envprops.get(SOCKET_FACTORY); |
|
2676 |
authMechanism = |
|
2677 |
(String)envprops.get(Context.SECURITY_AUTHENTICATION); |
|
2678 |
||
2679 |
usePool = "true".equalsIgnoreCase((String)envprops.get(ENABLE_POOL)); |
|
2680 |
} |
|
2681 |
||
2682 |
if (socketFactory == null) { |
|
2683 |
socketFactory = |
|
2684 |
"ssl".equals(secProtocol) ? DEFAULT_SSL_FACTORY : null; |
|
2685 |
} |
|
2686 |
||
2687 |
if (authMechanism == null) { |
|
2688 |
authMechanism = (user == null) ? "none" : "simple"; |
|
2689 |
} |
|
2690 |
||
2691 |
try { |
|
2692 |
boolean initial = (clnt == null); |
|
2693 |
||
2694 |
if (initial) { |
|
2695 |
ldapVersion = (ver != null) ? Integer.parseInt(ver) : |
|
2696 |
DEFAULT_LDAP_VERSION; |
|
2697 |
||
2698 |
clnt = LdapClient.getInstance( |
|
2699 |
usePool, // Whether to use connection pooling |
|
2700 |
||
2701 |
// Required for LdapClient constructor |
|
2702 |
hostname, |
|
2703 |
port_number, |
|
2704 |
socketFactory, |
|
2705 |
connectTimeout, |
|
2706 |
readTimeout, |
|
2707 |
trace, |
|
2708 |
||
2709 |
// Required for basic client identity |
|
2710 |
ldapVersion, |
|
2711 |
authMechanism, |
|
2712 |
bindCtls, |
|
2713 |
secProtocol, |
|
2714 |
||
2715 |
// Required for simple client identity |
|
2716 |
user, |
|
2717 |
passwd, |
|
2718 |
||
2719 |
// Required for SASL client identity |
|
2720 |
envprops); |
|
2721 |
||
2722 |
||
2723 |
/** |
|
2724 |
* Pooled connections are preauthenticated; |
|
2725 |
* newly created ones are not. |
|
2726 |
*/ |
|
2727 |
if (clnt.authenticateCalled()) { |
|
2728 |
return; |
|
2729 |
} |
|
2730 |
||
2731 |
} else if (sharable && startTLS) { |
|
2732 |
return; // no authentication required |
|
2733 |
||
2734 |
} else { |
|
2735 |
// reauthenticating over existing connection; |
|
2736 |
// only v3 supports this |
|
2737 |
ldapVersion = LdapClient.LDAP_VERSION3; |
|
2738 |
} |
|
2739 |
||
2740 |
LdapResult answer = clnt.authenticate(initial, |
|
2741 |
user, passwd, ldapVersion, authMechanism, bindCtls, envprops); |
|
2742 |
||
2743 |
respCtls = answer.resControls; // retrieve (bind) response controls |
|
2744 |
||
2745 |
if (answer.status != LdapClient.LDAP_SUCCESS) { |
|
2746 |
if (initial) { |
|
2747 |
closeConnection(HARD_CLOSE); // hard close |
|
2748 |
} |
|
2749 |
processReturnCode(answer); |
|
2750 |
} |
|
2751 |
||
2752 |
} catch (LdapReferralException e) { |
|
2753 |
if (handleReferrals == LdapClient.LDAP_REF_THROW) |
|
2754 |
throw e; |
|
2755 |
||
2756 |
String referral; |
|
2757 |
LdapURL url; |
|
2758 |
NamingException saved_ex = null; |
|
2759 |
||
2760 |
// Process the referrals sequentially (top level) and |
|
2761 |
// recursively (per referral) |
|
2762 |
while (true) { |
|
2763 |
||
2764 |
if ((referral = e.getNextReferral()) == null) { |
|
2765 |
// No more referrals to follow |
|
2766 |
||
2767 |
if (saved_ex != null) { |
|
2768 |
throw (NamingException)(saved_ex.fillInStackTrace()); |
|
2769 |
} else { |
|
2770 |
// No saved exception, something must have gone wrong |
|
2771 |
throw new NamingException( |
|
2772 |
"Internal error processing referral during connection"); |
|
2773 |
} |
|
2774 |
} |
|
2775 |
||
2776 |
// Use host/port number from referral |
|
2777 |
url = new LdapURL(referral); |
|
2778 |
hostname = url.getHost(); |
|
2779 |
if ((hostname != null) && (hostname.charAt(0) == '[')) { |
|
2780 |
hostname = hostname.substring(1, hostname.length() - 1); |
|
2781 |
} |
|
2782 |
port_number = url.getPort(); |
|
2783 |
||
2784 |
// Try to connect again using new host/port number |
|
2785 |
try { |
|
2786 |
connect(startTLS); |
|
2787 |
break; |
|
2788 |
||
2789 |
} catch (NamingException ne) { |
|
2790 |
saved_ex = ne; |
|
2791 |
continue; // follow another referral |
|
2792 |
} |
|
2793 |
} |
|
2794 |
} |
|
2795 |
} |
|
2796 |
||
2797 |
private void closeConnection(boolean hardclose) { |
|
2798 |
removeUnsolicited(); // idempotent |
|
2799 |
||
2800 |
if (clnt != null) { |
|
2801 |
if (debug) { |
|
2802 |
System.err.println("LdapCtx: calling clnt.close() " + this); |
|
2803 |
} |
|
2804 |
clnt.close(reqCtls, hardclose); |
|
2805 |
clnt = null; |
|
2806 |
} |
|
2807 |
} |
|
2808 |
||
2809 |
// Used by Enum classes to track whether it still needs context |
|
2810 |
private int enumCount = 0; |
|
2811 |
private boolean closeRequested = false; |
|
2812 |
||
2813 |
synchronized void incEnumCount() { |
|
2814 |
++enumCount; |
|
2815 |
if (debug) System.err.println("LdapCtx: " + this + " enum inc: " + enumCount); |
|
2816 |
} |
|
2817 |
||
2818 |
synchronized void decEnumCount() { |
|
2819 |
--enumCount; |
|
2820 |
if (debug) System.err.println("LdapCtx: " + this + " enum dec: " + enumCount); |
|
2821 |
||
2822 |
if (enumCount == 0 && closeRequested) { |
|
2823 |
try { |
|
2824 |
close(); |
|
2825 |
} catch (NamingException e) { |
|
2826 |
// ignore failures |
|
2827 |
} |
|
2828 |
} |
|
2829 |
} |
|
2830 |
||
2831 |
||
2832 |
// ------------ Return code and Error messages ----------------------- |
|
2833 |
||
2834 |
protected void processReturnCode(LdapResult answer) throws NamingException { |
|
2835 |
processReturnCode(answer, null, this, null, envprops, null); |
|
2836 |
} |
|
2837 |
||
2838 |
void processReturnCode(LdapResult answer, Name remainName) |
|
2839 |
throws NamingException { |
|
2840 |
processReturnCode(answer, |
|
2841 |
(new CompositeName()).add(currentDN), |
|
2842 |
this, |
|
2843 |
remainName, |
|
2844 |
envprops, |
|
2845 |
fullyQualifiedName(remainName)); |
|
2846 |
} |
|
2847 |
||
2848 |
protected void processReturnCode(LdapResult res, Name resolvedName, |
|
2849 |
Object resolvedObj, Name remainName, Hashtable envprops, String fullDN) |
|
2850 |
throws NamingException { |
|
2851 |
||
2852 |
String msg = LdapClient.getErrorMessage(res.status, res.errorMessage); |
|
2853 |
NamingException e; |
|
2854 |
LdapReferralException r = null; |
|
2855 |
||
2856 |
switch (res.status) { |
|
2857 |
||
2858 |
case LdapClient.LDAP_SUCCESS: |
|
2859 |
||
2860 |
// handle Search continuation references |
|
2861 |
if (res.referrals != null) { |
|
2862 |
||
2863 |
msg = "Unprocessed Continuation Reference(s)"; |
|
2864 |
||
2865 |
if (handleReferrals == LdapClient.LDAP_REF_IGNORE) { |
|
2866 |
e = new PartialResultException(msg); |
|
2867 |
break; |
|
2868 |
} |
|
2869 |
||
2870 |
// handle multiple sets of URLs |
|
2871 |
int contRefCount = res.referrals.size(); |
|
2872 |
LdapReferralException head = null; |
|
2873 |
LdapReferralException ptr = null; |
|
2874 |
||
2875 |
msg = "Continuation Reference"; |
|
2876 |
||
2877 |
// make a chain of LdapReferralExceptions |
|
2878 |
for (int i = 0; i < contRefCount; i++) { |
|
2879 |
||
2880 |
r = new LdapReferralException(resolvedName, resolvedObj, |
|
2881 |
remainName, msg, envprops, fullDN, handleReferrals, |
|
2882 |
reqCtls); |
|
2883 |
r.setReferralInfo((Vector)res.referrals.elementAt(i), true); |
|
2884 |
||
2885 |
if (hopCount > 1) { |
|
2886 |
r.setHopCount(hopCount); |
|
2887 |
} |
|
2888 |
||
2889 |
if (head == null) { |
|
2890 |
head = ptr = r; |
|
2891 |
} else { |
|
2892 |
ptr.nextReferralEx = r; // append ex. to end of chain |
|
2893 |
ptr = r; |
|
2894 |
} |
|
2895 |
} |
|
2896 |
res.referrals = null; // reset |
|
2897 |
||
2898 |
if (res.refEx == null) { |
|
2899 |
res.refEx = head; |
|
2900 |
||
2901 |
} else { |
|
2902 |
ptr = res.refEx; |
|
2903 |
||
2904 |
while (ptr.nextReferralEx != null) { |
|
2905 |
ptr = ptr.nextReferralEx; |
|
2906 |
} |
|
2907 |
ptr.nextReferralEx = head; |
|
2908 |
} |
|
2909 |
||
2910 |
// check the hop limit |
|
2911 |
if (hopCount > referralHopLimit) { |
|
2912 |
NamingException lee = |
|
2913 |
new LimitExceededException("Referral limit exceeded"); |
|
2914 |
lee.setRootCause(r); |
|
2915 |
throw lee; |
|
2916 |
} |
|
2917 |
} |
|
2918 |
return; |
|
2919 |
||
2920 |
case LdapClient.LDAP_REFERRAL: |
|
2921 |
||
2922 |
if (handleReferrals == LdapClient.LDAP_REF_IGNORE) { |
|
2923 |
e = new PartialResultException(msg); |
|
2924 |
break; |
|
2925 |
} |
|
2926 |
||
2927 |
r = new LdapReferralException(resolvedName, resolvedObj, remainName, |
|
2928 |
msg, envprops, fullDN, handleReferrals, reqCtls); |
|
2929 |
// only one set of URLs is present |
|
2930 |
r.setReferralInfo((Vector)res.referrals.elementAt(0), false); |
|
2931 |
||
2932 |
if (hopCount > 1) { |
|
2933 |
r.setHopCount(hopCount); |
|
2934 |
} |
|
2935 |
||
2936 |
// check the hop limit |
|
2937 |
if (hopCount > referralHopLimit) { |
|
2938 |
NamingException lee = |
|
2939 |
new LimitExceededException("Referral limit exceeded"); |
|
2940 |
lee.setRootCause(r); |
|
2941 |
e = lee; |
|
2942 |
||
2943 |
} else { |
|
2944 |
e = r; |
|
2945 |
} |
|
2946 |
break; |
|
2947 |
||
2948 |
/* |
|
2949 |
* Handle SLAPD-style referrals. |
|
2950 |
* |
|
2951 |
* Referrals received during name resolution should be followed |
|
2952 |
* until one succeeds - the target entry is located. An exception |
|
2953 |
* is thrown now to handle these. |
|
2954 |
* |
|
2955 |
* Referrals received during a search operation point to unexplored |
|
2956 |
* parts of the directory and each should be followed. An exception |
|
2957 |
* is thrown later (during results enumeration) to handle these. |
|
2958 |
*/ |
|
2959 |
||
2960 |
case LdapClient.LDAP_PARTIAL_RESULTS: |
|
2961 |
||
2962 |
if (handleReferrals == LdapClient.LDAP_REF_IGNORE) { |
|
2963 |
e = new PartialResultException(msg); |
|
2964 |
break; |
|
2965 |
} |
|
2966 |
||
2967 |
// extract SLAPD-style referrals from errorMessage |
|
2968 |
if ((res.errorMessage != null) && (!res.errorMessage.equals(""))) { |
|
2969 |
res.referrals = extractURLs(res.errorMessage); |
|
2970 |
} else { |
|
2971 |
e = new PartialResultException(msg); |
|
2972 |
break; |
|
2973 |
} |
|
2974 |
||
2975 |
// build exception |
|
2976 |
r = new LdapReferralException(resolvedName, |
|
2977 |
resolvedObj, |
|
2978 |
remainName, |
|
2979 |
msg, |
|
2980 |
envprops, |
|
2981 |
fullDN, |
|
2982 |
handleReferrals, |
|
2983 |
reqCtls); |
|
2984 |
||
2985 |
if (hopCount > 1) { |
|
2986 |
r.setHopCount(hopCount); |
|
2987 |
} |
|
2988 |
/* |
|
2989 |
* %%% |
|
2990 |
* SLAPD-style referrals received during name resolution |
|
2991 |
* cannot be distinguished from those received during a |
|
2992 |
* search operation. Since both must be handled differently |
|
2993 |
* the following rule is applied: |
|
2994 |
* |
|
2995 |
* If 1 referral and 0 entries is received then |
|
2996 |
* assume name resolution has not yet completed. |
|
2997 |
*/ |
|
2998 |
if (((res.entries == null) || (res.entries.size() == 0)) && |
|
2999 |
(res.referrals.size() == 1)) { |
|
3000 |
||
3001 |
r.setReferralInfo((Vector)res.referrals, false); |
|
3002 |
||
3003 |
// check the hop limit |
|
3004 |
if (hopCount > referralHopLimit) { |
|
3005 |
NamingException lee = |
|
3006 |
new LimitExceededException("Referral limit exceeded"); |
|
3007 |
lee.setRootCause(r); |
|
3008 |
e = lee; |
|
3009 |
||
3010 |
} else { |
|
3011 |
e = r; |
|
3012 |
} |
|
3013 |
||
3014 |
} else { |
|
3015 |
r.setReferralInfo(res.referrals, true); |
|
3016 |
res.refEx = r; |
|
3017 |
return; |
|
3018 |
} |
|
3019 |
break; |
|
3020 |
||
3021 |
case LdapClient.LDAP_INVALID_DN_SYNTAX: |
|
3022 |
case LdapClient.LDAP_NAMING_VIOLATION: |
|
3023 |
||
3024 |
if (remainName != null) { |
|
3025 |
e = new |
|
3026 |
InvalidNameException(remainName.toString() + ": " + msg); |
|
3027 |
} else { |
|
3028 |
e = new InvalidNameException(msg); |
|
3029 |
} |
|
3030 |
break; |
|
3031 |
||
3032 |
default: |
|
3033 |
e = mapErrorCode(res.status, res.errorMessage); |
|
3034 |
break; |
|
3035 |
} |
|
3036 |
e.setResolvedName(resolvedName); |
|
3037 |
e.setResolvedObj(resolvedObj); |
|
3038 |
e.setRemainingName(remainName); |
|
3039 |
throw e; |
|
3040 |
} |
|
3041 |
||
3042 |
/** |
|
3043 |
* Maps an LDAP error code to an appropriate NamingException. |
|
3044 |
* %%% public; used by controls |
|
3045 |
* |
|
3046 |
* @param errorCode numeric LDAP error code |
|
3047 |
* @param errorMessage textual description of the LDAP error. May be null. |
|
3048 |
* |
|
3049 |
* @return A NamingException or null if the error code indicates success. |
|
3050 |
*/ |
|
3051 |
public static NamingException mapErrorCode(int errorCode, |
|
3052 |
String errorMessage) { |
|
3053 |
||
3054 |
if (errorCode == LdapClient.LDAP_SUCCESS) |
|
3055 |
return null; |
|
3056 |
||
3057 |
NamingException e = null; |
|
3058 |
String message = LdapClient.getErrorMessage(errorCode, errorMessage); |
|
3059 |
||
3060 |
switch (errorCode) { |
|
3061 |
||
3062 |
case LdapClient.LDAP_ALIAS_DEREFERENCING_PROBLEM: |
|
3063 |
e = new NamingException(message); |
|
3064 |
break; |
|
3065 |
||
3066 |
case LdapClient.LDAP_ALIAS_PROBLEM: |
|
3067 |
e = new NamingException(message); |
|
3068 |
break; |
|
3069 |
||
3070 |
case LdapClient.LDAP_ATTRIBUTE_OR_VALUE_EXISTS: |
|
3071 |
e = new AttributeInUseException(message); |
|
3072 |
break; |
|
3073 |
||
3074 |
case LdapClient.LDAP_AUTH_METHOD_NOT_SUPPORTED: |
|
3075 |
case LdapClient.LDAP_CONFIDENTIALITY_REQUIRED: |
|
3076 |
case LdapClient.LDAP_STRONG_AUTH_REQUIRED: |
|
3077 |
case LdapClient.LDAP_INAPPROPRIATE_AUTHENTICATION: |
|
3078 |
e = new AuthenticationNotSupportedException(message); |
|
3079 |
break; |
|
3080 |
||
3081 |
case LdapClient.LDAP_ENTRY_ALREADY_EXISTS: |
|
3082 |
e = new NameAlreadyBoundException(message); |
|
3083 |
break; |
|
3084 |
||
3085 |
case LdapClient.LDAP_INVALID_CREDENTIALS: |
|
3086 |
case LdapClient.LDAP_SASL_BIND_IN_PROGRESS: |
|
3087 |
e = new AuthenticationException(message); |
|
3088 |
break; |
|
3089 |
||
3090 |
case LdapClient.LDAP_INAPPROPRIATE_MATCHING: |
|
3091 |
e = new InvalidSearchFilterException(message); |
|
3092 |
break; |
|
3093 |
||
3094 |
case LdapClient.LDAP_INSUFFICIENT_ACCESS_RIGHTS: |
|
3095 |
e = new NoPermissionException(message); |
|
3096 |
break; |
|
3097 |
||
3098 |
case LdapClient.LDAP_INVALID_ATTRIBUTE_SYNTAX: |
|
3099 |
case LdapClient.LDAP_CONSTRAINT_VIOLATION: |
|
3100 |
e = new InvalidAttributeValueException(message); |
|
3101 |
break; |
|
3102 |
||
3103 |
case LdapClient.LDAP_LOOP_DETECT: |
|
3104 |
e = new NamingException(message); |
|
3105 |
break; |
|
3106 |
||
3107 |
case LdapClient.LDAP_NO_SUCH_ATTRIBUTE: |
|
3108 |
e = new NoSuchAttributeException(message); |
|
3109 |
break; |
|
3110 |
||
3111 |
case LdapClient.LDAP_NO_SUCH_OBJECT: |
|
3112 |
e = new NameNotFoundException(message); |
|
3113 |
break; |
|
3114 |
||
3115 |
case LdapClient.LDAP_OBJECT_CLASS_MODS_PROHIBITED: |
|
3116 |
case LdapClient.LDAP_OBJECT_CLASS_VIOLATION: |
|
3117 |
case LdapClient.LDAP_NOT_ALLOWED_ON_RDN: |
|
3118 |
e = new SchemaViolationException(message); |
|
3119 |
break; |
|
3120 |
||
3121 |
case LdapClient.LDAP_NOT_ALLOWED_ON_NON_LEAF: |
|
3122 |
e = new ContextNotEmptyException(message); |
|
3123 |
break; |
|
3124 |
||
3125 |
case LdapClient.LDAP_OPERATIONS_ERROR: |
|
3126 |
// %%% need new exception ? |
|
3127 |
e = new NamingException(message); |
|
3128 |
break; |
|
3129 |
||
3130 |
case LdapClient.LDAP_OTHER: |
|
3131 |
e = new NamingException(message); |
|
3132 |
break; |
|
3133 |
||
3134 |
case LdapClient.LDAP_PROTOCOL_ERROR: |
|
3135 |
e = new CommunicationException(message); |
|
3136 |
break; |
|
3137 |
||
3138 |
case LdapClient.LDAP_SIZE_LIMIT_EXCEEDED: |
|
3139 |
e = new SizeLimitExceededException(message); |
|
3140 |
break; |
|
3141 |
||
3142 |
case LdapClient.LDAP_TIME_LIMIT_EXCEEDED: |
|
3143 |
e = new TimeLimitExceededException(message); |
|
3144 |
break; |
|
3145 |
||
3146 |
case LdapClient.LDAP_UNAVAILABLE_CRITICAL_EXTENSION: |
|
3147 |
e = new OperationNotSupportedException(message); |
|
3148 |
break; |
|
3149 |
||
3150 |
case LdapClient.LDAP_UNAVAILABLE: |
|
3151 |
case LdapClient.LDAP_BUSY: |
|
3152 |
e = new ServiceUnavailableException(message); |
|
3153 |
break; |
|
3154 |
||
3155 |
case LdapClient.LDAP_UNDEFINED_ATTRIBUTE_TYPE: |
|
3156 |
e = new InvalidAttributeIdentifierException(message); |
|
3157 |
break; |
|
3158 |
||
3159 |
case LdapClient.LDAP_UNWILLING_TO_PERFORM: |
|
3160 |
e = new OperationNotSupportedException(message); |
|
3161 |
break; |
|
3162 |
||
3163 |
case LdapClient.LDAP_COMPARE_FALSE: |
|
3164 |
case LdapClient.LDAP_COMPARE_TRUE: |
|
3165 |
case LdapClient.LDAP_IS_LEAF: |
|
3166 |
// these are really not exceptions and this code probably |
|
3167 |
// never gets executed |
|
3168 |
e = new NamingException(message); |
|
3169 |
break; |
|
3170 |
||
3171 |
case LdapClient.LDAP_ADMIN_LIMIT_EXCEEDED: |
|
3172 |
e = new LimitExceededException(message); |
|
3173 |
break; |
|
3174 |
||
3175 |
case LdapClient.LDAP_REFERRAL: |
|
3176 |
e = new NamingException(message); |
|
3177 |
break; |
|
3178 |
||
3179 |
case LdapClient.LDAP_PARTIAL_RESULTS: |
|
3180 |
e = new NamingException(message); |
|
3181 |
break; |
|
3182 |
||
3183 |
case LdapClient.LDAP_INVALID_DN_SYNTAX: |
|
3184 |
case LdapClient.LDAP_NAMING_VIOLATION: |
|
3185 |
e = new InvalidNameException(message); |
|
3186 |
break; |
|
3187 |
||
3188 |
default: |
|
3189 |
e = new NamingException(message); |
|
3190 |
break; |
|
3191 |
} |
|
3192 |
||
3193 |
return e; |
|
3194 |
} |
|
3195 |
||
3196 |
// ----------------- Extensions and Controls ------------------- |
|
3197 |
||
3198 |
public ExtendedResponse extendedOperation(ExtendedRequest request) |
|
3199 |
throws NamingException { |
|
3200 |
||
3201 |
boolean startTLS = (request.getID().equals(STARTTLS_REQ_OID)); |
|
3202 |
ensureOpen(startTLS); |
|
3203 |
||
3204 |
try { |
|
3205 |
||
3206 |
LdapResult answer = |
|
3207 |
clnt.extendedOp(request.getID(), request.getEncodedValue(), |
|
3208 |
reqCtls, startTLS); |
|
3209 |
respCtls = answer.resControls; // retrieve response controls |
|
3210 |
||
3211 |
if (answer.status != LdapClient.LDAP_SUCCESS) { |
|
3212 |
processReturnCode(answer, new CompositeName()); |
|
3213 |
} |
|
3214 |
// %%% verify request.getID() == answer.extensionId |
|
3215 |
||
3216 |
int len = (answer.extensionValue == null) ? |
|
3217 |
0 : |
|
3218 |
answer.extensionValue.length; |
|
3219 |
||
3220 |
ExtendedResponse er = |
|
3221 |
request.createExtendedResponse(answer.extensionId, |
|
3222 |
answer.extensionValue, 0, len); |
|
3223 |
||
3224 |
if (er instanceof StartTlsResponseImpl) { |
|
3225 |
// Pass the connection handle to StartTlsResponseImpl |
|
3226 |
String domainName = (String) |
|
3227 |
(envprops != null ? envprops.get(DOMAIN_NAME) : null); |
|
3228 |
((StartTlsResponseImpl)er).setConnection(clnt.conn, domainName); |
|
3229 |
} |
|
3230 |
return er; |
|
3231 |
||
3232 |
} catch (LdapReferralException e) { |
|
3233 |
||
3234 |
if (handleReferrals == LdapClient.LDAP_REF_THROW) |
|
3235 |
throw e; |
|
3236 |
||
3237 |
// process the referrals sequentially |
|
3238 |
while (true) { |
|
3239 |
||
3240 |
LdapReferralContext refCtx = |
|
3241 |
(LdapReferralContext)e.getReferralContext(envprops, bindCtls); |
|
3242 |
||
3243 |
// repeat the original operation at the new context |
|
3244 |
try { |
|
3245 |
||
3246 |
return refCtx.extendedOperation(request); |
|
3247 |
||
3248 |
} catch (LdapReferralException re) { |
|
3249 |
e = re; |
|
3250 |
continue; |
|
3251 |
||
3252 |
} finally { |
|
3253 |
// Make sure we close referral context |
|
3254 |
refCtx.close(); |
|
3255 |
} |
|
3256 |
} |
|
3257 |
||
3258 |
} catch (IOException e) { |
|
3259 |
NamingException e2 = new CommunicationException(e.getMessage()); |
|
3260 |
e2.setRootCause(e); |
|
3261 |
throw e2; |
|
3262 |
} |
|
3263 |
} |
|
3264 |
||
3265 |
public void setRequestControls(Control[] reqCtls) throws NamingException { |
|
3266 |
if (handleReferrals == LdapClient.LDAP_REF_IGNORE) { |
|
3267 |
this.reqCtls = addControl(reqCtls, manageReferralControl); |
|
3268 |
} else { |
|
3269 |
this.reqCtls = cloneControls(reqCtls); |
|
3270 |
} |
|
3271 |
} |
|
3272 |
||
3273 |
public Control[] getRequestControls() throws NamingException { |
|
3274 |
return cloneControls(reqCtls); |
|
3275 |
} |
|
3276 |
||
3277 |
public Control[] getConnectControls() throws NamingException { |
|
3278 |
return cloneControls(bindCtls); |
|
3279 |
} |
|
3280 |
||
3281 |
public Control[] getResponseControls() throws NamingException { |
|
3282 |
return (respCtls != null)? convertControls(respCtls) : null; |
|
3283 |
} |
|
3284 |
||
3285 |
/** |
|
3286 |
* Narrow controls using own default factory and ControlFactory. |
|
3287 |
* @param ctls A non-null Vector |
|
3288 |
*/ |
|
3289 |
Control[] convertControls(Vector ctls) throws NamingException { |
|
3290 |
int count = ctls.size(); |
|
3291 |
||
3292 |
if (count == 0) { |
|
3293 |
return null; |
|
3294 |
} |
|
3295 |
||
3296 |
Control[] controls = new Control[count]; |
|
3297 |
||
3298 |
for (int i = 0; i < count; i++) { |
|
3299 |
// Try own factory first |
|
3300 |
controls[i] = myResponseControlFactory.getControlInstance( |
|
3301 |
(Control)ctls.elementAt(i)); |
|
3302 |
||
3303 |
// Try assigned factories if own produced null |
|
3304 |
if (controls[i] == null) { |
|
3305 |
controls[i] = ControlFactory.getControlInstance( |
|
3306 |
(Control)ctls.elementAt(i), this, envprops); |
|
3307 |
} |
|
3308 |
} |
|
3309 |
return controls; |
|
3310 |
} |
|
3311 |
||
3312 |
private static Control[] addControl(Control[] prevCtls, Control addition) { |
|
3313 |
if (prevCtls == null) { |
|
3314 |
return new Control[]{addition}; |
|
3315 |
} |
|
3316 |
||
3317 |
// Find it |
|
3318 |
int found = findControl(prevCtls, addition); |
|
3319 |
if (found != -1) { |
|
3320 |
return prevCtls; // no need to do it again |
|
3321 |
} |
|
3322 |
||
3323 |
Control[] newCtls = new Control[prevCtls.length+1]; |
|
3324 |
System.arraycopy(prevCtls, 0, newCtls, 0, prevCtls.length); |
|
3325 |
newCtls[prevCtls.length] = addition; |
|
3326 |
return newCtls; |
|
3327 |
} |
|
3328 |
||
3329 |
private static int findControl(Control[] ctls, Control target) { |
|
3330 |
for (int i = 0; i < ctls.length; i++) { |
|
3331 |
if (ctls[i] == target) { |
|
3332 |
return i; |
|
3333 |
} |
|
3334 |
} |
|
3335 |
return -1; |
|
3336 |
} |
|
3337 |
||
3338 |
private static Control[] removeControl(Control[] prevCtls, Control target) { |
|
3339 |
if (prevCtls == null) { |
|
3340 |
return null; |
|
3341 |
} |
|
3342 |
||
3343 |
// Find it |
|
3344 |
int found = findControl(prevCtls, target); |
|
3345 |
if (found == -1) { |
|
3346 |
return prevCtls; // not there |
|
3347 |
} |
|
3348 |
||
3349 |
// Remove it |
|
3350 |
Control[] newCtls = new Control[prevCtls.length-1]; |
|
3351 |
System.arraycopy(prevCtls, 0, newCtls, 0, found); |
|
3352 |
System.arraycopy(prevCtls, found+1, newCtls, found, |
|
3353 |
prevCtls.length-found-1); |
|
3354 |
return newCtls; |
|
3355 |
} |
|
3356 |
||
3357 |
private static Control[] cloneControls(Control[] ctls) { |
|
3358 |
if (ctls == null) { |
|
3359 |
return null; |
|
3360 |
} |
|
3361 |
Control[] copiedCtls = new Control[ctls.length]; |
|
3362 |
System.arraycopy(ctls, 0, copiedCtls, 0, ctls.length); |
|
3363 |
return copiedCtls; |
|
3364 |
} |
|
3365 |
||
3366 |
// -------------------- Events ------------------------ |
|
3367 |
/* |
|
3368 |
* Access to eventSupport need not be synchronized even though the |
|
3369 |
* Connection thread can access it asynchronously. It is |
|
3370 |
* impossible for a race condition to occur because |
|
3371 |
* eventSupport.addNamingListener() must have been called before |
|
3372 |
* the Connection thread can call back to this ctx. |
|
3373 |
*/ |
|
3374 |
public void addNamingListener(Name nm, int scope, NamingListener l) |
|
3375 |
throws NamingException { |
|
3376 |
addNamingListener(getTargetName(nm), scope, l); |
|
3377 |
} |
|
3378 |
||
3379 |
public void addNamingListener(String nm, int scope, NamingListener l) |
|
3380 |
throws NamingException { |
|
3381 |
if (eventSupport == null) |
|
3382 |
eventSupport = new EventSupport(this); |
|
3383 |
eventSupport.addNamingListener(getTargetName(new CompositeName(nm)), |
|
3384 |
scope, l); |
|
3385 |
||
3386 |
// If first time asking for unsol |
|
3387 |
if (l instanceof UnsolicitedNotificationListener && !unsolicited) { |
|
3388 |
addUnsolicited(); |
|
3389 |
} |
|
3390 |
} |
|
3391 |
||
3392 |
public void removeNamingListener(NamingListener l) throws NamingException { |
|
3393 |
if (eventSupport == null) |
|
3394 |
return; // no activity before, so just return |
|
3395 |
||
3396 |
eventSupport.removeNamingListener(l); |
|
3397 |
||
3398 |
// If removing an Unsol listener and it is the last one, let clnt know |
|
3399 |
if (l instanceof UnsolicitedNotificationListener && |
|
3400 |
!eventSupport.hasUnsolicited()) { |
|
3401 |
removeUnsolicited(); |
|
3402 |
} |
|
3403 |
} |
|
3404 |
||
3405 |
public void addNamingListener(String nm, String filter, SearchControls ctls, |
|
3406 |
NamingListener l) throws NamingException { |
|
3407 |
if (eventSupport == null) |
|
3408 |
eventSupport = new EventSupport(this); |
|
3409 |
eventSupport.addNamingListener(getTargetName(new CompositeName(nm)), |
|
3410 |
filter, cloneSearchControls(ctls), l); |
|
3411 |
||
3412 |
// If first time asking for unsol |
|
3413 |
if (l instanceof UnsolicitedNotificationListener && !unsolicited) { |
|
3414 |
addUnsolicited(); |
|
3415 |
} |
|
3416 |
} |
|
3417 |
||
3418 |
public void addNamingListener(Name nm, String filter, SearchControls ctls, |
|
3419 |
NamingListener l) throws NamingException { |
|
3420 |
addNamingListener(getTargetName(nm), filter, ctls, l); |
|
3421 |
} |
|
3422 |
||
3423 |
public void addNamingListener(Name nm, String filter, Object[] filterArgs, |
|
3424 |
SearchControls ctls, NamingListener l) throws NamingException { |
|
3425 |
addNamingListener(getTargetName(nm), filter, filterArgs, ctls, l); |
|
3426 |
} |
|
3427 |
||
3428 |
public void addNamingListener(String nm, String filterExpr, Object[] filterArgs, |
|
3429 |
SearchControls ctls, NamingListener l) throws NamingException { |
|
3430 |
String strfilter = SearchFilter.format(filterExpr, filterArgs); |
|
3431 |
addNamingListener(getTargetName(new CompositeName(nm)), strfilter, ctls, l); |
|
3432 |
} |
|
3433 |
||
3434 |
public boolean targetMustExist() { |
|
3435 |
return true; |
|
3436 |
} |
|
3437 |
||
3438 |
/** |
|
3439 |
* Retrieves the target name for which the listener is registering. |
|
3440 |
* If nm is a CompositeName, use its first and only component. It |
|
3441 |
* cannot have more than one components because a target be outside of |
|
3442 |
* this namespace. If nm is not a CompositeName, then treat it as a |
|
3443 |
* compound name. |
|
3444 |
* @param nm The non-null target name. |
|
3445 |
*/ |
|
3446 |
private static String getTargetName(Name nm) throws NamingException { |
|
3447 |
if (nm instanceof CompositeName) { |
|
3448 |
if (nm.size() > 1) { |
|
3449 |
throw new InvalidNameException( |
|
3450 |
"Target cannot span multiple namespaces: " + nm); |
|
3451 |
} else if (nm.size() == 0) { |
|
3452 |
return ""; |
|
3453 |
} else { |
|
3454 |
return nm.get(0); |
|
3455 |
} |
|
3456 |
} else { |
|
3457 |
// treat as compound name |
|
3458 |
return nm.toString(); |
|
3459 |
} |
|
3460 |
} |
|
3461 |
||
3462 |
// ------------------ Unsolicited Notification --------------- |
|
3463 |
// package private methods for handling unsolicited notification |
|
3464 |
||
3465 |
/** |
|
3466 |
* Registers this context with the underlying LdapClient. |
|
3467 |
* When the underlying LdapClient receives an unsolicited notification, |
|
3468 |
* it will invoke LdapCtx.fireUnsolicited() so that this context |
|
3469 |
* can (using EventSupport) notified any registered listeners. |
|
3470 |
* This method is called by EventSupport when an unsolicited listener |
|
3471 |
* first registers with this context (should be called just once). |
|
3472 |
* @see #removeUnsolicited |
|
3473 |
* @see #fireUnsolicited |
|
3474 |
*/ |
|
3475 |
private void addUnsolicited() throws NamingException { |
|
3476 |
if (debug) { |
|
3477 |
System.out.println("LdapCtx.addUnsolicited: " + this); |
|
3478 |
} |
|
3479 |
||
3480 |
// addNamingListener must have created EventSupport already |
|
3481 |
ensureOpen(); |
|
3482 |
synchronized (eventSupport) { |
|
3483 |
clnt.addUnsolicited(this); |
|
3484 |
unsolicited = true; |
|
3485 |
} |
|
3486 |
} |
|
3487 |
||
3488 |
/** |
|
3489 |
* Removes this context from registering interest in unsolicited |
|
3490 |
* notifications from the underlying LdapClient. This method is called |
|
3491 |
* under any one of the following conditions: |
|
3492 |
* <ul> |
|
3493 |
* <li>All unsolicited listeners have been removed. (see removingNamingListener) |
|
3494 |
* <li>This context is closed. |
|
3495 |
* <li>This context's underlying LdapClient changes. |
|
3496 |
*</ul> |
|
3497 |
* After this method has been called, this context will not pass |
|
3498 |
* on any events related to unsolicited notifications to EventSupport and |
|
3499 |
* and its listeners. |
|
3500 |
*/ |
|
3501 |
||
3502 |
private void removeUnsolicited() { |
|
3503 |
if (debug) { |
|
3504 |
System.out.println("LdapCtx.removeUnsolicited: " + unsolicited); |
|
3505 |
} |
|
3506 |
if (eventSupport == null) { |
|
3507 |
return; |
|
3508 |
} |
|
3509 |
||
3510 |
// addNamingListener must have created EventSupport already |
|
3511 |
synchronized(eventSupport) { |
|
3512 |
if (unsolicited && clnt != null) { |
|
3513 |
clnt.removeUnsolicited(this); |
|
3514 |
} |
|
3515 |
unsolicited = false; |
|
3516 |
} |
|
3517 |
} |
|
3518 |
||
3519 |
/** |
|
3520 |
* Uses EventSupport to fire an event related to an unsolicited notification. |
|
3521 |
* Called by LdapClient when LdapClient receives an unsolicited notification. |
|
3522 |
*/ |
|
3523 |
void fireUnsolicited(Object obj) { |
|
3524 |
if (debug) { |
|
3525 |
System.out.println("LdapCtx.fireUnsolicited: " + obj); |
|
3526 |
} |
|
3527 |
// addNamingListener must have created EventSupport already |
|
3528 |
synchronized(eventSupport) { |
|
3529 |
if (unsolicited) { |
|
3530 |
eventSupport.fireUnsolicited(obj); |
|
3531 |
||
3532 |
if (obj instanceof NamingException) { |
|
3533 |
unsolicited = false; |
|
3534 |
// No need to notify clnt because clnt is the |
|
3535 |
// only one that can fire a NamingException to |
|
3536 |
// unsol listeners and it will handle its own cleanup |
|
3537 |
} |
|
3538 |
} |
|
3539 |
} |
|
3540 |
} |
|
3541 |
} |