Source for gnu.javax.naming.giop.CorbalocParser

   1: /* CorbalocParser.java -- handles corbaname: urls
   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.giop;
  39: 
  40: import gnu.CORBA.IOR;
  41: import gnu.CORBA.Minor;
  42: import gnu.CORBA.Unexpected;
  43: import gnu.CORBA.Version;
  44: import gnu.CORBA.NamingService.NameTransformer;
  45: 
  46: import gnu.java.lang.CPStringBuilder;
  47: 
  48: import java.io.File;
  49: import java.io.FileReader;
  50: import java.io.IOException;
  51: import java.io.InputStreamReader;
  52: import java.io.UnsupportedEncodingException;
  53: import java.net.MalformedURLException;
  54: import java.net.URL;
  55: import java.net.URLDecoder;
  56: import java.util.StringTokenizer;
  57: 
  58: import javax.naming.InvalidNameException;
  59: 
  60: import org.omg.CORBA.BAD_PARAM;
  61: import org.omg.CORBA.DATA_CONVERSION;
  62: import org.omg.CORBA.ORB;
  63: import org.omg.CORBA.Object;
  64: import org.omg.CORBA.ORBPackage.InvalidName;
  65: 
  66: /**
  67:  * Parses the alternative IOR representations into our IOR structure.
  68:  *
  69:  * TODO This parser currently supports only one address per target string. A
  70:  * string with the multiple addresses will be accepted, but only the last
  71:  * address will be taken into consideration. The fault tolerance is not yet
  72:  * implemented.
  73:  *
  74:  * The key string is filtered using {@link java.net.URLDecoder} that replaces
  75:  * the agreed escape sequences by the corresponding non alphanumeric characters.
  76:  *
  77:  * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
  78:  */
  79: public class CorbalocParser
  80:   extends NameTransformer
  81: {
  82:   /**
  83:    * The corbaloc prefix.
  84:    */
  85:   public static final String pxCORBALOC = "corbaloc";
  86: 
  87:   /**
  88:    * The corbaname prefix.
  89:    */
  90:   public static final String pxCORBANAME = "corbaname";
  91: 
  92:   /**
  93:    * The IOR prefix.
  94:    */
  95:   public static final String pxIOR = "ior";
  96: 
  97:   /**
  98:    * The file:// prefix.
  99:    */
 100:   public static final String pxFILE = "file://";
 101: 
 102:   /**
 103:    * The ftp:// prefix.
 104:    */
 105:   public static final String pxFTP = "ftp://";
 106: 
 107:   /**
 108:    * The http:// prefix.
 109:    */
 110:   public static final String pxHTTP = "http://";
 111: 
 112:   /**
 113:    * Marks iiop protocol.
 114:    */
 115:   public static final String IIOP = "iiop";
 116: 
 117:   /**
 118:    * Marks rir protocol.
 119:    */
 120:   public static final String RIR = "rir";
 121: 
 122:   /**
 123:    * The default port value, as specified in OMG documentation.
 124:    */
 125:   public static final int DEFAULT_PORT = 2809;
 126: 
 127:   /**
 128:    * The default name.
 129:    */
 130:   public static final String DEFAULT_NAME = "NameService";
 131: 
 132:   /**
 133:    * The string to name converter, initialized on demand.
 134:    */
 135:   static NameTransformer converter;
 136: 
 137:   /**
 138:    * The current position.
 139:    */
 140:   int p;
 141: 
 142:   /**
 143:    * The address being parsed, splitted into tokens.
 144:    */
 145:   String[] t;
 146: 
 147:   /**
 148:    * Parse CORBALOC.
 149:    *
 150:    * The expected format is: <br>
 151:    * 1. corbaloc:[iiop][version.subversion@]:host[:port]/key <br>
 152:    * 2. corbaloc:rir:[/key] <br>
 153:    * 3. corbaname:[iiop][version.subversion@]:host[:port]/key <br>
 154:    * 4. corbaname:rir:[/key] <br>
 155:    * 5. file://[file name]<br>
 156:    * 6. http://[url]<br>
 157:    * 7. ftp://[url]<br>
 158:    *
 159:    * Protocol defaults to IOP, the object key defaults to the NameService.
 160:    *
 161:    * @param corbaloc the string to parse.
 162:    * @param orb the ORB, needed to create IORs and resolve rir references.
 163:    *
 164:    * @return the arrey of strings, first member being the IOR of the
 165:    * naming service, second member the name in the naming service.
 166:    */
 167:   public synchronized String[] corbaloc(String corbaloc,
 168:     ORB orb)
 169:     throws InvalidNameException
 170:   {
 171:     return corbaloc(corbaloc, orb, 0);
 172:   }
 173: 
 174:   /**
 175:    * Parse controlling against the infinite recursion loop.
 176:    */
 177:   private String[] corbaloc(String corbaloc,
 178:     ORB orb, int recursion) throws InvalidNameException
 179:   {
 180:     // The used CORBA specification does not state how many times we should to
 181:     //redirect, but the infinite loop may be used to knock out the system.
 182:     // by malicious attempt.
 183:     if (recursion > 10)
 184:       throw new DATA_CONVERSION("More than 10 redirections");
 185: 
 186:     if (corbaloc.startsWith(pxFILE))
 187:       return corbaloc(readFile(corbaloc.substring(pxFILE.length())), orb, recursion+1);
 188:     else if (corbaloc.startsWith(pxHTTP))
 189:       return corbaloc(readUrl(corbaloc), orb, recursion+1);
 190:     else if (corbaloc.startsWith(pxFTP))
 191:       return corbaloc(readUrl(corbaloc), orb, recursion+1);
 192: 
 193:     // The version numbers with default values.
 194:     int major = 1;
 195:     int minor = 0;
 196: 
 197:     // The host address.
 198:     String host;
 199: 
 200:     // The port.
 201:     int port = DEFAULT_PORT;
 202: 
 203:     // The object key as string.
 204:     String key;
 205: 
 206:     StringTokenizer st = new StringTokenizer(corbaloc, ":@/.,#", true);
 207: 
 208:     t = new String[st.countTokens()];
 209: 
 210:     for (int i = 0; i < t.length; i++)
 211:       {
 212:         t[i] = st.nextToken();
 213:       }
 214: 
 215:     p = 0;
 216: 
 217:     if (!t[p].startsWith(pxCORBANAME))
 218:       throw new InvalidNameException(corbaloc+" must start with "+pxCORBANAME);
 219: 
 220:     p++;
 221: 
 222:     if (!t[p++].equals(":"))
 223:       throw new BAD_PARAM("Syntax (':' expected after name prefix)");
 224: 
 225:     // Check for rir:
 226:     if (t[p].equals(RIR))
 227:       {
 228:         p++;
 229:         if (!t[p++].equals(":"))
 230:           throw new BAD_PARAM("':' expected after 'rir'");
 231: 
 232:         key = readKey("/");
 233: 
 234:         Object object;
 235:         try
 236:           {
 237:             object = orb.resolve_initial_references(key);
 238:             return resolve(orb.object_to_string(object));
 239:           }
 240:         catch (InvalidName e)
 241:           {
 242:             throw new BAD_PARAM("Unknown initial reference '" + key + "'");
 243:           }
 244:       }
 245:     else
 246:     // Check for iiop.
 247:     if (t[p].equals(IIOP) || t[p].equals(":"))
 248:       {
 249:         IOR ior = new IOR();
 250: 
 251:         Addresses: do
 252:           { // Read addresses.
 253:             if (t[p].equals(":"))
 254:               {
 255:                 p++;
 256:               }
 257:             else
 258:               {
 259:                 p++;
 260:                 if (!t[p++].equals(":"))
 261:                   throw new BAD_PARAM("':' expected after 'iiop'");
 262:                 // Check if version is present.
 263:                 if (t[p + 1].equals("."))
 264:                   if (t[p + 3].equals("@"))
 265:                     {
 266:                       // Version info present.
 267:                       try
 268:                         {
 269:                           major = Integer.parseInt(t[p++]);
 270:                         }
 271:                       catch (NumberFormatException e)
 272:                         {
 273:                           throw new BAD_PARAM("Major version number '"
 274:                             + t[p - 1] + "'");
 275:                         }
 276:                       p++; // '.' at this point.
 277:                       try
 278:                         {
 279:                           minor = Integer.parseInt(t[p++]);
 280:                         }
 281:                       catch (NumberFormatException e)
 282:                         {
 283:                           throw new BAD_PARAM("Major version number '"
 284:                             + t[p - 1] + "'");
 285:                         }
 286:                       p++; // '@' at this point.
 287:                     }
 288:               }
 289: 
 290:             ior.Internet.version = new Version(major, minor);
 291: 
 292:             // Then host data goes till '/' or ':'.
 293:             CPStringBuilder bhost = new CPStringBuilder(corbaloc.length());
 294:             while (!t[p].equals(":") && !t[p].equals("/") && !t[p].equals(","))
 295:               bhost.append(t[p++]);
 296: 
 297:             host = bhost.toString();
 298: 
 299:             ior.Internet.host = host;
 300: 
 301:             if (t[p].equals(":"))
 302:               {
 303:                 // Port specified.
 304:                 p++;
 305:                 try
 306:                   {
 307:                     port = Integer.parseInt(t[p++]);
 308:                   }
 309:                 catch (NumberFormatException e)
 310:                   {
 311:                     throw new BAD_PARAM("Invalid port '" + t[p - 1] + "'");
 312:                   }
 313:               }
 314: 
 315:             ior.Internet.port = port;
 316: 
 317:             // Id is not listed.
 318:             ior.Id = "";
 319: 
 320:             if (t[p].equals(","))
 321:               p++;
 322:             else
 323:               break Addresses;
 324:           }
 325:         while (true);
 326: 
 327:         key = readKey("/");
 328:         ior.key = key.getBytes();
 329: 
 330:         return resolve(ior.toStringifiedReference());
 331:       }
 332: 
 333:     else
 334:       throw new InvalidNameException("Unsupported protocol '" + t[p] +
 335:                                      "' (iiop expected)");
 336:   }
 337: 
 338:   /**
 339:    * Read IOR from the file in the local file system.
 340:    */
 341:   String readFile(String file)
 342:   {
 343:     File f = new File(file);
 344:     if (!f.exists())
 345:       {
 346:         DATA_CONVERSION err = new DATA_CONVERSION(f.getAbsolutePath()
 347:           + " does not exist.");
 348:         err.minor = Minor.Missing_IOR;
 349:       }
 350:     try
 351:       {
 352:         char[] c = new char[(int) f.length()];
 353:         FileReader fr = new FileReader(f);
 354:         fr.read(c);
 355:         fr.close();
 356:         return new String(c).trim();
 357:       }
 358:     catch (IOException ex)
 359:       {
 360:         DATA_CONVERSION d = new DATA_CONVERSION();
 361:         d.initCause(ex);
 362:         d.minor = Minor.Missing_IOR;
 363:         throw (d);
 364:       }
 365:   }
 366: 
 367:   /**
 368:    * Read IOR from the remote URL.
 369:    */
 370:   String readUrl(String url)
 371:   {
 372:     URL u;
 373:     try
 374:       {
 375:         u = new URL(url);
 376:       }
 377:     catch (MalformedURLException mex)
 378:       {
 379:         throw new BAD_PARAM("Malformed URL: '" + url + "'");
 380:       }
 381: 
 382:     try
 383:       {
 384:         InputStreamReader r = new InputStreamReader(u.openStream());
 385: 
 386:         CPStringBuilder b = new CPStringBuilder();
 387:         int c;
 388: 
 389:         while ((c = r.read()) > 0)
 390:           b.append((char) c);
 391: 
 392:         return b.toString().trim();
 393:       }
 394:     catch (Exception exc)
 395:       {
 396:         DATA_CONVERSION d = new DATA_CONVERSION("Reading " + url + " failed.");
 397:         d.minor = Minor.Missing_IOR;
 398:         throw d;
 399:       }
 400:   }
 401: 
 402:   private String[] resolve(String nsIor)
 403:   {
 404:     String [] n = new String[2];
 405:     n[0] = nsIor;
 406:     n[1] = readKey("#");
 407:     return n;
 408:   }
 409: 
 410:   private String readKey(String delimiter)
 411:     throws BAD_PARAM
 412:   {
 413:     if (p < t.length)
 414:       if (!t[p].equals(delimiter))
 415:         {
 416:           if (t[p].equals("#"))
 417:             return DEFAULT_NAME;
 418:           else
 419:             throw new BAD_PARAM("'" + delimiter + "String' expected '" + t[p]
 420:               + "' found");
 421:         }
 422: 
 423:     CPStringBuilder bKey = new CPStringBuilder();
 424:     p++;
 425: 
 426:     while (p < t.length && !t[p].equals("#"))
 427:       bKey.append(t[p++]);
 428: 
 429:     if (bKey.length() == 0)
 430:       return DEFAULT_NAME;
 431: 
 432:     try
 433:       {
 434:         return URLDecoder.decode(bKey.toString(), "UTF-8");
 435:       }
 436:     catch (UnsupportedEncodingException e)
 437:       {
 438:         throw new Unexpected("URLDecoder does not support UTF-8", e);
 439:       }
 440:   }
 441: }