1 /* 2 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package javax.xml.catalog; 26 27 import java.net.MalformedURLException; 28 import java.net.URISyntaxException; 29 import java.util.HashMap; 30 import java.util.Map; 31 import jdk.xml.internal.SecuritySupport; 32 33 /** 34 * The CatalogFeatures holds a collection of features and properties. 35 * <p> 36 * 37 * <center><h2><a name="CatalogFeatures">Catalog Features</a></h2></center></p> 38 * 39 * <table border="1"> 40 * <thead> 41 * <tr> 42 * <th rowspan="2">Feature</th> 43 * <th rowspan="2">Description</th> 44 * <th rowspan="2">Property Name</th> 45 * <th rowspan="2">System Property [1]</th> 46 * <th rowspan="2">jaxp.properties [1]</th> 47 * <th colspan="2" align="center">Value [2]</th> 48 * <th rowspan="2">Action</th> 49 * </tr> 50 * <tr> 51 * <th>Type</th> 52 * <th>Value</th> 53 * </tr> 54 * </thead> 55 * <tbody> 56 * 57 * <tr> 58 * <td><a name="FILES">FILES</a></td> 59 * <td>A semicolon-delimited list of catalog files. Relative file paths are 60 * considered relative to ${user.dir}. 61 * </td> 62 * <td>javax.xml.catalog.files</td> 63 * <td>javax.xml.catalog.files</td> 64 * <td>javax.xml.catalog.files</td> 65 * <td>String</td> 66 * <td>File paths</td> 67 * <td> 68 * Reads the first catalog as the current catalog; Loads others if no match 69 * is found in the current catalog including delegate catalogs if any. 70 * </td> 71 * </tr> 72 * 73 * <tr> 74 * <td rowspan="2"><a name="PREFER">PREFER</a></td> 75 * <td rowspan="2">Indicates the preference between the public and system 76 * identifiers. The default value is public [3].</td> 77 * <td rowspan="2">javax.xml.catalog.prefer</td> 78 * <td rowspan="2">N/A</td> 79 * <td rowspan="2">N/A</td> 80 * <td rowspan="2">String</td> 81 * <td>{@code system}</td> 82 * <td>Searches system entries for a match; Searches public entries when 83 * external identifier specifies only a public identifier</td> 84 * </tr> 85 * <tr> 86 * <td>{@code public}</td> 87 * <td>Searches system entries for a match; Searches public entries when 88 * there is no matching system entry.</td> 89 * </tr> 90 * 91 * <tr> 92 * <td rowspan="2"><a name="DEFER">DEFER</a></td> 93 * <td rowspan="2">Indicates that the alternative catalogs including those 94 * specified in delegate entries or nextCatalog are not read until they are 95 * needed. The default value is true.</td> 96 * <td rowspan="2">javax.xml.catalog.defer [4]</td> 97 * <td rowspan="2">javax.xml.catalog.defer</td> 98 * <td rowspan="2">javax.xml.catalog.defer</td> 99 * <td rowspan="2">String</td> 100 * <td>{@code true}</td> 101 * <td>Loads alternative catalogs as needed. 102 * </td> 103 * </tr> 104 * <tr> 105 * <td>{@code false}</td> 106 * <td>Loads all catalogs[5]. </td> 107 * </tr> 108 * 109 * <tr> 110 * <td rowspan="3"><a name="RESOLVE">RESOLVE</a></td> 111 * <td rowspan="3">Determines the action if there is no matching entry found after 112 * all of the specified catalogs are exhausted. The default is strict.</td> 113 * <td rowspan="3">javax.xml.catalog.resolve [4]</td> 114 * <td rowspan="3">javax.xml.catalog.resolve</td> 115 * <td rowspan="3">javax.xml.catalog.resolve</td> 116 * <td rowspan="3">String</td> 117 * <td>{@code strict}</td> 118 * <td>Throws CatalogException if there is no match. 119 * </td> 120 * </tr> 121 * <tr> 122 * <td>{@code continue}</td> 123 * <td>Allows the XML parser to continue as if there is no match. 124 * </td> 125 * </tr> 126 * <tr> 127 * <td>{@code ignore}</td> 128 * <td>Tells the XML parser to skip the external references if there no match. 129 * </td> 130 * </tr> 131 * 132 * </tbody> 133 * </table> 134 * <p> 135 * <b>[1]</b> There is no System property for the features that marked as "N/A". 136 * 137 * <p> 138 * <b>[2]</b> The value shall be exactly as listed in this table, case-sensitive. 139 * Any unspecified value will result in {@link IllegalArgumentException}. 140 * <p> 141 * <b>[3]</b> The Catalog specification defined complex rules on 142 * <a href="https://www.oasis-open.org/committees/download.php/14809/xml-catalogs.html#attrib.prefer"> 143 * the prefer attribute</a>. Although the prefer can be public or system, the 144 * specification actually made system the preferred option, that is, no matter 145 * the option, a system entry is always used if found. Public entries are only 146 * considered if the prefer is public and system entries are not found. It is 147 * therefore recommended that the prefer attribute be set as public 148 * (which is the default). 149 * <p> 150 * <b>[4]</b> Although non-standard attributes in the OASIS Catalog specification, 151 * {@code defer} and {@code resolve} are recognized by the Java Catalog API the 152 * same as the {@code prefer} as being an attribute in the catalog entry of the 153 * main catalog. Note that only the attributes specified for the catalog entry 154 * of the main Catalog file will be used. 155 * <p> 156 * <b>[5]</b> If the intention is to share an entire catalog store, it may be desirable to 157 * set the property {@code javax.xml.catalog.defer} to false to allow the entire 158 * catalog to be pre-loaded. 159 * <p> 160 * <h3>Scope and Order</h3> 161 * Features and properties can be set through the catalog file, the Catalog API, 162 * system properties, and {@code jaxp.properties}, with a preference in the same order. 163 * <p> 164 * Properties that are specified as attributes in the catalog file for the 165 * catalog and group entries shall take preference over any of the other settings. 166 * For example, if a {@code prefer} attribute is set in the catalog file as in 167 * {@code <catalog prefer="public">}, any other input for the "prefer" property 168 * is not necessary or will be ignored. 169 * <p> 170 * Properties set through the Catalog API override those that may have been set 171 * by system properties and/or in {@code jaxp.properties}. In case of multiple 172 * interfaces, the latest in a procedure shall take preference. For 173 * {@link Feature#FILES}, this means that the path(s) specified through the methods 174 * of the {@link CatalogManager} will override any that may have been entered 175 * through the {@link Builder}. 176 * 177 * <p> 178 * System properties when set shall override those in {@code jaxp.properties}. 179 * <p> 180 * The {@code jaxp.properties} file is typically in the conf directory of the Java 181 * installation. The file is read only once by the JAXP implementation and 182 * its values are then cached for future use. If the file does not exist 183 * when the first attempt is made to read from it, no further attempts are 184 * made to check for its existence. It is not possible to change the value 185 * of any properties in {@code jaxp.properties} after it has been read. 186 * <p> 187 * A CatalogFeatures instance can be created through its builder as illustrated 188 * in the following sample code: 189 * <pre>{@code 190 CatalogFeatures f = CatalogFeatures.builder() 191 .with(Feature.FILES, "catalog.xml") 192 .with(Feature.PREFER, "public") 193 .with(Feature.DEFER, "true") 194 .with(Feature.RESOLVE, "ignore") 195 .build(); 196 * }</pre> 197 * 198 * @since 9 199 */ 200 public class CatalogFeatures { 201 202 /** 203 * The constant name of the javax.xml.catalog.files property. See the property table for more details. 204 */ 205 static final String CATALOG_FILES = "javax.xml.catalog.files"; 206 207 /** 208 * The javax.xml.catalog.prefer property. See the property table for more details. 209 */ 210 static final String CATALOG_PREFER = "javax.xml.catalog.prefer"; 211 212 /** 213 * Determines whether or not delegated catalogs and nextCatalog will be read 214 * when the current catalog is loaded. 215 */ 216 static final String CATALOG_DEFER = "javax.xml.catalog.defer"; 217 218 /** 219 * Determines the action if there is no matching entry found after 220 * all of the specified catalogs are exhausted. 221 */ 222 static final String CATALOG_RESOLVE = "javax.xml.catalog.resolve"; 223 224 //values for the prefer property 225 static final String PREFER_SYSTEM = "system"; 226 static final String PREFER_PUBLIC = "public"; 227 228 //values for the defer property 229 static final String DEFER_TRUE = "true"; 230 static final String DEFER_FALSE = "false"; 231 232 //values for the Resolve property 233 static final String RESOLVE_STRICT = "strict"; 234 static final String RESOLVE_CONTINUE = "continue"; 235 static final String RESOLVE_IGNORE = "ignore"; 236 237 /** 238 * A Feature type as defined in the 239 * <a href="CatalogFeatures.html#CatalogFeatures">Catalog Features table</a>. 240 */ 241 public static enum Feature { 242 /** 243 * The {@code javax.xml.catalog.files} property as described in 244 * item <a href="CatalogFeatures.html#FILES">FILES</a> of the 245 * Catalog Features table. 246 */ 247 FILES(CATALOG_FILES, null, true), 248 /** 249 * The {@code javax.xml.catalog.prefer} property as described in 250 * item <a href="CatalogFeatures.html#PREFER">PREFER</a> of the 251 * Catalog Features table. 252 */ 253 PREFER(CATALOG_PREFER, PREFER_PUBLIC, false), 254 /** 255 * The {@code javax.xml.catalog.defer} property as described in 256 * item <a href="CatalogFeatures.html#DEFER">DEFER</a> of the 257 * Catalog Features table. 258 */ 259 DEFER(CATALOG_DEFER, DEFER_TRUE, true), 260 /** 261 * The {@code javax.xml.catalog.resolve} property as described in 262 * item <a href="CatalogFeatures.html#RESOLVE">RESOLVE</a> of the 263 * Catalog Features table. 264 */ 265 RESOLVE(CATALOG_RESOLVE, RESOLVE_STRICT, true); 266 267 private final String name; 268 private final String defaultValue; 269 private String value; 270 private final boolean hasSystem; 271 272 /** 273 * Constructs a CatalogFeature instance. 274 * @param name the name of the feature 275 * @param value the value of the feature 276 * @param hasSystem a flag to indicate whether the feature is supported 277 * with a System property 278 */ 279 Feature(String name, String value, boolean hasSystem) { 280 this.name = name; 281 this.defaultValue = value; 282 this.hasSystem = hasSystem; 283 } 284 285 /** 286 * Checks whether the specified property is equal to the current property. 287 * @param propertyName the name of a property 288 * @return true if the specified property is the current property, false 289 * otherwise 290 */ 291 boolean equalsPropertyName(String propertyName) { 292 return name.equals(propertyName); 293 } 294 295 /** 296 * Returns the name of the corresponding System Property. 297 * 298 * @return the name of the System Property 299 */ 300 public String getPropertyName() { 301 return name; 302 } 303 304 /** 305 * Returns the default value of the property. 306 * @return the default value of the property 307 */ 308 String defaultValue() { 309 return defaultValue; 310 } 311 312 /** 313 * Returns the value of the property. 314 * @return the value of the property 315 */ 316 String getValue() { 317 return value; 318 } 319 320 /** 321 * Checks whether System property is supported for the feature. 322 * @return true it is supported, false otherwise 323 */ 324 boolean hasSystemProperty() { 325 return hasSystem; 326 } 327 } 328 329 /** 330 * States of the settings of a property, in the order: default value, 331 * jaxp.properties file, jaxp system properties, and jaxp api properties 332 */ 333 static enum State { 334 /** represents the default state of a feature. */ 335 DEFAULT("default"), 336 /** indicates the value of the feature is read from jaxp.properties. */ 337 JAXPDOTPROPERTIES("jaxp.properties"), 338 /** indicates the value of the feature is read from its System property. */ 339 SYSTEMPROPERTY("system property"), 340 /** indicates the value of the feature is specified through the API. */ 341 APIPROPERTY("property"), 342 /** indicates the value of the feature is specified as a catalog attribute. */ 343 CATALOGATTRIBUTE("catalog attribute"); 344 345 final String literal; 346 347 State(String literal) { 348 this.literal = literal; 349 } 350 351 String literal() { 352 return literal; 353 } 354 } 355 356 /** 357 * Values of the properties 358 */ 359 private String[] values; 360 361 /** 362 * States of the settings for each property 363 */ 364 private State[] states; 365 366 /** 367 * Private class constructor 368 */ 369 private CatalogFeatures() { 370 } 371 372 /** 373 * Returns a CatalogFeatures instance with default settings. 374 * @return a default CatalogFeatures instance 375 */ 376 public static CatalogFeatures defaults() { 377 return CatalogFeatures.builder().build(); 378 } 379 380 /** 381 * Constructs a new CatalogFeatures instance with the builder. 382 * 383 * @param builder the builder to build the CatalogFeatures 384 */ 385 CatalogFeatures(Builder builder) { 386 init(); 387 setProperties(builder); 388 } 389 390 /** 391 * Returns the value of the specified feature. 392 * 393 * @param cf the type of the Catalog feature 394 * @return the value of the feature 395 */ 396 public String get(Feature cf) { 397 return values[cf.ordinal()]; 398 } 399 400 /** 401 * Initializes the supported properties 402 */ 403 private void init() { 404 values = new String[Feature.values().length]; 405 states = new State[Feature.values().length]; 406 for (Feature cf : Feature.values()) { 407 setProperty(cf.ordinal(), State.DEFAULT, cf.defaultValue()); 408 } 409 //read system properties or jaxp.properties 410 readSystemProperties(); 411 } 412 413 /** 414 * Sets properties by the Builder. 415 * @param builder the CatalogFeatures builder 416 */ 417 private void setProperties(Builder builder) { 418 builder.values.entrySet().stream().forEach((entry) -> { 419 setProperty(entry.getKey().ordinal(), State.APIPROPERTY, entry.getValue()); 420 }); 421 } 422 /** 423 * Sets the value of a property by its index, updates only if it shall override. 424 * 425 * @param index the index of the property 426 * @param state the state of the property 427 * @param value the value of the property 428 * @throws IllegalArgumentException if the value is invalid 429 */ 430 private void setProperty(int index, State state, String value) { 431 if (value != null && value.length() != 0) { 432 if (index == Feature.PREFER.ordinal()) { 433 if (!value.equals(PREFER_SYSTEM) && !value.equals(PREFER_PUBLIC)) { 434 CatalogMessages.reportIAE(new Object[]{value, Feature.PREFER.name()}, null); 435 } 436 } else if (index == Feature.DEFER.ordinal()) { 437 if (!value.equals(DEFER_TRUE) && !value.equals(DEFER_FALSE)) { 438 CatalogMessages.reportIAE(new Object[]{value, Feature.DEFER.name()}, null); 439 } 440 } else if (index == Feature.RESOLVE.ordinal()) { 441 if (!value.equals(RESOLVE_STRICT) && !value.equals(RESOLVE_CONTINUE) 442 && !value.equals(RESOLVE_IGNORE)) { 443 CatalogMessages.reportIAE(new Object[]{value, Feature.RESOLVE.name()}, null); 444 } 445 } else if (index == Feature.FILES.ordinal()) { 446 try { 447 String[] catalogFile = value.split(";[ ]*"); 448 for (String temp : catalogFile) { 449 if (Util.verifyAndGetURI(temp, null) == null) { 450 CatalogMessages.reportIAE(new Object[]{value, Feature.FILES.name()}, null); 451 } 452 } 453 }catch (MalformedURLException | URISyntaxException | IllegalArgumentException ex) { 454 CatalogMessages.reportIAE(new Object[]{value, Feature.FILES.name()}, ex); 455 } 456 } 457 if (states[index] == null || state.compareTo(states[index]) >= 0) { 458 values[index] = value; 459 states[index] = state; 460 } 461 } else { 462 if (state == State.SYSTEMPROPERTY || state == State.JAXPDOTPROPERTIES) { 463 CatalogMessages.reportIAE(new Object[]{value, Feature.values()[index].name()}, null); 464 } 465 } 466 } 467 468 /** 469 * Reads from system properties, or those in jaxp.properties 470 */ 471 private void readSystemProperties() { 472 for (Feature cf : Feature.values()) { 473 getSystemProperty(cf, cf.getPropertyName()); 474 } 475 } 476 477 /** 478 * Reads from system properties, or those in jaxp.properties 479 * 480 * @param cf the type of the property 481 * @param sysPropertyName the name of system property 482 */ 483 private boolean getSystemProperty(Feature cf, String sysPropertyName) { 484 if (cf.hasSystemProperty()) { 485 String value = SecuritySupport.getSystemProperty(sysPropertyName); 486 if (value != null && !value.equals("")) { 487 setProperty(cf.ordinal(), State.SYSTEMPROPERTY, value); 488 return true; 489 } 490 491 value = SecuritySupport.readJAXPProperty(sysPropertyName); 492 if (value != null && !value.equals("")) { 493 setProperty(cf.ordinal(), State.JAXPDOTPROPERTIES, value); 494 return true; 495 } 496 } 497 return false; 498 } 499 500 /** 501 * Returns an instance of the builder for creating the CatalogFeatures object. 502 * 503 * @return an instance of the builder 504 */ 505 public static Builder builder() { 506 return new CatalogFeatures.Builder(); 507 } 508 509 /** 510 * The Builder class for building the CatalogFeatures object. 511 */ 512 public static class Builder { 513 /** 514 * Values of the features supported by CatalogFeatures. 515 */ 516 Map<Feature, String> values = new HashMap<>(); 517 518 /** 519 * Instantiation of Builder is not allowed. 520 */ 521 private Builder() {} 522 523 /** 524 * Sets the value to a specified Feature. 525 * @param feature the Feature to be set 526 * @param value the value to be set for the Feature 527 * @return this Builder instance 528 * @throws IllegalArgumentException if the value is not valid for the 529 * Feature or has the wrong syntax for the {@code javax.xml.catalog.files} 530 * property 531 */ 532 public Builder with(Feature feature, String value) { 533 if (value == null || value.length() == 0) { 534 CatalogMessages.reportIAE(new Object[]{value, feature.name()}, null); 535 } 536 values.put(feature, value); 537 return this; 538 } 539 540 /** 541 * Returns a CatalogFeatures object built by this builder. 542 * 543 * @return an instance of CatalogFeatures 544 */ 545 public CatalogFeatures build() { 546 return new CatalogFeatures(this); 547 } 548 } 549 }