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