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

   1: /* RmiContinuation.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 RmiContinuation 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:    */
 211:   public RmiContinuation(Map props)
 212:   {
 213:     properties = new Properties();
 214:     if (props != null)
 215:       properties.putAll(props);
 216:   }
 217: 
 218:   /**
 219:    * Bind the given name into this context. The .toString() is called to
 220:    * convert into the string representation, required by RMI registry.
 221:    *
 222:    * @throws NamingException if the object is not an instance of Remote
 223:    */
 224:   public void bind(Name name, Object obj) throws NamingException
 225:   {
 226:     bind(name.toString(), obj);
 227:   }
 228: 
 229:   /**
 230:    * Bind the given name into this context.
 231:    */
 232:   public void bind(String name, Object obj) throws NamingException
 233:   {
 234:     try
 235:       {
 236:         getRegistry().bind(name, (Remote) obj);
 237:       }
 238:     catch (AccessException e)
 239:       {
 240:         throw new NamingException("access:"+e.toString());
 241:       }
 242:     catch (RemoteException e)
 243:       {
 244:         throw new CommunicationException(e.toString());
 245:       }
 246:     catch (AlreadyBoundException e)
 247:       {
 248:         throw new NameAlreadyBoundException(name);
 249:       }
 250:     catch (ClassCastException c)
 251:       {
 252:         throw new NamingException("Only Remote can be bound:"
 253:                                   + obj.getClass().getName());
 254:       }
 255:   }
 256: 
 257:   /**
 258:    * Not supported.
 259:    */
 260:   public Name composeName(Name name, Name prefix) throws NamingException
 261:   {
 262:     throw new OperationNotSupportedException();
 263:   }
 264: 
 265:   /**
 266:    * Not supported.
 267:    */
 268:   public String composeName(String name, String prefix) throws NamingException
 269:   {
 270:     throw new OperationNotSupportedException();
 271:   }
 272: 
 273:   /**
 274:    * Subcontexts are not supporte by RMI registry. The only supported case is an
 275:    * empty name (returns the cloned instance of self).
 276:    */
 277:   public Context createSubcontext(Name name) throws NamingException
 278:   {
 279:     if (name.size() == 0)
 280:       return new RmiContinuation(properties);
 281:     else
 282:       throw new OperationNotSupportedException();
 283:   }
 284: 
 285:   /**
 286:    * Subcontexts are not supporte by RMI registry. The only supported case is an
 287:    * empty name (returns the cloned instance of self).
 288:    */
 289:   public Context createSubcontext(String name) throws NamingException
 290:   {
 291:     if (name.length() == 0)
 292:       return new RmiContinuation(properties);
 293:     else
 294:       throw new OperationNotSupportedException();
 295:   }
 296: 
 297:   /**
 298:    * Subcontexts are not supporte by RMI registry.
 299:    */
 300:   public void destroySubcontext(Name name) throws NamingException
 301:   {
 302:     throw new OperationNotSupportedException();
 303:   }
 304: 
 305:   /**
 306:    * Subcontexts are not supporte by RMI registry.
 307:    */
 308:   public void destroySubcontext(String name) throws NamingException
 309:   {
 310:     throw new OperationNotSupportedException();
 311:   }
 312: 
 313:   /**
 314:    * Returns the naming service URL, same that was passed vie
 315:    * {@link Context#PROVIDER_URL}.
 316:    */
 317:   public String getNameInNamespace() throws NamingException
 318:   {
 319:     return properties.getProperty(Context.PROVIDER_URL,
 320:                                   DEFAULT_REGISTRY_LOCATION);
 321:   }
 322: 
 323:   /**
 324:    * Not supported, this context never parses any names.
 325:    */
 326:   public NameParser getNameParser(Name name) throws NamingException
 327:   {
 328:     throw new OperationNotSupportedException();
 329:   }
 330: 
 331:   /**
 332:    * Not supported, this context never parses any names.
 333:    */
 334:   public NameParser getNameParser(String name) throws NamingException
 335:   {
 336:     throw new OperationNotSupportedException();
 337:   }
 338: 
 339:   /**
 340:    * List existing bindings of this context (the parameter must be empty name,
 341:    * indicating the root context). The class name of the returned name class
 342:    * pairs is "Remote", as this "quick preview" method should probably not call
 343:    * the naming service again. Use listBindings if more details are required.
 344:    */
 345:   public NamingEnumeration list(Name name) throws NamingException
 346:   {
 347:     if (name.size() > 0)
 348:       throw new OperationNotSupportedException("Only empty name is accepted");
 349:     return list("");
 350:   }
 351: 
 352:   /**
 353:    * List existing bindings of this context (the parameter must be empty string,
 354:    * indicating the root context). The class name of the returned name class
 355:    * pairs is "Remote", as this "quick preview" method should probably not call
 356:    * the naming service again. Use listBindings if more details are required.
 357:    */
 358:   public NamingEnumeration list(String name) throws NamingException
 359:   {
 360:     if (name.length() > 0)
 361:       throw new OperationNotSupportedException("Only empty name is accepted");
 362: 
 363:     try
 364:       {
 365:         return new ListEnumeration(getRegistry().list());
 366:       }
 367:     catch (Exception e)
 368:       {
 369:         throw new NamingException(e.toString());
 370:       }
 371:   }
 372: 
 373:   /**
 374:    * List existing bindings of this context (the parameter must be empty name,
 375:    * indicating the root context).
 376:    */
 377:   public NamingEnumeration listBindings(Name name) throws NamingException
 378:   {
 379:     if (name.size() > 0)
 380:       throw new OperationNotSupportedException("Only empty name is accepted");
 381:     return listBindings("");
 382:   }
 383: 
 384:   /**
 385:    * List existing bindings of this context (the parameter must be empty name,
 386:    * indicating the root context).
 387:    */
 388:   public NamingEnumeration listBindings(String name) throws NamingException
 389:   {
 390:     if (name.length() > 0)
 391:       throw new OperationNotSupportedException("Only empty name is accepted");
 392: 
 393:     try
 394:       {
 395:         Registry r = getRegistry();
 396:         return new ListBindingsEnumeration(r.list(), r);
 397:       }
 398:     catch (Exception e)
 399:       {
 400:         throw new NamingException(e.toString());
 401:       }
 402:   }
 403: 
 404:   /**
 405:    * Not supported.
 406:    */
 407:   public Object lookupLink(Name name) throws NamingException
 408:   {
 409:     throw new OperationNotSupportedException();
 410:   }
 411: 
 412:   /**
 413:    * Not supported.
 414:    */
 415:   public Object lookupLink(String name) throws NamingException
 416:   {
 417:     throw new OperationNotSupportedException();
 418:   }
 419: 
 420:   /**
 421:    * Rebinds this object.
 422:    *
 423:    * @param name
 424:    *          the object name (.toString()) is used to convert into string
 425:    *          representation.
 426:    * @param obj
 427:    *          object (must be an instance of Remote).
 428:    */
 429:   public void rebind(Name name, Object obj) throws NamingException
 430:   {
 431:     rebind(name.toString(), obj);
 432:   }
 433: 
 434:   /**
 435:    * Rebinds this object.
 436:    *
 437:    * @param name
 438:    *          the object name.
 439:    * @param obj
 440:    *          object (must be an instance of Remote).
 441:    */
 442:   public void rebind(String name, Object obj) throws NamingException
 443:   {
 444:     try
 445:       {
 446:         getRegistry().rebind(name, (Remote) obj);
 447:       }
 448:     catch (AccessException e)
 449:       {
 450:         throw new NamingException("access:"+e.toString());
 451:       }
 452:     catch (RemoteException e)
 453:       {
 454:         throw new CommunicationException(e.toString());
 455:       }
 456:     catch (ClassCastException c)
 457:       {
 458:         throw new NamingException("Only Remote can be bound:"
 459:                                   + obj.getClass().getName());
 460:       }
 461:   }
 462: 
 463:   /**
 464:    * Renames the object. If the new name is already bound in the given context,
 465:    * the {@link AlreadyBoundException} is thrown and the oldName binding is
 466:    * preserved.
 467:    */
 468:   public void rename(Name oldName, Name newName) throws NamingException
 469:   {
 470:     rename(oldName.toString(), newName.toString());
 471:   }
 472: 
 473:   /**
 474:    * Renames the object. If the new name is already bound in the given context,
 475:    * the {@link AlreadyBoundException} is thrown and the oldName binding is
 476:    * preserved.
 477:    */
 478:   public synchronized void rename(String oldName, String newName)
 479:       throws NamingException
 480:   {
 481:     try
 482:       {
 483:         Registry r = getRegistry();
 484:         Remote object = r.lookup(oldName);
 485:         r.unbind(oldName);
 486:         try
 487:           {
 488:             r.bind(newName, object);
 489:           }
 490:         catch (AlreadyBoundException e)
 491:           {
 492:             // Bind it back.
 493:             try
 494:               {
 495:                 r.bind(oldName, object);
 496:               }
 497:             catch (AlreadyBoundException e1)
 498:               {
 499:                 // We have just removed this name.
 500:                 throw new InternalError();
 501:               }
 502:             throw new NameAlreadyBoundException(newName);
 503:           }
 504:       }
 505:     catch (AccessException e)
 506:       {
 507:         throw new NamingException(e.toString());
 508:       }
 509:     catch (RemoteException e)
 510:       {
 511:         throw new CommunicationException(e.toString());
 512:       }
 513:     catch (NotBoundException e)
 514:       {
 515:         throw new CommunicationException(e.toString());
 516:       }
 517:   }
 518: 
 519:   /**
 520:    * Unbind the object.
 521:    */
 522:   public void unbind(Name name) throws NamingException
 523:   {
 524:     unbind(name.toString());
 525:   }
 526: 
 527:   /**
 528:    * Unbind the object.
 529:    */
 530:   public void unbind(String name) throws NamingException
 531:   {
 532:     try
 533:       {
 534:         getRegistry().unbind(name);
 535:       }
 536:     catch (AccessException e)
 537:       {
 538:         throw new NamingException(e.toString());
 539:       }
 540:     catch (RemoteException e)
 541:       {
 542:         throw new CommunicationException(e.toString());
 543:       }
 544:     catch (NotBoundException e)
 545:       {
 546:         throw new CommunicationException(e.toString());
 547:       }
 548:   }
 549: 
 550:   /**
 551:    * Release the associated resources.
 552:    */
 553:   public void close() throws NamingException
 554:   {
 555:     removeRegistry();
 556:   }
 557: 
 558:   /**
 559:    * Resolve the object by name.
 560:    *
 561:    * @param name
 562:    *          the object name, .toString() is used to get the string
 563:    *          representation.
 564:    */
 565:   public Object lookup(Name name) throws NamingException
 566:   {
 567:     return lookup(name.toString());
 568:   }
 569: 
 570:   /**
 571:    * Resolve the object by name
 572:    *
 573:    * @param name the object name.
 574:    */
 575:   public Object lookup(String name) throws NamingException
 576:   {
 577:     try
 578:       {
 579:         return getRegistry().lookup(name);
 580:       }
 581:     catch (AccessException e)
 582:       {
 583:         throw new NamingException(e.toString());
 584:       }
 585:     catch (RemoteException e)
 586:       {
 587:         throw new CommunicationException(e.toString());
 588:       }
 589:     catch (NotBoundException e)
 590:       {
 591:         throw new NameNotFoundException(name);
 592:       }
 593:   }
 594: }