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

   1: /* ContextContinuation.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: 
  51: import javax.naming.CommunicationException;
  52: import javax.naming.Context;
  53: import javax.naming.InvalidNameException;
  54: import javax.naming.Name;
  55: import javax.naming.NameAlreadyBoundException;
  56: import javax.naming.NameNotFoundException;
  57: import javax.naming.NameParser;
  58: import javax.naming.NamingEnumeration;
  59: import javax.naming.NamingException;
  60: import javax.naming.OperationNotSupportedException;
  61: 
  62: /**
  63:  * The implementation of the RMI URL context. This context connects
  64:  *
  65:  * @author Audrius Meskauskas
  66:  */
  67: public class ContextContinuation implements Context
  68: {
  69:   /**
  70:    * The default registry location.
  71:    */
  72:   public static final String DEFAULT_REGISTRY_LOCATION = "rmi://localhost:1099";
  73: 
  74:   /**
  75:    * The local or remote RMI registry, performing the actual work for this
  76:    * context.
  77:    */
  78:   Registry registry;
  79: 
  80:  /**
  81:    * The properties.
  82:    */
  83:   Properties properties;
  84: 
  85:   /**
  86:    * The flag, indicating, that the lookup methods were called before.
  87:    * If the lookup methods were called before, the existing ORB cannot be
  88:    * destroyed, as references to the existing objects will become
  89:    * unfunctional.
  90:    */
  91:   boolean lookupCalled;
  92: 
  93:   /**
  94:    * Add new environment property to the environment of this context. Both name
  95:    * and value of the new property must not be null. If the property is already
  96:    * defined, is current value is replaced by the propVal. This method replaces
  97:    * the registry. The new registry will be lazily instantiated on the first
  98:    * call.
  99:    *
 100:    * @param key
 101:    *          the name of the new property
 102:    * @param value
 103:    *          the value of the new property
 104:    * @return the previous value of this property or null if the property has not
 105:    *         been previously defined
 106:    */
 107:   public Object addToEnvironment(String key, Object value)
 108:   {
 109:     removeRegistry();
 110:     if (key == null || value == null)
 111:       throw new NullPointerException();
 112:     return properties.put(key, value);
 113:   }
 114: 
 115:   /**
 116:    * Returns the environment, associated with this naming context. The returned
 117:    * table should never be modified by the caller (the registry would not be updated
 118:    * in such case). Use {@link #addToEnvironment} and
 119:    * {@link #removeFromEnvironment} to modify the environement, if needed.
 120:    *
 121:    * @return the table, representing the environment of this context
 122:    * @throws NamingException
 123:    */
 124:   public Hashtable getEnvironment() throws NamingException
 125:   {
 126:     return properties;
 127:   }
 128: 
 129:   /**
 130:    * Removes the property with the given name from the environment. Returns
 131:    * without action if this property is not defined. Replaces the ORB,
 132:    * constructing the new ORB with the changes set of properties (you can
 133:    * replace the CORBA implementation provider, for instance). The new ORB will
 134:    * be lazily instantiated on the first call.
 135:    *
 136:    * @param propName
 137:    *          the name of the property being removed.
 138:    * @return the value of the property that has been removed or null if the
 139:    *         property was not defined.
 140:    * @throws NamingException
 141:    */
 142:   public Object removeFromEnvironment(String propName) throws NamingException
 143:   {
 144:     removeRegistry();
 145:     return properties.remove(propName);
 146:   }
 147: 
 148:   /**
 149:    * Remove the current registry reference.
 150:    */
 151:   public void removeRegistry()
 152:   {
 153:     registry = null;
 154:   }
 155: 
 156:   /**
 157:    * Get the cached or new registry reference.
 158:    *
 159:    * @return the registry reference, either cached or new.
 160:    */
 161:   public Registry getRegistry() throws NamingException
 162:   {
 163:     if (registry == null)
 164:       {
 165:         String address = properties.getProperty(Context.PROVIDER_URL,
 166:                                                 DEFAULT_REGISTRY_LOCATION);
 167: 
 168:         // The format like rmi://localhost:1099 is expected. Parse.
 169:         if (!address.startsWith("rmi://"))
 170:           throw new InvalidNameException(address);
 171: 
 172:         String a = address.substring("rmi://".length());
 173: 
 174:         // The colon, if present, indicates the start of the port number.
 175:         int colon = a.lastIndexOf(':');
 176:         int port;
 177: 
 178:         try
 179:           {
 180:             if (colon >=0)
 181:               {
 182:                 port = Integer.parseInt(a.substring(colon+1));
 183:                 a = a.substring(0, colon);
 184:               }
 185:             else
 186:               port = Registry.REGISTRY_PORT;
 187:           }
 188:         catch (NumberFormatException e1)
 189:           {
 190:             throw new InvalidNameException(address);
 191:           }
 192: 
 193:         try
 194:           {
 195:             registry = LocateRegistry.getRegistry(a, port);
 196:           }
 197:         catch (RemoteException e)
 198:           {
 199:             throw new CommunicationException(e.toString());
 200:           }
 201:       }
 202:     return registry;
 203:   }
 204: 
 205:   /**
 206:    * Create the rmi url context that works, talking with the given RMI registry.
 207:    *
 208:    * @param props
 209:    *          the properties for this context
 210:    * @param initialRegistry
 211:    *          the initial value of the registry
 212:    */
 213:   public ContextContinuation(Map props, Registry initialRegistry)
 214:   {
 215:     properties = new Properties();
 216:     if (props != null)
 217:       properties.putAll(props);
 218:     registry = initialRegistry;
 219:   }
 220: 
 221:   /**
 222:    * Bind the given name into this context. The .toString() is called to
 223:    * convert into the string representation, required by RMI registry.
 224:    *
 225:    * @throws NamingException if the object is not an instance of Remote
 226:    */
 227:   public void bind(Name name, Object obj) throws NamingException
 228:   {
 229:     bind(name.toString(), obj);
 230:   }
 231: 
 232:   /**
 233:    * Bind the given name into this context.
 234:    */
 235:   public void bind(String name, Object obj) throws NamingException
 236:   {
 237:     try
 238:       {
 239:         getRegistry().bind(name, (Remote) obj);
 240:       }
 241:     catch (AccessException e)
 242:       {
 243:         throw new NamingException("access:"+e.toString());
 244:       }
 245:     catch (RemoteException e)
 246:       {
 247:         throw new CommunicationException(e.toString());
 248:       }
 249:     catch (AlreadyBoundException e)
 250:       {
 251:         throw new NameAlreadyBoundException(name);
 252:       }
 253:     catch (ClassCastException c)
 254:       {
 255:         throw new NamingException("Only Remote can be bound:"
 256:                                   + obj.getClass().getName());
 257:       }
 258:   }
 259: 
 260:   /**
 261:    * Not supported.
 262:    */
 263:   public Name composeName(Name name, Name prefix) throws NamingException
 264:   {
 265:     throw new OperationNotSupportedException();
 266:   }
 267: 
 268:   /**
 269:    * Not supported.
 270:    */
 271:   public String composeName(String name, String prefix) throws NamingException
 272:   {
 273:     throw new OperationNotSupportedException();
 274:   }
 275: 
 276:   /**
 277:    * Subcontexts are not supporte by RMI registry. The only supported case is an
 278:    * empty name (returns the cloned instance of self).
 279:    */
 280:   public Context createSubcontext(Name name) throws NamingException
 281:   {
 282:     if (name.size() == 0)
 283:       return new rmiURLContext(properties);
 284:     else
 285:       throw new OperationNotSupportedException();
 286:   }
 287: 
 288:   /**
 289:    * Subcontexts are not supporte by RMI registry. The only supported case is an
 290:    * empty name (returns the cloned instance of self).
 291:    */
 292:   public Context createSubcontext(String name) throws NamingException
 293:   {
 294:     if (name.length() == 0)
 295:       return new rmiURLContext(properties);
 296:     else
 297:       throw new OperationNotSupportedException();
 298:   }
 299: 
 300:   /**
 301:    * Subcontexts are not supporte by RMI registry.
 302:    */
 303:   public void destroySubcontext(Name name) throws NamingException
 304:   {
 305:     throw new OperationNotSupportedException();
 306:   }
 307: 
 308:   /**
 309:    * Subcontexts are not supporte by RMI registry.
 310:    */
 311:   public void destroySubcontext(String name) throws NamingException
 312:   {
 313:     throw new OperationNotSupportedException();
 314:   }
 315: 
 316:   /**
 317:    * Returns the naming service URL, same that was passed vie
 318:    * {@link Context#PROVIDER_URL}.
 319:    */
 320:   public String getNameInNamespace() throws NamingException
 321:   {
 322:     return properties.getProperty(Context.PROVIDER_URL,
 323:                                   DEFAULT_REGISTRY_LOCATION);
 324:   }
 325: 
 326:   /**
 327:    * Not supported, this context never parses any names.
 328:    */
 329:   public NameParser getNameParser(Name name) throws NamingException
 330:   {
 331:     throw new OperationNotSupportedException();
 332:   }
 333: 
 334:   /**
 335:    * Not supported, this context never parses any names.
 336:    */
 337:   public NameParser getNameParser(String name) throws NamingException
 338:   {
 339:     throw new OperationNotSupportedException();
 340:   }
 341: 
 342:   /**
 343:    * List existing bindings of this context (the parameter must be empty name,
 344:    * indicating the root context). The class name of the returned name class
 345:    * pairs is "Remote", as this "quick preview" method should probably not call
 346:    * the naming service again. Use listBindings if more details are required.
 347:    */
 348:   public NamingEnumeration list(Name name) throws NamingException
 349:   {
 350:     if (name.size() > 0)
 351:       throw new OperationNotSupportedException("Only empty name is accepted");
 352:     return list("");
 353:   }
 354: 
 355:   /**
 356:    * List existing bindings of this context (the parameter must be empty string,
 357:    * indicating the root context). The class name of the returned name class
 358:    * pairs is "Remote", as this "quick preview" method should probably not call
 359:    * the naming service again. Use listBindings if more details are required.
 360:    */
 361:   public NamingEnumeration list(String name) throws NamingException
 362:   {
 363:     if (name.length() > 0)
 364:       throw new OperationNotSupportedException("Only empty name is accepted");
 365: 
 366:     try
 367:       {
 368:         return new ListEnumeration(getRegistry().list());
 369:       }
 370:     catch (Exception e)
 371:       {
 372:         throw new NamingException(e.toString());
 373:       }
 374:   }
 375: 
 376:   /**
 377:    * List existing bindings of this context (the parameter must be empty name,
 378:    * indicating the root context).
 379:    */
 380:   public NamingEnumeration listBindings(Name name) throws NamingException
 381:   {
 382:     if (name.size() > 0)
 383:       throw new OperationNotSupportedException("Only empty name is accepted");
 384:     return listBindings("");
 385:   }
 386: 
 387:   /**
 388:    * List existing bindings of this context (the parameter must be empty name,
 389:    * indicating the root context).
 390:    */
 391:   public NamingEnumeration listBindings(String name) throws NamingException
 392:   {
 393:     if (name.length() > 0)
 394:       throw new OperationNotSupportedException("Only empty name is accepted");
 395: 
 396:     try
 397:       {
 398:         Registry r = getRegistry();
 399:         return new ListBindingsEnumeration(r.list(), r);
 400:       }
 401:     catch (Exception e)
 402:       {
 403:         throw new NamingException(e.toString());
 404:       }
 405:   }
 406: 
 407:   /**
 408:    * Not supported.
 409:    */
 410:   public Object lookupLink(Name name) throws NamingException
 411:   {
 412:     throw new OperationNotSupportedException();
 413:   }
 414: 
 415:   /**
 416:    * Not supported.
 417:    */
 418:   public Object lookupLink(String name) throws NamingException
 419:   {
 420:     throw new OperationNotSupportedException();
 421:   }
 422: 
 423:   /**
 424:    * Rebinds this object.
 425:    *
 426:    * @param name
 427:    *          the object name (.toString()) is used to convert into string
 428:    *          representation.
 429:    * @param obj
 430:    *          object (must be an instance of Remote).
 431:    */
 432:   public void rebind(Name name, Object obj) throws NamingException
 433:   {
 434:     rebind(name.toString(), obj);
 435:   }
 436: 
 437:   /**
 438:    * Rebinds this object.
 439:    *
 440:    * @param name
 441:    *          the object name.
 442:    * @param obj
 443:    *          object (must be an instance of Remote).
 444:    */
 445:   public void rebind(String name, Object obj) throws NamingException
 446:   {
 447:     try
 448:       {
 449:         getRegistry().rebind(name, (Remote) obj);
 450:       }
 451:     catch (AccessException e)
 452:       {
 453:         throw new NamingException("access:"+e.toString());
 454:       }
 455:     catch (RemoteException e)
 456:       {
 457:         throw new CommunicationException(e.toString());
 458:       }
 459:     catch (ClassCastException c)
 460:       {
 461:         throw new NamingException("Only Remote can be bound:"
 462:                                   + obj.getClass().getName());
 463:       }
 464:   }
 465: 
 466:   /**
 467:    * Renames the object. If the new name is already bound in the given context,
 468:    * the {@link AlreadyBoundException} is thrown and the oldName binding is
 469:    * preserved.
 470:    */
 471:   public void rename(Name oldName, Name newName) throws NamingException
 472:   {
 473:     rename(oldName.toString(), newName.toString());
 474:   }
 475: 
 476:   /**
 477:    * Renames the object. If the new name is already bound in the given context,
 478:    * the {@link AlreadyBoundException} is thrown and the oldName binding is
 479:    * preserved.
 480:    */
 481:   public synchronized void rename(String oldName, String newName)
 482:       throws NamingException
 483:   {
 484:     try
 485:       {
 486:         Registry r = getRegistry();
 487:         Remote object = r.lookup(oldName);
 488:         r.unbind(oldName);
 489:         try
 490:           {
 491:             r.bind(newName, object);
 492:           }
 493:         catch (AlreadyBoundException e)
 494:           {
 495:             // Bind it back.
 496:             try
 497:               {
 498:                 r.bind(oldName, object);
 499:               }
 500:             catch (AlreadyBoundException e1)
 501:               {
 502:                 // We have just removed this name.
 503:                 throw new InternalError();
 504:               }
 505:             throw new NameAlreadyBoundException(newName);
 506:           }
 507:       }
 508:     catch (AccessException e)
 509:       {
 510:         throw new NamingException(e.toString());
 511:       }
 512:     catch (RemoteException e)
 513:       {
 514:         throw new CommunicationException(e.toString());
 515:       }
 516:     catch (NotBoundException e)
 517:       {
 518:         throw new CommunicationException(e.toString());
 519:       }
 520:   }
 521: 
 522:   /**
 523:    * Unbind the object.
 524:    */
 525:   public void unbind(Name name) throws NamingException
 526:   {
 527:     unbind(name.toString());
 528:   }
 529: 
 530:   /**
 531:    * Unbind the object.
 532:    */
 533:   public void unbind(String name) throws NamingException
 534:   {
 535:     try
 536:       {
 537:         getRegistry().unbind(name);
 538:       }
 539:     catch (AccessException e)
 540:       {
 541:         throw new NamingException(e.toString());
 542:       }
 543:     catch (RemoteException e)
 544:       {
 545:         throw new CommunicationException(e.toString());
 546:       }
 547:     catch (NotBoundException e)
 548:       {
 549:         throw new CommunicationException(e.toString());
 550:       }
 551:   }
 552: 
 553:   /**
 554:    * Release the associated resources.
 555:    */
 556:   public void close() throws NamingException
 557:   {
 558:     removeRegistry();
 559:   }
 560: 
 561:   /**
 562:    * Resolve the object by name.
 563:    *
 564:    * @param name
 565:    *          the object name, .toString() is used to get the string
 566:    *          representation.
 567:    */
 568:   public Object lookup(Name name) throws NamingException
 569:   {
 570:     return lookup(name.toString());
 571:   }
 572: 
 573:   /**
 574:    * Resolve the object by name
 575:    *
 576:    * @param name the object name.
 577:    */
 578:   public Object lookup(String name) throws NamingException
 579:   {
 580:     try
 581:       {
 582:         return getRegistry().lookup(name);
 583:       }
 584:     catch (AccessException e)
 585:       {
 586:         throw new NamingException(e.toString());
 587:       }
 588:     catch (RemoteException e)
 589:       {
 590:         throw new CommunicationException(e.toString());
 591:       }
 592:     catch (NotBoundException e)
 593:       {
 594:         throw new NameNotFoundException(name);
 595:       }
 596:   }
 597: }