Class Adjusting.XML

  • Enclosing class:
    Adjusting

    public static final class Adjusting.XML
    extends Object
    Class that collects adjustment APIs for affecting the behavior of PL/Java's XML support.

    XML parser behavior adjustments

    Retrieving or verifying the XML content in a JDBC SQLXML object can involve applying an XML parser. The full XML specification includes features that can require an XML parser to retrieve external resources or consume unexpected amounts of memory. The full feature support may be an asset in an environment where the XML content will always be from a known, trusted source, or a liability if less is known about the XML content being processed.

    The Open Web Application Security Project (OWASP) advocates for the default use of settings that strictly limit the related features of Java XML parsers, as outlined in a "cheat sheet" the organization publishes. The strict default settings can then be selectively relaxed in applications where the features are needed and the content is sufficiently trusted.

    However, the recommended defaults really are severely restrictive (for example, disabling document-type declarations by default will cause PL/Java's SQLXML implementation to reject all XML values that contain DTDs). Therefore, there must be a simple and clear way for code to selectively adjust the settings, or adopting the strictest settings by default would pose an unacceptable burden to developers.

    The usual way that Java XML parsers expose their settings for adjustment is through setFeature or setProperty methods that must be passed particular URIs that identify adjustable features, and objects of appropriate types (often boolean) as the values for those properties. The supported properties and the URIs that identify them can be different from one parser implementation to another or one version to another. That is not the "simple and clear" adjustment mechanism needed here. Furthermore, the JDBC SQLXML API conceals much of the complexity of configuring any underlying XML parser behind a simple getSource method whose result can be used directly with other Java APIs expecting some flavor of Source object, and for some of those flavors, the returned object does not even expose the methods one would need to call to adjust the underlying parser, if any.

    Hence this adjustment API. JDBC already provides for extensibility of the SQLXML.getSource method; it is passed the class object for a desired subtype of Source and, if the implementation supports it, returns an object of that type. The subtypes that every conformant implementation must support are StreamSource, SAXSource, StAXSource, and DOMSource. If null is passed, the implementation will choose which flavor to return, often based on internal implementation details making one most natural or efficient.

    The types Adjusting.XML.SAXSource, Adjusting.XML.StAXSource, and Adjusting.XML.DOMSource are used the same way, by passing the corresponding class literal to SQLXML's getSource method, which will return an object providing the chainable adjustment methods of Adjusting.XML.Source, with the chain ending in a get method that returns the corresponding Java Source object, configured as adjusted.

    Example:

    SAXSource src1 = sqx1.getSource(SAXSource.class);
    SAXSource src2 = sqx2.getSource(Adjusting.XML.SAXSource.class)
                         .allowDTD(true).get();
    
    src1 would be assigned a SAXSource object configured with the OWASP-recommended defaults, which will not allow the content to have a DTD, among other restrictions, while src2 would be assigned a SAXSource object configured with the other default restrictions (as if the allowDTD(true) is preceded by an implied defaults()), but with DTD parsing enabled.

    No Adjusting.XML.StreamSource is needed or provided, as any application code that requests a StreamSource will have to provide and configure its own parser anyway.

    Like passing null to getSource, passing the parent interface Adjusting.XML.Source.class will allow the implementation to choose which subtype of Adjusting.XML.Source to return. The object returned by get can then be passed directly to Java APIs like Transformer that accept several flavors of Source, or examined to see of what class it is.

    • Method Detail

      • setFirstSupported

        public static <T,​V extends T> Exception setFirstSupported​(Adjusting.XML.SetMethod<? super T> setter,
                                                                        V value,
                                                                        List<Class<? extends Exception>> expected,
                                                                        Exception caught,
                                                                        Consumer<? super Exception> onUnexpected,
                                                                        String... names)
        Attempts a given action (typically to set something) using a given value, trying one or more supplied keys in order until the action succeeds with no exception.

        This logic is common to the setFirstSupportedFeature and setFirstSupportedProperty methods, and is exposed here because it may be useful for other tasks in Java's XML APIs, such as configuring Transformers.

        If any attempt succeeds, null is returned. If no attempt succeeds, the first exception caught is returned, with any exceptions from the subsequent attempts retrievable from it with getSuppressed. The return is immediate, without any remaining names being tried, if an exception is caught that is not assignable to a class in the expected list. Such an exception is returned (or added to the suppressed list of an exception already to be returned) only if the onUnexpected handler is null; otherwise, it is passed to the handler and does not affect the method's return.

        For some purposes, a single call of this method may not suffice: if alternate means to establish a desired configuration have existed and are not simply alternate property names that will accept the same value. For such a case, this method may be called more than once. The caller abandons the sequence of calls after the first call that returns null (indicating that it either succeeded, or incurred an unexpected exception and passed it to the onUnexpected handler. Otherwise, the exception returned by the first call can be passed as caught to the next call, instead of passing the usual null. (When a non-null caught is passed, it will be returned on failure, even if an unexpected exception has been caught; therefore, should it ever be necessary to chain more than two of these calls, the caller should abandon the sequence as soon as a call returns null or returns its caught argument with no growth of its suppressed list.)

        Parameters:
        setter - typically a method reference for a method that takes a string key and some value.
        value - the value to pass to the setter
        expected - a list of exception classes that can be foreseen to indicate that a key was not recognized, and the operation should be retried with the next possible key.
        caught - null, or an exception returned by a preceding call if an operation cannot be implemented with one call of this method
        onUnexpected - invoked, if non-null, on an Exception that is caught and matches nothing in the expected list, instead of returning it. If this parameter is null, such an exception is returned (or added to the suppressed list of the exception to be returned), just as for expected exceptions, but the return is immediate, without trying remaining names, if any.
        names - one or more String keys to be tried in order until the action succeeds.
        Returns:
        null if any attempt succeeded, or if the first exception caught was passed to the onUnexpected handler; otherwise the first exception caught (if the caller supplied a non-null caught, then that exception), which may have further exceptions in its suppressed list.