1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 // Resolver.java - Represents an extension of OASIS Open Catalog files. 6 7 /* 8 * Copyright 2001-2004 The Apache Software Foundation or its licensors, 9 * as applicable. 10 * 11 * Licensed under the Apache License, Version 2.0 (the "License"); 12 * you may not use this file except in compliance with the License. 13 * You may obtain a copy of the License at 14 * 15 * http://www.apache.org/licenses/LICENSE-2.0 16 * 17 * Unless required by applicable law or agreed to in writing, software 18 * distributed under the License is distributed on an "AS IS" BASIS, 19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 * See the License for the specific language governing permissions and 21 * limitations under the License. 22 */ 23 24 package com.sun.org.apache.xml.internal.resolver; 25 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.io.FileNotFoundException; 29 import java.util.Enumeration; 30 import java.util.Vector; 31 import java.net.URL; 32 import java.net.URLConnection; 33 import java.net.MalformedURLException; 34 import javax.xml.parsers.SAXParserFactory; 35 import com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl; 36 import com.sun.org.apache.xerces.internal.utils.SecuritySupport; 37 import com.sun.org.apache.xml.internal.resolver.readers.SAXCatalogReader; 38 import com.sun.org.apache.xml.internal.resolver.readers.OASISXMLCatalogReader; 39 import com.sun.org.apache.xml.internal.resolver.readers.TR9401CatalogReader; 40 41 /** 42 * An extension to OASIS Open Catalog files, this class supports 43 * suffix-based matching and an external RFC2483 resolver. 44 * 45 * @see Catalog 46 * 47 * @author Norman Walsh 48 * <a href="mailto:Norman.Walsh@Sun.COM">Norman.Walsh@Sun.COM</a> 49 * 50 * @version 1.0 51 */ 52 public class Resolver extends Catalog { 53 /** 54 * The URISUFFIX Catalog Entry type. 55 * 56 * <p>URI suffix entries match URIs that end in a specified suffix.</p> 57 */ 58 public static final int URISUFFIX = CatalogEntry.addEntryType("URISUFFIX", 2); 59 60 /** 61 * The SYSTEMSUFFIX Catalog Entry type. 62 * 63 * <p>System suffix entries match system identifiers that end in a 64 * specified suffix.</p> 65 */ 66 public static final int SYSTEMSUFFIX = CatalogEntry.addEntryType("SYSTEMSUFFIX", 2); 67 68 /** 69 * The RESOLVER Catalog Entry type. 70 * 71 * <p>A hook for providing support for web-based backup resolvers.</p> 72 */ 73 public static final int RESOLVER = CatalogEntry.addEntryType("RESOLVER", 1); 74 75 /** 76 * The SYSTEMREVERSE Catalog Entry type. 77 * 78 * <p>This is a bit of a hack. There's no actual SYSTEMREVERSE entry, 79 * but this entry type is used to indicate that a reverse lookup is 80 * being performed. (This allows the Resolver to implement 81 * RFC2483 I2N and I2NS.) 82 */ 83 public static final int SYSTEMREVERSE 84 = CatalogEntry.addEntryType("SYSTEMREVERSE", 1); 85 86 /** 87 * Setup readers. 88 */ 89 public void setupReaders() { 90 SAXParserFactory spf = catalogManager.useServicesMechanism() ? 91 SAXParserFactory.newInstance() : new SAXParserFactoryImpl(); 92 spf.setNamespaceAware(true); 93 spf.setValidating(false); 94 95 SAXCatalogReader saxReader = new SAXCatalogReader(spf); 96 97 saxReader.setCatalogParser(null, "XMLCatalog", 98 "com.sun.org.apache.xml.internal.resolver.readers.XCatalogReader"); 99 100 saxReader.setCatalogParser(OASISXMLCatalogReader.namespaceName, 101 "catalog", 102 "com.sun.org.apache.xml.internal.resolver.readers.ExtendedXMLCatalogReader"); 103 104 addReader("application/xml", saxReader); 105 106 TR9401CatalogReader textReader = new TR9401CatalogReader(); 107 addReader("text/plain", textReader); 108 } 109 110 /** 111 * Cleanup and process a Catalog entry. 112 * 113 * <p>This method processes each Catalog entry, changing mapped 114 * relative system identifiers into absolute ones (based on the current 115 * base URI), and maintaining other information about the current 116 * catalog.</p> 117 * 118 * @param entry The CatalogEntry to process. 119 */ 120 public void addEntry(CatalogEntry entry) { 121 int type = entry.getEntryType(); 122 123 if (type == URISUFFIX) { 124 String suffix = normalizeURI(entry.getEntryArg(0)); 125 String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1))); 126 127 entry.setEntryArg(1, fsi); 128 129 catalogManager.debug.message(4, "URISUFFIX", suffix, fsi); 130 } else if (type == SYSTEMSUFFIX) { 131 String suffix = normalizeURI(entry.getEntryArg(0)); 132 String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1))); 133 134 entry.setEntryArg(1, fsi); 135 136 catalogManager.debug.message(4, "SYSTEMSUFFIX", suffix, fsi); 137 } 138 139 super.addEntry(entry); 140 } 141 142 /** 143 * Return the applicable URI. 144 * 145 * <p>If a URI entry exists in the Catalog 146 * for the URI specified, return the mapped value.</p> 147 * 148 * <p>In the Resolver (as opposed to the Catalog) class, if the 149 * URI isn't found by the usual algorithm, URISUFFIX entries are 150 * considered.</p> 151 * 152 * <p>URI comparison is case sensitive.</p> 153 * 154 * @param uri The URI to locate in the catalog. 155 * 156 * @return The resolved URI. 157 * 158 * @throws MalformedURLException The system identifier of a 159 * subordinate catalog cannot be turned into a valid URL. 160 * @throws IOException Error reading subordinate catalog file. 161 */ 162 public String resolveURI(String uri) 163 throws MalformedURLException, IOException { 164 165 String resolved = super.resolveURI(uri); 166 if (resolved != null) { 167 return resolved; 168 } 169 170 Enumeration en = catalogEntries.elements(); 171 while (en.hasMoreElements()) { 172 CatalogEntry e = (CatalogEntry) en.nextElement(); 173 if (e.getEntryType() == RESOLVER) { 174 resolved = resolveExternalSystem(uri, e.getEntryArg(0)); 175 if (resolved != null) { 176 return resolved; 177 } 178 } else if (e.getEntryType() == URISUFFIX) { 179 String suffix = e.getEntryArg(0); 180 String result = e.getEntryArg(1); 181 182 if (suffix.length() <= uri.length() 183 && uri.substring(uri.length()-suffix.length()).equals(suffix)) { 184 return result; 185 } 186 } 187 } 188 189 // Otherwise, look in the subordinate catalogs 190 return resolveSubordinateCatalogs(Catalog.URI, 191 null, 192 null, 193 uri); 194 } 195 196 /** 197 * Return the applicable SYSTEM system identifier, resorting 198 * to external RESOLVERs if necessary. 199 * 200 * <p>If a SYSTEM entry exists in the Catalog 201 * for the system ID specified, return the mapped value.</p> 202 * 203 * <p>In the Resolver (as opposed to the Catalog) class, if the 204 * URI isn't found by the usual algorithm, SYSTEMSUFFIX entries are 205 * considered.</p> 206 * 207 * <p>On Windows-based operating systems, the comparison between 208 * the system identifier provided and the SYSTEM entries in the 209 * Catalog is case-insensitive.</p> 210 * 211 * @param systemId The system ID to locate in the catalog. 212 * 213 * @return The system identifier to use for systemId. 214 * 215 * @throws MalformedURLException The formal system identifier of a 216 * subordinate catalog cannot be turned into a valid URL. 217 * @throws IOException Error reading subordinate catalog file. 218 */ 219 public String resolveSystem(String systemId) 220 throws MalformedURLException, IOException { 221 222 String resolved = super.resolveSystem(systemId); 223 if (resolved != null) { 224 return resolved; 225 } 226 227 Enumeration en = catalogEntries.elements(); 228 while (en.hasMoreElements()) { 229 CatalogEntry e = (CatalogEntry) en.nextElement(); 230 if (e.getEntryType() == RESOLVER) { 231 resolved = resolveExternalSystem(systemId, e.getEntryArg(0)); 232 if (resolved != null) { 233 return resolved; 234 } 235 } else if (e.getEntryType() == SYSTEMSUFFIX) { 236 String suffix = e.getEntryArg(0); 237 String result = e.getEntryArg(1); 238 239 if (suffix.length() <= systemId.length() 240 && systemId.substring(systemId.length()-suffix.length()).equals(suffix)) { 241 return result; 242 } 243 } 244 } 245 246 return resolveSubordinateCatalogs(Catalog.SYSTEM, 247 null, 248 null, 249 systemId); 250 } 251 252 /** 253 * Return the applicable PUBLIC or SYSTEM identifier, resorting 254 * to external resolvers if necessary. 255 * 256 * <p>This method searches the Catalog and returns the system 257 * identifier specified for the given system or 258 * public identifiers. If 259 * no appropriate PUBLIC or SYSTEM entry is found in the Catalog, 260 * null is returned.</p> 261 * 262 * <p>Note that a system or public identifier in the current catalog 263 * (or subordinate catalogs) will be used in preference to an 264 * external resolver. Further, if a systemId is present, the external 265 * resolver(s) will be queried for that before the publicId.</p> 266 * 267 * @param publicId The public identifier to locate in the catalog. 268 * Public identifiers are normalized before comparison. 269 * @param systemId The nominal system identifier for the entity 270 * in question (as provided in the source document). 271 * 272 * @throws MalformedURLException The formal system identifier of a 273 * subordinate catalog cannot be turned into a valid URL. 274 * @throws IOException Error reading subordinate catalog file. 275 * 276 * @return The system identifier to use. 277 * Note that the nominal system identifier is not returned if a 278 * match is not found in the catalog, instead null is returned 279 * to indicate that no match was found. 280 */ 281 public String resolvePublic(String publicId, String systemId) 282 throws MalformedURLException, IOException { 283 284 String resolved = super.resolvePublic(publicId, systemId); 285 if (resolved != null) { 286 return resolved; 287 } 288 289 Enumeration en = catalogEntries.elements(); 290 while (en.hasMoreElements()) { 291 CatalogEntry e = (CatalogEntry) en.nextElement(); 292 if (e.getEntryType() == RESOLVER) { 293 if (systemId != null) { 294 resolved = resolveExternalSystem(systemId, 295 e.getEntryArg(0)); 296 if (resolved != null) { 297 return resolved; 298 } 299 } 300 resolved = resolveExternalPublic(publicId, e.getEntryArg(0)); 301 if (resolved != null) { 302 return resolved; 303 } 304 } 305 } 306 307 return resolveSubordinateCatalogs(Catalog.PUBLIC, 308 null, 309 publicId, 310 systemId); 311 } 312 313 /** 314 * Query an external RFC2483 resolver for a system identifier. 315 * 316 * @param systemId The system ID to locate. 317 * @param resolver The name of the resolver to use. 318 * 319 * @return The system identifier to use for the systemId. 320 */ 321 protected String resolveExternalSystem(String systemId, String resolver) 322 throws MalformedURLException, IOException { 323 Resolver r = queryResolver(resolver, "i2l", systemId, null); 324 if (r != null) { 325 return r.resolveSystem(systemId); 326 } else { 327 return null; 328 } 329 } 330 331 /** 332 * Query an external RFC2483 resolver for a public identifier. 333 * 334 * @param publicId The system ID to locate. 335 * @param resolver The name of the resolver to use. 336 * 337 * @return The system identifier to use for the systemId. 338 */ 339 protected String resolveExternalPublic(String publicId, String resolver) 340 throws MalformedURLException, IOException { 341 Resolver r = queryResolver(resolver, "fpi2l", publicId, null); 342 if (r != null) { 343 return r.resolvePublic(publicId, null); 344 } else { 345 return null; 346 } 347 } 348 349 /** 350 * Query an external RFC2483 resolver. 351 * 352 * @param resolver The URL of the RFC2483 resolver. 353 * @param command The command to send the resolver. 354 * @param arg1 The first argument to the resolver. 355 * @param arg2 The second argument to the resolver, usually null. 356 * 357 * @return The Resolver constructed. 358 */ 359 protected Resolver queryResolver(String resolver, 360 String command, 361 String arg1, 362 String arg2) { 363 InputStream iStream = null; 364 String RFC2483 = resolver + "?command=" + command 365 + "&format=tr9401&uri=" + arg1 366 + "&uri2=" + arg2; 367 String line = null; 368 369 try { 370 URL url = new URL(RFC2483); 371 372 URLConnection urlCon = url.openConnection(); 373 374 urlCon.setUseCaches(false); 375 376 Resolver r = (Resolver) newCatalog(); 377 378 String cType = urlCon.getContentType(); 379 380 // I don't care about the character set or subtype 381 if (cType.indexOf(";") > 0) { 382 cType = cType.substring(0, cType.indexOf(";")); 383 } 384 385 r.parseCatalog(cType, urlCon.getInputStream()); 386 387 return r; 388 } catch (CatalogException cex) { 389 if (cex.getExceptionType() == CatalogException.UNPARSEABLE) { 390 catalogManager.debug.message(1, "Unparseable catalog: " + RFC2483); 391 } else if (cex.getExceptionType() 392 == CatalogException.UNKNOWN_FORMAT) { 393 catalogManager.debug.message(1, "Unknown catalog format: " + RFC2483); 394 } 395 return null; 396 } catch (MalformedURLException mue) { 397 catalogManager.debug.message(1, "Malformed resolver URL: " + RFC2483); 398 return null; 399 } catch (IOException ie) { 400 catalogManager.debug.message(1, "I/O Exception opening resolver: " + RFC2483); 401 return null; 402 } 403 } 404 405 /** 406 * Append two vectors, returning the result. 407 * 408 * @param vec The first vector 409 * @param appvec The vector to be appended 410 * @return The vector vec, with appvec's elements appended to it 411 */ 412 private Vector appendVector(Vector vec, Vector appvec) { 413 if (appvec != null) { 414 for (int count = 0; count < appvec.size(); count++) { 415 vec.addElement(appvec.elementAt(count)); 416 } 417 } 418 return vec; 419 } 420 421 /** 422 * Find the URNs for a given system identifier in all catalogs. 423 * 424 * @param systemId The system ID to locate. 425 * 426 * @return A vector of URNs that map to the systemId. 427 */ 428 public Vector resolveAllSystemReverse(String systemId) 429 throws MalformedURLException, IOException { 430 Vector resolved = new Vector(); 431 432 // If there's a SYSTEM entry in this catalog, use it 433 if (systemId != null) { 434 Vector localResolved = resolveLocalSystemReverse(systemId); 435 resolved = appendVector(resolved, localResolved); 436 } 437 438 // Otherwise, look in the subordinate catalogs 439 Vector subResolved = resolveAllSubordinateCatalogs(SYSTEMREVERSE, 440 null, 441 null, 442 systemId); 443 444 return appendVector(resolved, subResolved); 445 } 446 447 /** 448 * Find the URN for a given system identifier. 449 * 450 * @param systemId The system ID to locate. 451 * 452 * @return A (single) URN that maps to the systemId. 453 */ 454 public String resolveSystemReverse(String systemId) 455 throws MalformedURLException, IOException { 456 Vector resolved = resolveAllSystemReverse(systemId); 457 if (resolved != null && resolved.size() > 0) { 458 return (String) resolved.elementAt(0); 459 } else { 460 return null; 461 } 462 } 463 464 /** 465 * Return the applicable SYSTEM system identifiers. 466 * 467 * <p>If one or more SYSTEM entries exists in the Catalog 468 * for the system ID specified, return the mapped values.</p> 469 * 470 * <p>The caller is responsible for doing any necessary 471 * normalization of the system identifier before calling 472 * this method. For example, a relative system identifier in 473 * a document might be converted to an absolute system identifier 474 * before attempting to resolve it.</p> 475 * 476 * <p>Note that this function will force all subordinate catalogs 477 * to be loaded.</p> 478 * 479 * <p>On Windows-based operating systems, the comparison between 480 * the system identifier provided and the SYSTEM entries in the 481 * Catalog is case-insensitive.</p> 482 * 483 * @param systemId The system ID to locate in the catalog. 484 * 485 * @return The system identifier to use for the notation. 486 * 487 * @throws MalformedURLException The formal system identifier of a 488 * subordinate catalog cannot be turned into a valid URL. 489 * @throws IOException Error reading subordinate catalog file. 490 */ 491 public Vector resolveAllSystem(String systemId) 492 throws MalformedURLException, IOException { 493 Vector resolutions = new Vector(); 494 495 // If there are SYSTEM entries in this catalog, start with them 496 if (systemId != null) { 497 Vector localResolutions = resolveAllLocalSystem(systemId); 498 resolutions = appendVector(resolutions, localResolutions); 499 } 500 501 // Then look in the subordinate catalogs 502 Vector subResolutions = resolveAllSubordinateCatalogs(SYSTEM, 503 null, 504 null, 505 systemId); 506 resolutions = appendVector(resolutions, subResolutions); 507 508 if (resolutions.size() > 0) { 509 return resolutions; 510 } else { 511 return null; 512 } 513 } 514 515 /** 516 * Return all applicable SYSTEM system identifiers in this 517 * catalog. 518 * 519 * <p>If one or more SYSTEM entries exists in the catalog file 520 * for the system ID specified, return the mapped values.</p> 521 * 522 * @param systemId The system ID to locate in the catalog 523 * 524 * @return A vector of the mapped system identifiers or null 525 */ 526 private Vector resolveAllLocalSystem(String systemId) { 527 Vector map = new Vector(); 528 String osname = SecuritySupport.getSystemProperty("os.name"); 529 boolean windows = (osname.indexOf("Windows") >= 0); 530 Enumeration en = catalogEntries.elements(); 531 while (en.hasMoreElements()) { 532 CatalogEntry e = (CatalogEntry) en.nextElement(); 533 if (e.getEntryType() == SYSTEM 534 && (e.getEntryArg(0).equals(systemId) 535 || (windows 536 && e.getEntryArg(0).equalsIgnoreCase(systemId)))) { 537 map.addElement(e.getEntryArg(1)); 538 } 539 } 540 if (map.size() == 0) { 541 return null; 542 } else { 543 return map; 544 } 545 } 546 547 /** 548 * Find the URNs for a given system identifier in the current catalog. 549 * 550 * @param systemId The system ID to locate. 551 * 552 * @return A vector of URNs that map to the systemId. 553 */ 554 private Vector resolveLocalSystemReverse(String systemId) { 555 Vector map = new Vector(); 556 String osname = SecuritySupport.getSystemProperty("os.name"); 557 boolean windows = (osname.indexOf("Windows") >= 0); 558 Enumeration en = catalogEntries.elements(); 559 while (en.hasMoreElements()) { 560 CatalogEntry e = (CatalogEntry) en.nextElement(); 561 if (e.getEntryType() == SYSTEM 562 && (e.getEntryArg(1).equals(systemId) 563 || (windows 564 && e.getEntryArg(1).equalsIgnoreCase(systemId)))) { 565 map.addElement(e.getEntryArg(0)); 566 } 567 } 568 if (map.size() == 0) { 569 return null; 570 } else { 571 return map; 572 } 573 } 574 575 /** 576 * Search the subordinate catalogs, in order, looking for all 577 * match. 578 * 579 * <p>This method searches the Catalog and returns all of the system 580 * identifiers specified for the given entity type with the given 581 * name, public, and system identifiers. In some contexts, these 582 * may be null.</p> 583 * 584 * @param entityType The CatalogEntry type for which this query is 585 * being conducted. This is necessary in order to do the approprate 586 * query on a subordinate catalog. 587 * @param entityName The name of the entity being searched for, if 588 * appropriate. 589 * @param publicId The public identifier of the entity in question 590 * (as provided in the source document). 591 * @param systemId The nominal system identifier for the entity 592 * in question (as provided in the source document). 593 * 594 * @throws MalformedURLException The formal system identifier of a 595 * delegated catalog cannot be turned into a valid URL. 596 * @throws IOException Error reading delegated catalog file. 597 * 598 * @return The system identifier to use. 599 * Note that the nominal system identifier is not returned if a 600 * match is not found in the catalog, instead null is returned 601 * to indicate that no match was found. 602 */ 603 private synchronized Vector resolveAllSubordinateCatalogs(int entityType, 604 String entityName, 605 String publicId, 606 String systemId) 607 throws MalformedURLException, IOException { 608 609 Vector resolutions = new Vector(); 610 611 for (int catPos = 0; catPos < catalogs.size(); catPos++) { 612 Resolver c = null; 613 614 try { 615 c = (Resolver) catalogs.elementAt(catPos); 616 } catch (ClassCastException e) { 617 String catfile = (String) catalogs.elementAt(catPos); 618 c = (Resolver) newCatalog(); 619 620 try { 621 c.parseCatalog(catfile); 622 } catch (MalformedURLException mue) { 623 catalogManager.debug.message(1, "Malformed Catalog URL", catfile); 624 } catch (FileNotFoundException fnfe) { 625 catalogManager.debug.message(1, "Failed to load catalog, file not found", 626 catfile); 627 } catch (IOException ioe) { 628 catalogManager.debug.message(1, "Failed to load catalog, I/O error", catfile); 629 } 630 631 catalogs.setElementAt(c, catPos); 632 } 633 634 String resolved = null; 635 636 // Ok, now what are we supposed to call here? 637 if (entityType == DOCTYPE) { 638 resolved = c.resolveDoctype(entityName, 639 publicId, 640 systemId); 641 if (resolved != null) { 642 // Only find one DOCTYPE resolution 643 resolutions.addElement(resolved); 644 return resolutions; 645 } 646 } else if (entityType == DOCUMENT) { 647 resolved = c.resolveDocument(); 648 if (resolved != null) { 649 // Only find one DOCUMENT resolution 650 resolutions.addElement(resolved); 651 return resolutions; 652 } 653 } else if (entityType == ENTITY) { 654 resolved = c.resolveEntity(entityName, 655 publicId, 656 systemId); 657 if (resolved != null) { 658 // Only find one ENTITY resolution 659 resolutions.addElement(resolved); 660 return resolutions; 661 } 662 } else if (entityType == NOTATION) { 663 resolved = c.resolveNotation(entityName, 664 publicId, 665 systemId); 666 if (resolved != null) { 667 // Only find one NOTATION resolution 668 resolutions.addElement(resolved); 669 return resolutions; 670 } 671 } else if (entityType == PUBLIC) { 672 resolved = c.resolvePublic(publicId, systemId); 673 if (resolved != null) { 674 // Only find one PUBLIC resolution 675 resolutions.addElement(resolved); 676 return resolutions; 677 } 678 } else if (entityType == SYSTEM) { 679 Vector localResolutions = c.resolveAllSystem(systemId); 680 resolutions = appendVector(resolutions, localResolutions); 681 break; 682 } else if (entityType == SYSTEMREVERSE) { 683 Vector localResolutions = c.resolveAllSystemReverse(systemId); 684 resolutions = appendVector(resolutions, localResolutions); 685 } 686 } 687 688 if (resolutions != null) { 689 return resolutions; 690 } else { 691 return null; 692 } 693 } 694 }