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