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