Source for gnu.javax.naming.jndi.url.rmi.rmiURLContext

   1: /* rmiURLContext.java -- RMI naming context
   2:    Copyright (C) 2006 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: package gnu.javax.naming.jndi.url.rmi;
  39: 
  40: import java.rmi.AccessException;
  41: import java.rmi.AlreadyBoundException;
  42: import java.rmi.NotBoundException;
  43: import java.rmi.Remote;
  44: import java.rmi.RemoteException;
  45: import java.rmi.registry.LocateRegistry;
  46: import java.rmi.registry.Registry;
  47: import java.util.Hashtable;
  48: import java.util.Map;
  49: import java.util.Properties;
  50: import java.util.WeakHashMap;
  51: 
  52: import javax.naming.CommunicationException;
  53: import javax.naming.Context;
  54: import javax.naming.InvalidNameException;
  55: import javax.naming.Name;
  56: import javax.naming.NameAlreadyBoundException;
  57: import javax.naming.NameNotFoundException;
  58: import javax.naming.NameParser;
  59: import javax.naming.NamingEnumeration;
  60: import javax.naming.NamingException;
  61: import javax.naming.OperationNotSupportedException;
  62: 
  63: /**
  64:  * The implementation of the RMI URL context. This context connects
  65:  *
  66:  * @author Audrius Meskauskas
  67:  */
  68: public class rmiURLContext implements Context
  69: {
  70:   /**
  71:    * The default registry location.
  72:    */
  73:   public static final String DEFAULT_REGISTRY_LOCATION = "rmi://localhost:1099";
  74: 
  75:   /**
  76:    * The registry cache, maps the registry URL's to they instances. The
  77:    * obtained registries are reused, as newly obtaining them may cause the
  78:    * resource leak.
  79:    */
  80:   static WeakHashMap registryCache = new WeakHashMap();
  81: 
  82:   /**
  83:    * The properties.
  84:    */
  85:   Properties properties;
  86: 
  87:   /**
  88:    * The flag, indicating, that the lookup methods were called before.
  89:    * If the lookup methods were called before, the existing ORB cannot be
  90:    * destroyed, as references to the existing objects will become
  91:    * unfunctional.
  92:    */
  93:   boolean lookupCalled;
  94: 
  95:   /**
  96:    * Add new environment property to the environment of this context. Both name
  97:    * and value of the new property must not be null. If the property is already
  98:    * defined, is current value is replaced by the propVal. This method replaces
  99:    * the registry. The new registry will be lazily instantiated on the first
 100:    * call.
 101:    *
 102:    * @param key
 103:    *          the name of the new property
 104:    * @param value
 105:    *          the value of the new property
 106:    * @return the previous value of this property or null if the property has not
 107:    *         been previously defined
 108:    */
 109:   public Object addToEnvironment(String key, Object value)
 110:   {
 111:     if (key == null || value == null)
 112:       throw new NullPointerException();
 113:     return properties.put(key, value);
 114:   }
 115: 
 116:   /**
 117:    * Returns the environment, associated with this naming context. The returned
 118:    * table should never be modified by the caller (the registry would not be updated
 119:    * in such case). Use {@link #addToEnvironment} and
 120:    * {@link #removeFromEnvironment} to modify the environement, if needed.
 121:    *
 122:    * @return the table, representing the environment of this context
 123:    * @throws NamingException
 124:    */
 125:   public Hashtable getEnvironment() throws NamingException
 126:   {
 127:     return properties;
 128:   }
 129: 
 130:   /**
 131:    * Removes the property with the given name from the environment. Returns
 132:    * without action if this property is not defined. Replaces the ORB,
 133:    * constructing the new ORB with the changes set of properties (you can
 134:    * replace the CORBA implementation provider, for instance). The new ORB will
 135:    * be lazily instantiated on the first call.
 136:    *
 137:    * @param propName
 138:    *          the name of the property being removed.
 139:    * @return the value of the property that has been removed or null if the
 140:    *         property was not defined.
 141:    * @throws NamingException
 142:    */
 143:   public Object removeFromEnvironment(String propName) throws NamingException
 144:   {
 145:     return properties.remove(propName);
 146:   }
 147: 
 148:   /**
 149:    * Get the cached or new registry reference.
 150:    *
 151:    * @return the registry reference, either cached or new.
 152:    */
 153:   public Registry getRegistry(String netAddress) throws NamingException
 154:   {
 155:     Registry registry;
 156: 
 157:     synchronized (registryCache)
 158:       {
 159:         registry = (Registry) registryCache.get(netAddress);
 160:       }
 161: 
 162:     if (registry == null)
 163:       {
 164:         // The colon, if present, indicates the start of the port number.
 165:         int colon = netAddress.lastIndexOf(':');
 166:         int port;
 167: 
 168:         try
 169:           {
 170:             if (colon >= 0)
 171:               {
 172:                 port = Integer.parseInt(netAddress.substring(colon + 1));
 173:                 netAddress = netAddress.substring(0, colon);
 174:               }
 175:             else
 176:               port = Registry.REGISTRY_PORT;
 177:           }
 178:         catch (NumberFormatException e1)
 179:           {
 180:             throw new InvalidNameException(netAddress);
 181:           }
 182: 
 183:         try
 184:           {
 185:             registry = LocateRegistry.getRegistry(netAddress, port);
 186:           }
 187:         catch (RemoteException e)
 188:           {
 189:             throw new CommunicationException(e.toString());
 190:           }
 191: 
 192:         synchronized (registryCache)
 193:           {
 194:             registryCache.put(netAddress, registry);
 195:           }
 196:       }
 197:     return registry;
 198:   }
 199: 
 200:   /**
 201:    * Create the rmi url context that works, talking with the given RMI registry.
 202:    *
 203:    * @param props
 204:    *          the properties for this context
 205:    */
 206:   public rmiURLContext(Map props)
 207:   {
 208:     properties = new Properties();
 209:     if (props != null)
 210:       properties.putAll(props);
 211:   }
 212: 
 213:   /**
 214:    * Bind the given name into this context. The .toString() is called to
 215:    * convert into the string representation, required by RMI registry.
 216:    *
 217:    * @throws NamingException if the object is not an instance of Remote
 218:    */
 219:   public void bind(Name name, Object obj) throws NamingException
 220:   {
 221:     bind(name.toString(), obj);
 222:   }
 223: 
 224:   /**
 225:    * Bind the given name into this context.
 226:    */
 227:   public void bind(String name, Object obj) throws NamingException
 228:   {
 229:     try
 230:       {
 231:         String [] n = split(name);
 232:         getRegistry(n[0]).bind(n[1], (Remote) obj);
 233:       }
 234:     catch (AccessException e)
 235:       {
 236:         throw new NamingException("access:"+e.toString());
 237:       }
 238:     catch (RemoteException e)
 239:       {
 240:         throw new CommunicationException(e.toString());
 241:       }
 242:     catch (AlreadyBoundException e)
 243:       {
 244:         throw new NameAlreadyBoundException(name);
 245:       }
 246:     catch (ClassCastException c)
 247:       {
 248:         throw new NamingException("Only Remote can be bound:"
 249:                                   + obj.getClass().getName());
 250:       }
 251:   }
 252: 
 253:   /**
 254:    * Not supported.
 255:    */
 256:   public Name composeName(Name name, Name prefix) throws NamingException
 257:   {
 258:     throw new OperationNotSupportedException();
 259:   }
 260: 
 261:   /**
 262:    * Not supported.
 263:    */
 264:   public String composeName(String name, String prefix) throws NamingException
 265:   {
 266:     throw new OperationNotSupportedException();
 267:   }
 268: 
 269:   /**
 270:    * Subcontexts are not supporte by RMI registry. The only supported case is an
 271:    * empty name (returns the cloned instance of self).
 272:    */
 273:   public Context createSubcontext(Name name) throws NamingException
 274:   {
 275:     if (name.size() == 0)
 276:       return new rmiURLContext(properties);
 277:     else
 278:       throw new OperationNotSupportedException();
 279:   }
 280: 
 281:   /**
 282:    * Subcontexts are not supporte by RMI registry. The only supported case is an
 283:    * empty name (returns the cloned instance of self).
 284:    */
 285:   public Context createSubcontext(String name) throws NamingException
 286:   {
 287:     if (name.length() == 0)
 288:       return new rmiURLContext(properties);
 289:     else
 290:       throw new OperationNotSupportedException();
 291:   }
 292: 
 293:   /**
 294:    * Subcontexts are not supporte by RMI registry.
 295:    */
 296:   public void destroySubcontext(Name name) throws NamingException
 297:   {
 298:     throw new OperationNotSupportedException();
 299:   }
 300: 
 301:   /**
 302:    * Subcontexts are not supporte by RMI registry.
 303:    */
 304:   public void destroySubcontext(String name) throws NamingException
 305:   {
 306:     throw new OperationNotSupportedException();
 307:   }
 308: 
 309:   /**
 310:    * Returns the naming service URL, same that was passed vie
 311:    * {@link Context#PROVIDER_URL}.
 312:    */
 313:   public String getNameInNamespace() throws NamingException
 314:   {
 315:     return properties.getProperty(Context.PROVIDER_URL,
 316:                                   DEFAULT_REGISTRY_LOCATION);
 317:   }
 318: 
 319:   /**
 320:    * Not supported, this context never parses any names.
 321:    */
 322:   public NameParser getNameParser(Name name) throws NamingException
 323:   {
 324:     throw new OperationNotSupportedException();
 325:   }
 326: 
 327:   /**
 328:    * Not supported, this context never parses any names.
 329:    */
 330:   public NameParser getNameParser(String name) throws NamingException
 331:   {
 332:     throw new OperationNotSupportedException();
 333:   }
 334: 
 335:   /**
 336:    * List existing bindings of this context (the parameter must be empty name,
 337:    * indicating the root context). The class name of the returned name class
 338:    * pairs is "Remote", as this "quick preview" method should probably not call
 339:    * the naming service again. Use listBindings if more details are required.
 340:    */
 341:   public NamingEnumeration list(Name name) throws NamingException
 342:   {
 343:     return list(name);
 344:   }
 345: 
 346:   /**
 347:    * List existing bindings of thie given registry.The class name of the
 348:    * returned name class pairs is "Remote", as this "quick preview" method
 349:    * should probably not call the naming service again. Use listBindings if more
 350:    * details are required.
 351:    */
 352:   public NamingEnumeration list(String name) throws NamingException
 353:   {
 354:     try
 355:       {
 356:         String [] n = split(name);
 357:         if (n[1].length() > 0)
 358:           throw new InvalidNameException(name+", the name part must be empty");
 359:         return new ListEnumeration(getRegistry(n[0]).list());
 360:       }
 361:     catch (Exception e)
 362:       {
 363:         throw new NamingException(e.toString());
 364:       }
 365:   }
 366: 
 367:   /**
 368:    * List existing bindings of this context (the parameter must be empty name,
 369:    * indicating the root context).
 370:    */
 371:   public NamingEnumeration listBindings(Name name) throws NamingException
 372:   {
 373:     return listBindings(name.toString());
 374:   }
 375: 
 376:   /**
 377:    * List existing bindings of this context.
 378:    */
 379:   public NamingEnumeration listBindings(String name) throws NamingException
 380:   {
 381:     try
 382:       {
 383:         String [] n = split(name);
 384:         if (n[1].length() > 0)
 385:           throw new InvalidNameException(name+", the name part must be empty");
 386: 
 387:         Registry r = getRegistry(n[0]);
 388:         return new ListBindingsEnumeration(r.list(), r);
 389:       }
 390:     catch (Exception e)
 391:       {
 392:         throw new NamingException(e.toString());
 393:       }
 394:   }
 395: 
 396:   /**
 397:    * Returns the naming service context under the given address, wrapped as
 398:    * Context.
 399:    */
 400:   public Object lookupLink(Name name) throws NamingException
 401:   {
 402:     return lookupLink(name.toString());
 403:   }
 404: 
 405:   /**
 406:    * Returns the naming service context under the given address,
 407:    * wrapped as Context.
 408:    */
 409:   public Object lookupLink(String name) throws NamingException
 410:   {
 411:     return new ContextContinuation(properties, getRegistry(name));
 412:   }
 413: 
 414:   /**
 415:    * Rebinds this object.
 416:    *
 417:    * @param name
 418:    *          the object name (.toString()) is used to convert into string
 419:    *          representation.
 420:    * @param obj
 421:    *          object (must be an instance of Remote).
 422:    */
 423:   public void rebind(Name name, Object obj) throws NamingException
 424:   {
 425:     rebind(name.toString(), obj);
 426:   }
 427: 
 428:   /**
 429:    * Rebinds this object.
 430:    *
 431:    * @param name
 432:    *          the object name.
 433:    * @param obj
 434:    *          object (must be an instance of Remote).
 435:    */
 436:   public void rebind(String name, Object obj) throws NamingException
 437:   {
 438:     try
 439:       {
 440:         String [] n = split(name);
 441:         getRegistry(n[0]).rebind(n[1], (Remote) obj);
 442:       }
 443:     catch (AccessException e)
 444:       {
 445:         throw new NamingException("access:"+e.toString());
 446:       }
 447:     catch (RemoteException e)
 448:       {
 449:         throw new CommunicationException(e.toString());
 450:       }
 451:     catch (ClassCastException c)
 452:       {
 453:         throw new NamingException("Only Remote can be bound:"
 454:                                   + obj.getClass().getName());
 455:       }
 456:   }
 457: 
 458:   /**
 459:    * Renames the object. If the new name is already bound in the given context,
 460:    * the {@link AlreadyBoundException} is thrown and the oldName binding is
 461:    * preserved.
 462:    */
 463:   public void rename(Name oldName, Name newName) throws NamingException
 464:   {
 465:     rename(oldName.toString(), newName.toString());
 466:   }
 467: 
 468:   /**
 469:    * Renames the object. If the new name is already bound in the given context,
 470:    * the {@link AlreadyBoundException} is thrown and the oldName binding is
 471:    * preserved.
 472:    */
 473:   public synchronized void rename(String oldName, String newName)
 474:       throws NamingException
 475:   {
 476:     try
 477:       {
 478:         String [] n = split(oldName);
 479:         Registry r = getRegistry(n[0]);
 480:         Remote object = r.lookup(n[1]);
 481:         r.unbind(oldName);
 482:         try
 483:           {
 484:             String [] n2 = split(newName);
 485:             Registry r2 = getRegistry(n2[0]);
 486:             r2.bind(n2[1], object);
 487:           }
 488:         catch (AlreadyBoundException e)
 489:           {
 490:             // Bind it back.
 491:             try
 492:               {
 493:                 r.bind(oldName, object);
 494:               }
 495:             catch (AlreadyBoundException e1)
 496:               {
 497:                 // We have just removed this name.
 498:                 throw new InternalError();
 499:               }
 500:             throw new NameAlreadyBoundException(newName);
 501:           }
 502:       }
 503:     catch (AccessException e)
 504:       {
 505:         throw new NamingException(e.toString());
 506:       }
 507:     catch (RemoteException e)
 508:       {
 509:         throw new CommunicationException(e.toString());
 510:       }
 511:     catch (NotBoundException e)
 512:       {
 513:         throw new CommunicationException(e.toString());
 514:       }
 515:   }
 516: 
 517:   /**
 518:    * Unbind the object.
 519:    */
 520:   public void unbind(Name name) throws NamingException
 521:   {
 522:     unbind(name.toString());
 523:   }
 524: 
 525:   /**
 526:    * Unbind the object.
 527:    */
 528:   public void unbind(String name) throws NamingException
 529:   {
 530:     try
 531:       {
 532:         String [] n = split(name);
 533:         getRegistry(n[0]).unbind(n[1]);
 534:       }
 535:     catch (AccessException e)
 536:       {
 537:         throw new NamingException(e.toString());
 538:       }
 539:     catch (RemoteException e)
 540:       {
 541:         throw new CommunicationException(e.toString());
 542:       }
 543:     catch (NotBoundException e)
 544:       {
 545:         throw new CommunicationException(e.toString());
 546:       }
 547:   }
 548: 
 549:   /**
 550:    * Release the associated resources.
 551:    */
 552:   public void close() throws NamingException
 553:   {
 554:   }
 555: 
 556:   /**
 557:    * Resolve the object by name.
 558:    *
 559:    * @param name
 560:    *          the object name, .toString() is used to get the string
 561:    *          representation.
 562:    */
 563:   public Object lookup(Name name) throws NamingException
 564:   {
 565:     return lookup(name.toString());
 566:   }
 567: 
 568:   /**
 569:    * Resolve the object by name
 570:    *
 571:    * @param name the object name.
 572:    */
 573:   public Object lookup(String name) throws NamingException
 574:   {
 575:     try
 576:       {
 577:         String [] n = split(name);
 578:         return getRegistry(n[0]).lookup(n[1]);
 579:       }
 580:     catch (AccessException e)
 581:       {
 582:         throw new NamingException(e.toString());
 583:       }
 584:     catch (RemoteException e)
 585:       {
 586:         throw new CommunicationException(e.toString());
 587:       }
 588:     catch (NotBoundException e)
 589:       {
 590:         throw new NameNotFoundException(name);
 591:       }
 592:   }
 593: 
 594:   /**
 595:    * Split the given rmi address into the network address and naming service
 596:    * name.
 597:    *
 598:    * @param address
 599:    *          the address to split
 600:    * @return the two member array, lower being the network address of the naming
 601:    *         service and upper being the naming service name
 602:    * @throws NamingException
 603:    *           if the name is invalid
 604:    */
 605:   public String[] split(String address) throws NamingException
 606:   {
 607:     // The format like rmi://localhost:1099/name is expected. Parse.
 608:     if (!address.startsWith("rmi://"))
 609:       throw new InvalidNameException(
 610:                                      address
 611:                                          + " should be like 'rmi://localhost:1099/name'");
 612: 
 613:     String a = address.substring("rmi://".length());
 614: 
 615:     // The suffix starts after the first slash from the rmi://
 616:     int sfx = a.indexOf('/');
 617: 
 618:     // Handle the possible escape
 619:     while (sfx > 0 && a.charAt(sfx - 1) == '\\')
 620:       sfx = a.indexOf('/', sfx + 1);
 621: 
 622:     String net;
 623:     String name;
 624:     if (sfx >= 0)
 625:       {
 626:         net = a.substring(0, sfx);
 627:         name = a.substring(sfx + 1);
 628:       }
 629:     else
 630:       {
 631:         net = a;
 632:         name = "";
 633:       }
 634: 
 635:     return new String[] { net, name };
 636:   }
 637: }