1 /* 2 * Copyright (c) 1999, 2004, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 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 * 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. 24 */ 25 26 package com.sun.jndi.toolkit.url; 27 28 import javax.naming.*; 29 import javax.naming.spi.ResolveResult; 30 import javax.naming.spi.NamingManager; 31 32 import java.util.Hashtable; 33 import java.net.MalformedURLException; 34 35 /** 36 * This abstract class is a generic URL context that accepts as the 37 * name argument either a string URL or a Name whose first component 38 * is a URL. It resolves the URL to a target context and then continues 39 * the operation using the remaining name in the target context as if 40 * the first component names a junction. 41 * 42 * A subclass must define getRootURLContext() 43 * to process the URL into head/tail pieces. If it wants to control how 44 * URL strings are parsed and compared for the rename() operation, then 45 * it should override getNonRootURLSuffixes() and urlEquals(). 46 * 47 * @author Scott Seligman 48 * @author Rosanna Lee 49 */ 50 abstract public class GenericURLContext implements Context { 51 protected Hashtable myEnv = null; 52 53 public GenericURLContext(Hashtable env) { 54 // context that is not tied to any specific URL 55 myEnv = env; // copied on write 56 } 57 58 public void close() throws NamingException { 59 myEnv = null; 60 } 61 62 public String getNameInNamespace() throws NamingException { 63 return ""; // %%% check this out: A URL context's name is "" 64 } 65 66 /** 67 * Resolves 'name' into a target context with remaining name. 68 * For example, with a JNDI URL "jndi://dnsname/rest_name", 69 * this method resolves "jndi://dnsname/" to a target context, 70 * and returns the target context with "rest_name". 71 * The definition of "root URL" and how much of the URL to 72 * consume is implementation specific. 73 * If rename() is supported for a particular URL scheme, 74 * getRootURLContext(), getURLPrefix(), and getURLSuffix() 75 * must be in sync wrt how URLs are parsed and returned. 76 */ 77 abstract protected ResolveResult getRootURLContext(String url, 78 Hashtable env) throws NamingException; 79 80 /** 81 * Returns the suffix of the url. The result should be identical to 82 * that of calling getRootURLContext().getRemainingName(), but 83 * without the overhead of doing anything with the prefix like 84 * creating a context. 85 *<p> 86 * This method returns a Name instead of a String because to give 87 * the provider an opportunity to return a Name (for example, 88 * for weakly separated naming systems like COS naming). 89 *<p> 90 * The default implementation uses skips 'prefix', calls 91 * UrlUtil.decode() on it, and returns the result as a single component 92 * CompositeName. 93 * Subclass should override if this is not appropriate. 94 * This method is used only by rename(). 95 * If rename() is supported for a particular URL scheme, 96 * getRootURLContext(), getURLPrefix(), and getURLSuffix() 97 * must be in sync wrt how URLs are parsed and returned. 98 *<p> 99 * For many URL schemes, this method is very similar to URL.getFile(), 100 * except getFile() will return a leading slash in the 101 * 2nd, 3rd, and 4th cases. For schemes like "ldap" and "iiop", 102 * the leading slash must be skipped before the name is an acceptable 103 * format for operation by the Context methods. For schemes that treat the 104 * leading slash as significant (such as "file"), 105 * the subclass must override getURLSuffix() to get the correct behavior. 106 * Remember, the behavior must match getRootURLContext(). 107 * 108 * URL Suffix 109 * foo://host:port <empty string> 110 * foo://host:port/rest/of/name rest/of/name 111 * foo:///rest/of/name rest/of/name 112 * foo:/rest/of/name rest/of/name 113 * foo:rest/of/name rest/of/name 114 */ 115 protected Name getURLSuffix(String prefix, String url) throws NamingException { 116 String suffix = url.substring(prefix.length()); 117 if (suffix.length() == 0) { 118 return new CompositeName(); 119 } 120 121 if (suffix.charAt(0) == '/') { 122 suffix = suffix.substring(1); // skip leading slash 123 } 124 125 try { 126 return new CompositeName().add(UrlUtil.decode(suffix)); 127 } catch (MalformedURLException e) { 128 throw new InvalidNameException(e.getMessage()); 129 } 130 } 131 132 /** 133 * Finds the prefix of a URL. 134 * Default implementation looks for slashes and then extracts 135 * prefixes using String.substring(). 136 * Subclass should override if this is not appropriate. 137 * This method is used only by rename(). 138 * If rename() is supported for a particular URL scheme, 139 * getRootURLContext(), getURLPrefix(), and getURLSuffix() 140 * must be in sync wrt how URLs are parsed and returned. 141 *<p> 142 * URL Prefix 143 * foo://host:port foo://host:port 144 * foo://host:port/rest/of/name foo://host:port 145 * foo:///rest/of/name foo:// 146 * foo:/rest/of/name foo: 147 * foo:rest/of/name foo: 148 */ 149 protected String getURLPrefix(String url) throws NamingException { 150 int start = url.indexOf(":"); 151 152 if (start < 0) { 153 throw new OperationNotSupportedException("Invalid URL: " + url); 154 } 155 ++start; // skip ':' 156 157 if (url.startsWith("//", start)) { 158 start += 2; // skip double slash 159 160 // find last slash 161 int posn = url.indexOf("/", start); 162 if (posn >= 0) { 163 start = posn; 164 } else { 165 start = url.length(); // rest of URL 166 } 167 } 168 169 // else 0 or 1 iniitial slashes; start is unchanged 170 return url.substring(0, start); 171 } 172 173 /** 174 * Determines whether two URLs are the same. 175 * Default implementation uses String.equals(). 176 * Subclass should override if this is not appropriate. 177 * This method is used by rename(). 178 */ 179 protected boolean urlEquals(String url1, String url2) { 180 return url1.equals(url2); 181 } 182 183 /** 184 * Gets the context in which to continue the operation. This method 185 * is called when this context is asked to process a multicomponent 186 * Name in which the first component is a URL. 187 * Treat the first component like a junction: resolve it and then use 188 * NamingManager.getContinuationContext() to get the target context in 189 * which to operate on the remainder of the name (n.getSuffix(1)). 190 */ 191 protected Context getContinuationContext(Name n) throws NamingException { 192 Object obj = lookup(n.get(0)); 193 CannotProceedException cpe = new CannotProceedException(); 194 cpe.setResolvedObj(obj); 195 cpe.setEnvironment(myEnv); 196 return NamingManager.getContinuationContext(cpe); 197 } 198 199 public Object lookup(String name) throws NamingException { 200 ResolveResult res = getRootURLContext(name, myEnv); 201 Context ctx = (Context)res.getResolvedObj(); 202 try { 203 return ctx.lookup(res.getRemainingName()); 204 } finally { 205 ctx.close(); 206 } 207 } 208 209 public Object lookup(Name name) throws NamingException { 210 if (name.size() == 1) { 211 return lookup(name.get(0)); 212 } else { 213 Context ctx = getContinuationContext(name); 214 try { 215 return ctx.lookup(name.getSuffix(1)); 216 } finally { 217 ctx.close(); 218 } 219 } 220 } 221 222 public void bind(String name, Object obj) throws NamingException { 223 ResolveResult res = getRootURLContext(name, myEnv); 224 Context ctx = (Context)res.getResolvedObj(); 225 try { 226 ctx.bind(res.getRemainingName(), obj); 227 } finally { 228 ctx.close(); 229 } 230 } 231 232 public void bind(Name name, Object obj) throws NamingException { 233 if (name.size() == 1) { 234 bind(name.get(0), obj); 235 } else { 236 Context ctx = getContinuationContext(name); 237 try { 238 ctx.bind(name.getSuffix(1), obj); 239 } finally { 240 ctx.close(); 241 } 242 } 243 } 244 245 public void rebind(String name, Object obj) throws NamingException { 246 ResolveResult res = getRootURLContext(name, myEnv); 247 Context ctx = (Context)res.getResolvedObj(); 248 try { 249 ctx.rebind(res.getRemainingName(), obj); 250 } finally { 251 ctx.close(); 252 } 253 } 254 255 public void rebind(Name name, Object obj) throws NamingException { 256 if (name.size() == 1) { 257 rebind(name.get(0), obj); 258 } else { 259 Context ctx = getContinuationContext(name); 260 try { 261 ctx.rebind(name.getSuffix(1), obj); 262 } finally { 263 ctx.close(); 264 } 265 } 266 } 267 268 public void unbind(String name) throws NamingException { 269 ResolveResult res = getRootURLContext(name, myEnv); 270 Context ctx = (Context)res.getResolvedObj(); 271 try { 272 ctx.unbind(res.getRemainingName()); 273 } finally { 274 ctx.close(); 275 } 276 } 277 278 public void unbind(Name name) throws NamingException { 279 if (name.size() == 1) { 280 unbind(name.get(0)); 281 } else { 282 Context ctx = getContinuationContext(name); 283 try { 284 ctx.unbind(name.getSuffix(1)); 285 } finally { 286 ctx.close(); 287 } 288 } 289 } 290 291 public void rename(String oldName, String newName) throws NamingException { 292 String oldPrefix = getURLPrefix(oldName); 293 String newPrefix = getURLPrefix(newName); 294 if (!urlEquals(oldPrefix, newPrefix)) { 295 throw new OperationNotSupportedException( 296 "Renaming using different URL prefixes not supported : " + 297 oldName + " " + newName); 298 } 299 300 ResolveResult res = getRootURLContext(oldName, myEnv); 301 Context ctx = (Context)res.getResolvedObj(); 302 try { 303 ctx.rename(res.getRemainingName(), getURLSuffix(newPrefix, newName)); 304 } finally { 305 ctx.close(); 306 } 307 } 308 309 public void rename(Name name, Name newName) throws NamingException { 310 if (name.size() == 1) { 311 if (newName.size() != 1) { 312 throw new OperationNotSupportedException( 313 "Renaming to a Name with more components not supported: " + newName); 314 } 315 rename(name.get(0), newName.get(0)); 316 } else { 317 // > 1 component with 1st one being URL 318 // URLs must be identical; cannot deal with diff URLs 319 if (!urlEquals(name.get(0), newName.get(0))) { 320 throw new OperationNotSupportedException( 321 "Renaming using different URLs as first components not supported: " + 322 name + " " + newName); 323 } 324 325 Context ctx = getContinuationContext(name); 326 try { 327 ctx.rename(name.getSuffix(1), newName.getSuffix(1)); 328 } finally { 329 ctx.close(); 330 } 331 } 332 } 333 334 public NamingEnumeration<NameClassPair> list(String name) throws NamingException { 335 ResolveResult res = getRootURLContext(name, myEnv); 336 Context ctx = (Context)res.getResolvedObj(); 337 try { 338 return ctx.list(res.getRemainingName()); 339 } finally { 340 ctx.close(); 341 } 342 } 343 344 public NamingEnumeration<NameClassPair> list(Name name) throws NamingException { 345 if (name.size() == 1) { 346 return list(name.get(0)); 347 } else { 348 Context ctx = getContinuationContext(name); 349 try { 350 return ctx.list(name.getSuffix(1)); 351 } finally { 352 ctx.close(); 353 } 354 } 355 } 356 357 public NamingEnumeration<Binding> listBindings(String name) 358 throws NamingException { 359 ResolveResult res = getRootURLContext(name, myEnv); 360 Context ctx = (Context)res.getResolvedObj(); 361 try { 362 return ctx.listBindings(res.getRemainingName()); 363 } finally { 364 ctx.close(); 365 } 366 } 367 368 public NamingEnumeration<Binding> listBindings(Name name) throws NamingException { 369 if (name.size() == 1) { 370 return listBindings(name.get(0)); 371 } else { 372 Context ctx = getContinuationContext(name); 373 try { 374 return ctx.listBindings(name.getSuffix(1)); 375 } finally { 376 ctx.close(); 377 } 378 } 379 } 380 381 public void destroySubcontext(String name) throws NamingException { 382 ResolveResult res = getRootURLContext(name, myEnv); 383 Context ctx = (Context)res.getResolvedObj(); 384 try { 385 ctx.destroySubcontext(res.getRemainingName()); 386 } finally { 387 ctx.close(); 388 } 389 } 390 391 public void destroySubcontext(Name name) throws NamingException { 392 if (name.size() == 1) { 393 destroySubcontext(name.get(0)); 394 } else { 395 Context ctx = getContinuationContext(name); 396 try { 397 ctx.destroySubcontext(name.getSuffix(1)); 398 } finally { 399 ctx.close(); 400 } 401 } 402 } 403 404 public Context createSubcontext(String name) throws NamingException { 405 ResolveResult res = getRootURLContext(name, myEnv); 406 Context ctx = (Context)res.getResolvedObj(); 407 try { 408 return ctx.createSubcontext(res.getRemainingName()); 409 } finally { 410 ctx.close(); 411 } 412 } 413 414 public Context createSubcontext(Name name) throws NamingException { 415 if (name.size() == 1) { 416 return createSubcontext(name.get(0)); 417 } else { 418 Context ctx = getContinuationContext(name); 419 try { 420 return ctx.createSubcontext(name.getSuffix(1)); 421 } finally { 422 ctx.close(); 423 } 424 } 425 } 426 427 public Object lookupLink(String name) throws NamingException { 428 ResolveResult res = getRootURLContext(name, myEnv); 429 Context ctx = (Context)res.getResolvedObj(); 430 try { 431 return ctx.lookupLink(res.getRemainingName()); 432 } finally { 433 ctx.close(); 434 } 435 } 436 437 public Object lookupLink(Name name) throws NamingException { 438 if (name.size() == 1) { 439 return lookupLink(name.get(0)); 440 } else { 441 Context ctx = getContinuationContext(name); 442 try { 443 return ctx.lookupLink(name.getSuffix(1)); 444 } finally { 445 ctx.close(); 446 } 447 } 448 } 449 450 public NameParser getNameParser(String name) throws NamingException { 451 ResolveResult res = getRootURLContext(name, myEnv); 452 Context ctx = (Context)res.getResolvedObj(); 453 try { 454 return ctx.getNameParser(res.getRemainingName()); 455 } finally { 456 ctx.close(); 457 } 458 } 459 460 public NameParser getNameParser(Name name) throws NamingException { 461 if (name.size() == 1) { 462 return getNameParser(name.get(0)); 463 } else { 464 Context ctx = getContinuationContext(name); 465 try { 466 return ctx.getNameParser(name.getSuffix(1)); 467 } finally { 468 ctx.close(); 469 } 470 } 471 } 472 473 public String composeName(String name, String prefix) 474 throws NamingException { 475 if (prefix.equals("")) { 476 return name; 477 } else if (name.equals("")) { 478 return prefix; 479 } else { 480 return (prefix + "/" + name); 481 } 482 } 483 484 public Name composeName(Name name, Name prefix) throws NamingException { 485 Name result = (Name)prefix.clone(); 486 result.addAll(name); 487 return result; 488 } 489 490 public Object removeFromEnvironment(String propName) 491 throws NamingException { 492 if (myEnv == null) { 493 return null; 494 } 495 myEnv = (Hashtable)myEnv.clone(); 496 return myEnv.remove(propName); 497 } 498 499 public Object addToEnvironment(String propName, Object propVal) 500 throws NamingException { 501 myEnv = (myEnv == null) ? 502 new Hashtable(11, 0.75f) : (Hashtable)myEnv.clone(); 503 return myEnv.put(propName, propVal); 504 } 505 506 public Hashtable getEnvironment() throws NamingException { 507 if (myEnv == null) { 508 return new Hashtable(5, 0.75f); 509 } else { 510 return (Hashtable)myEnv.clone(); 511 } 512 } 513 514 /* 515 // To test, declare getURLPrefix and getURLSuffix static. 516 517 public static void main(String[] args) throws Exception { 518 String[] tests = {"file://host:port", 519 "file:///rest/of/name", 520 "file://host:port/rest/of/name", 521 "file:/rest/of/name", 522 "file:rest/of/name"}; 523 for (int i = 0; i < tests.length; i++) { 524 String pre = getURLPrefix(tests[i]); 525 System.out.println(pre); 526 System.out.println(getURLSuffix(pre, tests[i])); 527 } 528 } 529 */ 530 }