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