# HG changeset patch # User dfuchs # Date 1366204999 -7200 # Node ID fad6560cb32a20d4a998fdb1ef5575059b629728 # Parent 3f9817b8b0e0cc824d66668ebe53759e9f886ad9 8005954: JAXP Plugability Layer should use java.util.ServiceLoader Summary: This fix replaces manual processing of files under META-INF/services in JAXP factories by calls to java.util.ServiceLoader. Reviewed-by: alanb, joehw, mchung diff --git a/src/javax/xml/datatype/DatatypeFactory.java b/src/javax/xml/datatype/DatatypeFactory.java --- a/src/javax/xml/datatype/DatatypeFactory.java +++ b/src/javax/xml/datatype/DatatypeFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,8 +25,8 @@ package javax.xml.datatype; +import java.math.BigDecimal; import java.math.BigInteger; -import java.math.BigDecimal; import java.util.GregorianCalendar; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -34,12 +34,12 @@ /** *

Factory that creates new javax.xml.datatype Objects that map XML to/from Java Objects.

* - *

{@link #newInstance()} is used to create a new DatatypeFactory. - * The following implementation resolution mechanisms are used in the following order:

+ *

A new instance of the DatatypeFactory is created through the {@link #newInstance()} method + * that uses the following implementation resolution mechanisms to determine an implementation:

*
    *
  1. * If the system property specified by {@link #DATATYPEFACTORY_PROPERTY}, "javax.xml.datatype.DatatypeFactory", - * exists, a class with the name of the property's value is instantiated. + * exists, a class with the name of the property value is instantiated. * Any Exception thrown during the instantiation process is wrapped as a {@link DatatypeConfigurationException}. *
  2. *
  3. @@ -48,8 +48,12 @@ * and processed as documented in the prior step. *
  4. *
  5. - * The services resolution mechanism is used, e.g. META-INF/services/java.xml.datatype.DatatypeFactory. - * Any Exception thrown during the instantiation process is wrapped as a {@link DatatypeConfigurationException}. + * Uses the service-provider loading facilities, defined by the {@link java.util.ServiceLoader} class, to attempt + * to locate and load an implementation of the service. + *
    + * In case of {@link java.util.ServiceConfigurationError service + * configuration error} a {@link javax.xml.datatype.DatatypeConfigurationException} + * will be thrown. *
  6. *
  7. * The final mechanism is to attempt to instantiate the Class specified by @@ -67,26 +71,33 @@ */ public abstract class DatatypeFactory { - /** - *

    Default property name as defined in JSR 206: Java(TM) API for XML Processing (JAXP) 1.3.

    - * - *

    Default value is javax.xml.datatype.DatatypeFactory.

    - */ - public static final String DATATYPEFACTORY_PROPERTY = "javax.xml.datatype.DatatypeFactory"; + /** + *

    Default property name as defined in JSR 206: Java(TM) API for XML Processing (JAXP) 1.3.

    + * + *

    Default value is javax.xml.datatype.DatatypeFactory.

    + */ + public static final String DATATYPEFACTORY_PROPERTY = + // We use a String constant here, rather than calling + // DatatypeFactory.class.getName() - in order to make javadoc + // generate a See Also: Constant Field Value link. + "javax.xml.datatype.DatatypeFactory"; - /** - *

    Default implementation class name as defined in - * JSR 206: Java(TM) API for XML Processing (JAXP) 1.3.

    - * - *

    Implementers should specify the name of an appropriate class - * to be instantiated if no other implementation resolution mechanism - * succeeds.

    - * - *

    Users should not refer to this field; it is intended only to - * document a factory implementation detail. - *

    - */ - public static final String DATATYPEFACTORY_IMPLEMENTATION_CLASS = new String("com.sun.org.apache.xerces.internal.jaxp.datatype.DatatypeFactoryImpl"); + /** + *

    Default implementation class name as defined in + * JSR 206: Java(TM) API for XML Processing (JAXP) 1.3.

    + * + *

    Implementers should specify the name of an appropriate class + * to be instantiated if no other implementation resolution mechanism + * succeeds.

    + * + *

    Users should not refer to this field; it is intended only to + * document a factory implementation detail. + *

    + */ + public static final String DATATYPEFACTORY_IMPLEMENTATION_CLASS = + // We use new String() here to prevent javadoc from generating + // a See Also: Constant Field Value link. + new String("com.sun.org.apache.xerces.internal.jaxp.datatype.DatatypeFactoryImpl"); /** * http://www.w3.org/TR/xpath-datamodel/#xdtschema defines two regexps @@ -101,40 +112,36 @@ private static final Pattern XDTSCHEMA_DTD = Pattern.compile("[^YM]*[DT].*"); - /** - *

    Protected constructor to prevent instaniation outside of package.

    - * - *

    Use {@link #newInstance()} to create a DatatypeFactory.

    - */ - protected DatatypeFactory() { - } + /** + *

    Protected constructor to prevent instaniation outside of package.

    + * + *

    Use {@link #newInstance()} to create a DatatypeFactory.

    + */ + protected DatatypeFactory() { + } - /** - *

    Obtain a new instance of a DatatypeFactory.

    - * + /** + *

    Obtain a new instance of a DatatypeFactory.

    + * *

    The implementation resolution mechanisms are defined in this * Class's documentation.

    - * - * @return New instance of a DatatypeFactory - * - * @throws DatatypeConfigurationException If the implementation is not - * available or cannot be instantiated. + * + * @return New instance of a DatatypeFactory + * + * @throws DatatypeConfigurationException If the implementation is not + * available or cannot be instantiated. * * @see #newInstance(String factoryClassName, ClassLoader classLoader) - */ - public static DatatypeFactory newInstance() - throws DatatypeConfigurationException { + */ + public static DatatypeFactory newInstance() + throws DatatypeConfigurationException { - try { - return (DatatypeFactory) FactoryFinder.find( - /* The default property name according to the JAXP spec */ - DATATYPEFACTORY_PROPERTY, - /* The fallback implementation class name */ - DATATYPEFACTORY_IMPLEMENTATION_CLASS); - } catch (FactoryFinder.ConfigurationError e) { - throw new DatatypeConfigurationException(e.getMessage(), e.getException()); - } - } + return FactoryFinder.find( + /* The default property name according to the JAXP spec */ + DatatypeFactory.class, + /* The fallback implementation class name */ + DATATYPEFACTORY_IMPLEMENTATION_CLASS); + } /** *

    Obtain a new instance of a DatatypeFactory from class name. @@ -172,57 +179,54 @@ */ public static DatatypeFactory newInstance(String factoryClassName, ClassLoader classLoader) throws DatatypeConfigurationException { - try { - return (DatatypeFactory) FactoryFinder.newInstance(factoryClassName, classLoader, false); - } catch (FactoryFinder.ConfigurationError e) { - throw new DatatypeConfigurationException(e.getMessage(), e.getException()); - } - } + return FactoryFinder.newInstance(DatatypeFactory.class, + factoryClassName, classLoader, false); + } - /** - *

    Obtain a new instance of a Duration - * specifying the Duration as its string representation, "PnYnMnDTnHnMnS", - * as defined in XML Schema 1.0 section 3.2.6.1.

    - * - *

    XML Schema Part 2: Datatypes, 3.2.6 duration, defines duration as:

    - *
    - * duration represents a duration of time. - * The value space of duration is a six-dimensional space where the coordinates designate the - * Gregorian year, month, day, hour, minute, and second components defined in Section 5.5.3.2 of [ISO 8601], respectively. - * These components are ordered in their significance by their order of appearance i.e. as - * year, month, day, hour, minute, and second. - *
    - *

    All six values are set and availabe from the created {@link Duration}

    + /** + *

    Obtain a new instance of a Duration + * specifying the Duration as its string representation, "PnYnMnDTnHnMnS", + * as defined in XML Schema 1.0 section 3.2.6.1.

    + * + *

    XML Schema Part 2: Datatypes, 3.2.6 duration, defines duration as:

    + *
    + * duration represents a duration of time. + * The value space of duration is a six-dimensional space where the coordinates designate the + * Gregorian year, month, day, hour, minute, and second components defined in Section 5.5.3.2 of [ISO 8601], respectively. + * These components are ordered in their significance by their order of appearance i.e. as + * year, month, day, hour, minute, and second. + *
    + *

    All six values are set and available from the created {@link Duration}

    * *

    The XML Schema specification states that values can be of an arbitrary size. * Implementations may chose not to or be incapable of supporting arbitrarily large and/or small values. * An {@link UnsupportedOperationException} will be thrown with a message indicating implementation limits * if implementation capacities are exceeded.

    - * - * @param lexicalRepresentation String representation of a Duration. - * - * @return New Duration created from parsing the lexicalRepresentation. - * - * @throws IllegalArgumentException If lexicalRepresentation is not a valid representation of a Duration. - * @throws UnsupportedOperationException If implementation cannot support requested values. - * @throws NullPointerException if lexicalRepresentation is null. - */ - public abstract Duration newDuration(final String lexicalRepresentation); + * + * @param lexicalRepresentation String representation of a Duration. + * + * @return New Duration created from parsing the lexicalRepresentation. + * + * @throws IllegalArgumentException If lexicalRepresentation is not a valid representation of a Duration. + * @throws UnsupportedOperationException If implementation cannot support requested values. + * @throws NullPointerException if lexicalRepresentation is null. + */ + public abstract Duration newDuration(final String lexicalRepresentation); - /** - *

    Obtain a new instance of a Duration - * specifying the Duration as milliseconds.

    - * - *

    XML Schema Part 2: Datatypes, 3.2.6 duration, defines duration as:

    - *
    - * duration represents a duration of time. - * The value space of duration is a six-dimensional space where the coordinates designate the - * Gregorian year, month, day, hour, minute, and second components defined in Section 5.5.3.2 of [ISO 8601], respectively. - * These components are ordered in their significance by their order of appearance i.e. as - * year, month, day, hour, minute, and second. - *
    + /** + *

    Obtain a new instance of a Duration + * specifying the Duration as milliseconds.

    + * + *

    XML Schema Part 2: Datatypes, 3.2.6 duration, defines duration as:

    + *
    + * duration represents a duration of time. + * The value space of duration is a six-dimensional space where the coordinates designate the + * Gregorian year, month, day, hour, minute, and second components defined in Section 5.5.3.2 of [ISO 8601], respectively. + * These components are ordered in their significance by their order of appearance i.e. as + * year, month, day, hour, minute, and second. + *
    *

    All six values are set by computing their values from the specified milliseconds - * and are availabe using the get methods of the created {@link Duration}. + * and are available using the get methods of the created {@link Duration}. * The values conform to and are defined by:

    * - * - *

    The default start instance is defined by {@link GregorianCalendar}'s use of the start of the epoch: i.e., - * {@link java.util.Calendar#YEAR} = 1970, - * {@link java.util.Calendar#MONTH} = {@link java.util.Calendar#JANUARY}, - * {@link java.util.Calendar#DATE} = 1, etc. - * This is important as there are variations in the Gregorian Calendar, - * e.g. leap years have different days in the month = {@link java.util.Calendar#FEBRUARY} - * so the result of {@link Duration#getMonths()} and {@link Duration#getDays()} can be influenced.

    - * - * @param durationInMilliSeconds Duration in milliseconds to create. - * - * @return New Duration representing durationInMilliSeconds. - */ - public abstract Duration newDuration(final long durationInMilliSeconds); + * + *

    The default start instance is defined by {@link GregorianCalendar}'s use of the start of the epoch: i.e., + * {@link java.util.Calendar#YEAR} = 1970, + * {@link java.util.Calendar#MONTH} = {@link java.util.Calendar#JANUARY}, + * {@link java.util.Calendar#DATE} = 1, etc. + * This is important as there are variations in the Gregorian Calendar, + * e.g. leap years have different days in the month = {@link java.util.Calendar#FEBRUARY} + * so the result of {@link Duration#getMonths()} and {@link Duration#getDays()} can be influenced.

    + * + * @param durationInMilliSeconds Duration in milliseconds to create. + * + * @return New Duration representing durationInMilliSeconds. + */ + public abstract Duration newDuration(final long durationInMilliSeconds); - /** - *

    Obtain a new instance of a Duration - * specifying the Duration as isPositive, years, months, days, hours, minutes, seconds.

    - * + /** + *

    Obtain a new instance of a Duration + * specifying the Duration as isPositive, years, months, days, hours, minutes, seconds.

    + * *

    The XML Schema specification states that values can be of an arbitrary size. * Implementations may chose not to or be incapable of supporting arbitrarily large and/or small values. * An {@link UnsupportedOperationException} will be thrown with a message indicating implementation limits @@ -257,35 +261,35 @@ * *

    A null value indicates that field is not set.

    * - * @param isPositive Set to false to create a negative duration. When the length - * of the duration is zero, this parameter will be ignored. - * @param years of this Duration - * @param months of this Duration - * @param days of this Duration - * @param hours of this Duration - * @param minutes of this Duration - * @param seconds of this Duration - * - * @return New Duration created from the specified values. - * - * @throws IllegalArgumentException If the values are not a valid representation of a - * Duration: if all the fields (years, months, ...) are null or - * if any of the fields is negative. - * @throws UnsupportedOperationException If implementation cannot support requested values. - */ - public abstract Duration newDuration( - final boolean isPositive, - final BigInteger years, - final BigInteger months, - final BigInteger days, - final BigInteger hours, - final BigInteger minutes, - final BigDecimal seconds); + * @param isPositive Set to false to create a negative duration. When the length + * of the duration is zero, this parameter will be ignored. + * @param years of this Duration + * @param months of this Duration + * @param days of this Duration + * @param hours of this Duration + * @param minutes of this Duration + * @param seconds of this Duration + * + * @return New Duration created from the specified values. + * + * @throws IllegalArgumentException If the values are not a valid representation of a + * Duration: if all the fields (years, months, ...) are null or + * if any of the fields is negative. + * @throws UnsupportedOperationException If implementation cannot support requested values. + */ + public abstract Duration newDuration( + final boolean isPositive, + final BigInteger years, + final BigInteger months, + final BigInteger days, + final BigInteger hours, + final BigInteger minutes, + final BigDecimal seconds); - /** - *

    Obtain a new instance of a Duration - * specifying the Duration as isPositive, years, months, days, hours, minutes, seconds.

    - * + /** + *

    Obtain a new instance of a Duration + * specifying the Duration as isPositive, years, months, days, hours, minutes, seconds.

    + * *

    A {@link DatatypeConstants#FIELD_UNDEFINED} value indicates that field is not set.

    * * @param isPositive Set to false to create a negative duration. When the length @@ -297,113 +301,113 @@ * @param minutes of this Duration * @param seconds of this Duration * - * @return New Duration created from the specified values. - * - * @throws IllegalArgumentException If the values are not a valid representation of a - * Duration: if any of the fields is negative. - * - * @see #newDuration( - * boolean isPositive, - * BigInteger years, - * BigInteger months, - * BigInteger days, - * BigInteger hours, - * BigInteger minutes, - * BigDecimal seconds) - */ - public Duration newDuration( - final boolean isPositive, - final int years, - final int months, - final int days, - final int hours, - final int minutes, - final int seconds) { + * @return New Duration created from the specified values. + * + * @throws IllegalArgumentException If the values are not a valid representation of a + * Duration: if any of the fields is negative. + * + * @see #newDuration( + * boolean isPositive, + * BigInteger years, + * BigInteger months, + * BigInteger days, + * BigInteger hours, + * BigInteger minutes, + * BigDecimal seconds) + */ + public Duration newDuration( + final boolean isPositive, + final int years, + final int months, + final int days, + final int hours, + final int minutes, + final int seconds) { - // years may not be set - BigInteger realYears = (years != DatatypeConstants.FIELD_UNDEFINED) ? BigInteger.valueOf((long) years) : null; + // years may not be set + BigInteger realYears = (years != DatatypeConstants.FIELD_UNDEFINED) ? BigInteger.valueOf((long) years) : null; - // months may not be set - BigInteger realMonths = (months != DatatypeConstants.FIELD_UNDEFINED) ? BigInteger.valueOf((long) months) : null; + // months may not be set + BigInteger realMonths = (months != DatatypeConstants.FIELD_UNDEFINED) ? BigInteger.valueOf((long) months) : null; - // days may not be set - BigInteger realDays = (days != DatatypeConstants.FIELD_UNDEFINED) ? BigInteger.valueOf((long) days) : null; + // days may not be set + BigInteger realDays = (days != DatatypeConstants.FIELD_UNDEFINED) ? BigInteger.valueOf((long) days) : null; - // hours may not be set - BigInteger realHours = (hours != DatatypeConstants.FIELD_UNDEFINED) ? BigInteger.valueOf((long) hours) : null; + // hours may not be set + BigInteger realHours = (hours != DatatypeConstants.FIELD_UNDEFINED) ? BigInteger.valueOf((long) hours) : null; - // minutes may not be set - BigInteger realMinutes = (minutes != DatatypeConstants.FIELD_UNDEFINED) ? BigInteger.valueOf((long) minutes) : null; + // minutes may not be set + BigInteger realMinutes = (minutes != DatatypeConstants.FIELD_UNDEFINED) ? BigInteger.valueOf((long) minutes) : null; - // seconds may not be set - BigDecimal realSeconds = (seconds != DatatypeConstants.FIELD_UNDEFINED) ? BigDecimal.valueOf((long) seconds) : null; + // seconds may not be set + BigDecimal realSeconds = (seconds != DatatypeConstants.FIELD_UNDEFINED) ? BigDecimal.valueOf((long) seconds) : null; - return newDuration( - isPositive, - realYears, - realMonths, - realDays, - realHours, - realMinutes, - realSeconds - ); - } + return newDuration( + isPositive, + realYears, + realMonths, + realDays, + realHours, + realMinutes, + realSeconds + ); + } - /** - *

    Create a Duration of type xdt:dayTimeDuration by parsing its String representation, - * "PnDTnHnMnS", - * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration.

    - * - *

    The datatype xdt:dayTimeDuration is a subtype of xs:duration - * whose lexical representation contains only day, hour, minute, and second components. - * This datatype resides in the namespace http://www.w3.org/2003/11/xpath-datatypes.

    - * - *

    All four values are set and availabe from the created {@link Duration}

    - * + /** + *

    Create a Duration of type xdt:dayTimeDuration by parsing its String representation, + * "PnDTnHnMnS", + * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration.

    + * + *

    The datatype xdt:dayTimeDuration is a subtype of xs:duration + * whose lexical representation contains only day, hour, minute, and second components. + * This datatype resides in the namespace http://www.w3.org/2003/11/xpath-datatypes.

    + * + *

    All four values are set and available from the created {@link Duration}

    + * *

    The XML Schema specification states that values can be of an arbitrary size. * Implementations may chose not to or be incapable of supporting arbitrarily large and/or small values. * An {@link UnsupportedOperationException} will be thrown with a message indicating implementation limits * if implementation capacities are exceeded.

    * - * @param lexicalRepresentation Lexical representation of a duration. - * - * @return New Duration created using the specified lexicalRepresentation. - * - * @throws IllegalArgumentException If lexicalRepresentation is not a valid representation of a Duration expressed only in terms of days and time. - * @throws UnsupportedOperationException If implementation cannot support requested values. - * @throws NullPointerException If lexicalRepresentation is null. - */ - public Duration newDurationDayTime(final String lexicalRepresentation) { - // lexicalRepresentation must be non-null - if (lexicalRepresentation == null) { - throw new NullPointerException( - "Trying to create an xdt:dayTimeDuration with an invalid" - + " lexical representation of \"null\""); - } - - // test lexicalRepresentation against spec regex - Matcher matcher = XDTSCHEMA_DTD.matcher(lexicalRepresentation); - if (!matcher.matches()) { - throw new IllegalArgumentException( - "Trying to create an xdt:dayTimeDuration with an invalid" - + " lexical representation of \"" + lexicalRepresentation - + "\", data model requires years and months only."); - } - - return newDuration(lexicalRepresentation); + * @param lexicalRepresentation Lexical representation of a duration. + * + * @return New Duration created using the specified lexicalRepresentation. + * + * @throws IllegalArgumentException If lexicalRepresentation is not a valid representation of a Duration expressed only in terms of days and time. + * @throws UnsupportedOperationException If implementation cannot support requested values. + * @throws NullPointerException If lexicalRepresentation is null. + */ + public Duration newDurationDayTime(final String lexicalRepresentation) { + // lexicalRepresentation must be non-null + if (lexicalRepresentation == null) { + throw new NullPointerException( + "Trying to create an xdt:dayTimeDuration with an invalid" + + " lexical representation of \"null\""); } - /** - *

    Create a Duration of type xdt:dayTimeDuration using the specified milliseconds as defined in - * - * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration.

    - * - *

    The datatype xdt:dayTimeDuration is a subtype of xs:duration - * whose lexical representation contains only day, hour, minute, and second components. - * This datatype resides in the namespace http://www.w3.org/2003/11/xpath-datatypes.

    - * + // test lexicalRepresentation against spec regex + Matcher matcher = XDTSCHEMA_DTD.matcher(lexicalRepresentation); + if (!matcher.matches()) { + throw new IllegalArgumentException( + "Trying to create an xdt:dayTimeDuration with an invalid" + + " lexical representation of \"" + lexicalRepresentation + + "\", data model requires years and months only."); + } + + return newDuration(lexicalRepresentation); + } + + /** + *

    Create a Duration of type xdt:dayTimeDuration using the specified milliseconds as defined in + * + * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration.

    + * + *

    The datatype xdt:dayTimeDuration is a subtype of xs:duration + * whose lexical representation contains only day, hour, minute, and second components. + * This datatype resides in the namespace http://www.w3.org/2003/11/xpath-datatypes.

    + * *

    All four values are set by computing their values from the specified milliseconds - * and are availabe using the get methods of the created {@link Duration}. + * and are available using the get methods of the created {@link Duration}. * The values conform to and are defined by:

    * - * - *

    The default start instance is defined by {@link GregorianCalendar}'s use of the start of the epoch: i.e., - * {@link java.util.Calendar#YEAR} = 1970, - * {@link java.util.Calendar#MONTH} = {@link java.util.Calendar#JANUARY}, - * {@link java.util.Calendar#DATE} = 1, etc. - * This is important as there are variations in the Gregorian Calendar, - * e.g. leap years have different days in the month = {@link java.util.Calendar#FEBRUARY} - * so the result of {@link Duration#getDays()} can be influenced.

    - * + * + *

    The default start instance is defined by {@link GregorianCalendar}'s use of the start of the epoch: i.e., + * {@link java.util.Calendar#YEAR} = 1970, + * {@link java.util.Calendar#MONTH} = {@link java.util.Calendar#JANUARY}, + * {@link java.util.Calendar#DATE} = 1, etc. + * This is important as there are variations in the Gregorian Calendar, + * e.g. leap years have different days in the month = {@link java.util.Calendar#FEBRUARY} + * so the result of {@link Duration#getDays()} can be influenced.

    + * *

    Any remaining milliseconds after determining the day, hour, minute and second are discarded.

    * - * @param durationInMilliseconds Milliseconds of Duration to create. - * - * @return New Duration created with the specified durationInMilliseconds. - * - * @see - * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration - */ - public Duration newDurationDayTime(final long durationInMilliseconds) { + * @param durationInMilliseconds Milliseconds of Duration to create. + * + * @return New Duration created with the specified durationInMilliseconds. + * + * @see + * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration + */ + public Duration newDurationDayTime(final long durationInMilliseconds) { - return newDuration(durationInMilliseconds); - } + return newDuration(durationInMilliseconds); + } - /** - *

    Create a Duration of type xdt:dayTimeDuration using the specified - * day, hour, minute and second as defined in - * - * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration.

    - * - *

    The datatype xdt:dayTimeDuration is a subtype of xs:duration - * whose lexical representation contains only day, hour, minute, and second components. - * This datatype resides in the namespace http://www.w3.org/2003/11/xpath-datatypes.

    - * + /** + *

    Create a Duration of type xdt:dayTimeDuration using the specified + * day, hour, minute and second as defined in + * + * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration.

    + * + *

    The datatype xdt:dayTimeDuration is a subtype of xs:duration + * whose lexical representation contains only day, hour, minute, and second components. + * This datatype resides in the namespace http://www.w3.org/2003/11/xpath-datatypes.

    + * *

    The XML Schema specification states that values can be of an arbitrary size. * Implementations may chose not to or be incapable of supporting arbitrarily large and/or small values. * An {@link UnsupportedOperationException} will be thrown with a message indicating implementation limits @@ -454,102 +458,102 @@ * * @param isPositive Set to false to create a negative duration. When the length * of the duration is zero, this parameter will be ignored. - * @param day Day of Duration. - * @param hour Hour of Duration. - * @param minute Minute of Duration. - * @param second Second of Duration. - * - * @return New Duration created with the specified day, hour, minute - * and second. - * - * @throws IllegalArgumentException If the values are not a valid representation of a - * Duration: if all the fields (day, hour, ...) are null or - * if any of the fields is negative. - * @throws UnsupportedOperationException If implementation cannot support requested values. - */ - public Duration newDurationDayTime( - final boolean isPositive, - final BigInteger day, - final BigInteger hour, - final BigInteger minute, - final BigInteger second) { + * @param day Day of Duration. + * @param hour Hour of Duration. + * @param minute Minute of Duration. + * @param second Second of Duration. + * + * @return New Duration created with the specified day, hour, minute + * and second. + * + * @throws IllegalArgumentException If the values are not a valid representation of a + * Duration: if all the fields (day, hour, ...) are null or + * if any of the fields is negative. + * @throws UnsupportedOperationException If implementation cannot support requested values. + */ + public Duration newDurationDayTime( + final boolean isPositive, + final BigInteger day, + final BigInteger hour, + final BigInteger minute, + final BigInteger second) { - return newDuration( - isPositive, - null, // years - null, // months - day, - hour, - minute, - (second != null)? new BigDecimal(second):null - ); - } + return newDuration( + isPositive, + null, // years + null, // months + day, + hour, + minute, + (second != null)? new BigDecimal(second):null + ); + } - /** - *

    Create a Duration of type xdt:dayTimeDuration using the specified - * day, hour, minute and second as defined in - * - * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration.

    - * - *

    The datatype xdt:dayTimeDuration is a subtype of xs:duration - * whose lexical representation contains only day, hour, minute, and second components. - * This datatype resides in the namespace http://www.w3.org/2003/11/xpath-datatypes.

    - * + /** + *

    Create a Duration of type xdt:dayTimeDuration using the specified + * day, hour, minute and second as defined in + * + * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration.

    + * + *

    The datatype xdt:dayTimeDuration is a subtype of xs:duration + * whose lexical representation contains only day, hour, minute, and second components. + * This datatype resides in the namespace http://www.w3.org/2003/11/xpath-datatypes.

    + * *

    A {@link DatatypeConstants#FIELD_UNDEFINED} value indicates that field is not set.

    * * @param isPositive Set to false to create a negative duration. When the length * of the duration is zero, this parameter will be ignored. - * @param day Day of Duration. - * @param hour Hour of Duration. - * @param minute Minute of Duration. - * @param second Second of Duration. - * - * @return New Duration created with the specified day, hour, minute - * and second. - * - * @throws IllegalArgumentException If the values are not a valid representation of a - * Duration: if any of the fields (day, hour, ...) is negative. - */ - public Duration newDurationDayTime( - final boolean isPositive, - final int day, - final int hour, - final int minute, - final int second) { + * @param day Day of Duration. + * @param hour Hour of Duration. + * @param minute Minute of Duration. + * @param second Second of Duration. + * + * @return New Duration created with the specified day, hour, minute + * and second. + * + * @throws IllegalArgumentException If the values are not a valid representation of a + * Duration: if any of the fields (day, hour, ...) is negative. + */ + public Duration newDurationDayTime( + final boolean isPositive, + final int day, + final int hour, + final int minute, + final int second) { - return newDurationDayTime( - isPositive, - BigInteger.valueOf((long) day), - BigInteger.valueOf((long) hour), - BigInteger.valueOf((long) minute), - BigInteger.valueOf((long) second) - ); - } + return newDurationDayTime( + isPositive, + BigInteger.valueOf((long) day), + BigInteger.valueOf((long) hour), + BigInteger.valueOf((long) minute), + BigInteger.valueOf((long) second) + ); + } - /** - *

    Create a Duration of type xdt:yearMonthDuration by parsing its String representation, - * "PnYnM", - * XQuery 1.0 and XPath 2.0 Data Model, xdt:yearMonthDuration.

    - * - *

    The datatype xdt:yearMonthDuration is a subtype of xs:duration - * whose lexical representation contains only year and month components. - * This datatype resides in the namespace {@link javax.xml.XMLConstants#W3C_XPATH_DATATYPE_NS_URI}.

    - * - *

    Both values are set and availabe from the created {@link Duration}

    - * + /** + *

    Create a Duration of type xdt:yearMonthDuration by parsing its String representation, + * "PnYnM", + * XQuery 1.0 and XPath 2.0 Data Model, xdt:yearMonthDuration.

    + * + *

    The datatype xdt:yearMonthDuration is a subtype of xs:duration + * whose lexical representation contains only year and month components. + * This datatype resides in the namespace {@link javax.xml.XMLConstants#W3C_XPATH_DATATYPE_NS_URI}.

    + * + *

    Both values are set and available from the created {@link Duration}

    + * *

    The XML Schema specification states that values can be of an arbitrary size. * Implementations may chose not to or be incapable of supporting arbitrarily large and/or small values. * An {@link UnsupportedOperationException} will be thrown with a message indicating implementation limits * if implementation capacities are exceeded.

    * - * @param lexicalRepresentation Lexical representation of a duration. - * - * @return New Duration created using the specified lexicalRepresentation. - * - * @throws IllegalArgumentException If lexicalRepresentation is not a valid representation of a Duration expressed only in terms of years and months. - * @throws UnsupportedOperationException If implementation cannot support requested values. - * @throws NullPointerException If lexicalRepresentation is null. - */ + * @param lexicalRepresentation Lexical representation of a duration. + * + * @return New Duration created using the specified lexicalRepresentation. + * + * @throws IllegalArgumentException If lexicalRepresentation is not a valid representation of a Duration expressed only in terms of years and months. + * @throws UnsupportedOperationException If implementation cannot support requested values. + * @throws NullPointerException If lexicalRepresentation is null. + */ public Duration newDurationYearMonth( final String lexicalRepresentation) { @@ -572,17 +576,17 @@ return newDuration(lexicalRepresentation); } - /** - *

    Create a Duration of type xdt:yearMonthDuration using the specified milliseconds as defined in - * - * XQuery 1.0 and XPath 2.0 Data Model, xdt:yearMonthDuration.

    - * - *

    The datatype xdt:yearMonthDuration is a subtype of xs:duration - * whose lexical representation contains only year and month components. - * This datatype resides in the namespace {@link javax.xml.XMLConstants#W3C_XPATH_DATATYPE_NS_URI}.

    - * + /** + *

    Create a Duration of type xdt:yearMonthDuration using the specified milliseconds as defined in + * + * XQuery 1.0 and XPath 2.0 Data Model, xdt:yearMonthDuration.

    + * + *

    The datatype xdt:yearMonthDuration is a subtype of xs:duration + * whose lexical representation contains only year and month components. + * This datatype resides in the namespace {@link javax.xml.XMLConstants#W3C_XPATH_DATATYPE_NS_URI}.

    + * *

    Both values are set by computing their values from the specified milliseconds - * and are availabe using the get methods of the created {@link Duration}. + * and are available using the get methods of the created {@link Duration}. * The values conform to and are defined by:

    * * - *

    The default start instance is defined by {@link GregorianCalendar}'s use of the start of the epoch: i.e., - * {@link java.util.Calendar#YEAR} = 1970, - * {@link java.util.Calendar#MONTH} = {@link java.util.Calendar#JANUARY}, - * {@link java.util.Calendar#DATE} = 1, etc. - * This is important as there are variations in the Gregorian Calendar, - * e.g. leap years have different days in the month = {@link java.util.Calendar#FEBRUARY} - * so the result of {@link Duration#getMonths()} can be influenced.

    - * + *

    The default start instance is defined by {@link GregorianCalendar}'s use of the start of the epoch: i.e., + * {@link java.util.Calendar#YEAR} = 1970, + * {@link java.util.Calendar#MONTH} = {@link java.util.Calendar#JANUARY}, + * {@link java.util.Calendar#DATE} = 1, etc. + * This is important as there are variations in the Gregorian Calendar, + * e.g. leap years have different days in the month = {@link java.util.Calendar#FEBRUARY} + * so the result of {@link Duration#getMonths()} can be influenced.

    + * *

    Any remaining milliseconds after determining the year and month are discarded.

    - * - * @param durationInMilliseconds Milliseconds of Duration to create. - * - * @return New Duration created using the specified durationInMilliseconds. - */ + * + * @param durationInMilliseconds Milliseconds of Duration to create. + * + * @return New Duration created using the specified durationInMilliseconds. + */ public Duration newDurationYearMonth( final long durationInMilliseconds) { @@ -624,12 +628,12 @@ return newDurationYearMonth(isPositive, years, months); } - /** - *

    Create a Duration of type xdt:yearMonthDuration using the specified - * year and month as defined in - * - * XQuery 1.0 and XPath 2.0 Data Model, xdt:yearMonthDuration.

    - * + /** + *

    Create a Duration of type xdt:yearMonthDuration using the specified + * year and month as defined in + * + * XQuery 1.0 and XPath 2.0 Data Model, xdt:yearMonthDuration.

    + * *

    The XML Schema specification states that values can be of an arbitrary size. * Implementations may chose not to or be incapable of supporting arbitrarily large and/or small values. * An {@link UnsupportedOperationException} will be thrown with a message indicating implementation limits @@ -639,74 +643,74 @@ * * @param isPositive Set to false to create a negative duration. When the length * of the duration is zero, this parameter will be ignored. - * @param year Year of Duration. - * @param month Month of Duration. - * - * @return New Duration created using the specified year and month. - * - * @throws IllegalArgumentException If the values are not a valid representation of a - * Duration: if all of the fields (year, month) are null or - * if any of the fields is negative. - * @throws UnsupportedOperationException If implementation cannot support requested values. - */ - public Duration newDurationYearMonth( - final boolean isPositive, - final BigInteger year, - final BigInteger month) { + * @param year Year of Duration. + * @param month Month of Duration. + * + * @return New Duration created using the specified year and month. + * + * @throws IllegalArgumentException If the values are not a valid representation of a + * Duration: if all of the fields (year, month) are null or + * if any of the fields is negative. + * @throws UnsupportedOperationException If implementation cannot support requested values. + */ + public Duration newDurationYearMonth( + final boolean isPositive, + final BigInteger year, + final BigInteger month) { - return newDuration( - isPositive, - year, - month, - null, // days - null, // hours - null, // minutes - null // seconds - ); - } + return newDuration( + isPositive, + year, + month, + null, // days + null, // hours + null, // minutes + null // seconds + ); + } - /** - *

    Create a Duration of type xdt:yearMonthDuration using the specified - * year and month as defined in - * - * XQuery 1.0 and XPath 2.0 Data Model, xdt:yearMonthDuration.

    - * + /** + *

    Create a Duration of type xdt:yearMonthDuration using the specified + * year and month as defined in + * + * XQuery 1.0 and XPath 2.0 Data Model, xdt:yearMonthDuration.

    + * *

    A {@link DatatypeConstants#FIELD_UNDEFINED} value indicates that field is not set.

    * * @param isPositive Set to false to create a negative duration. When the length * of the duration is zero, this parameter will be ignored. - * @param year Year of Duration. - * @param month Month of Duration. - * - * @return New Duration created using the specified year and month. - * - * @throws IllegalArgumentException If the values are not a valid representation of a - * Duration: if any of the fields (year, month) is negative. - */ - public Duration newDurationYearMonth( - final boolean isPositive, - final int year, - final int month) { + * @param year Year of Duration. + * @param month Month of Duration. + * + * @return New Duration created using the specified year and month. + * + * @throws IllegalArgumentException If the values are not a valid representation of a + * Duration: if any of the fields (year, month) is negative. + */ + public Duration newDurationYearMonth( + final boolean isPositive, + final int year, + final int month) { - return newDurationYearMonth( - isPositive, - BigInteger.valueOf((long) year), - BigInteger.valueOf((long) month)); - } + return newDurationYearMonth( + isPositive, + BigInteger.valueOf((long) year), + BigInteger.valueOf((long) month)); + } - /** - *

    Create a new instance of an XMLGregorianCalendar.

    - * + /** + *

    Create a new instance of an XMLGregorianCalendar.

    + * *

    All date/time datatype fields set to {@link DatatypeConstants#FIELD_UNDEFINED} or null.

    * * @return New XMLGregorianCalendar with all date/time datatype fields set to * {@link DatatypeConstants#FIELD_UNDEFINED} or null. - */ - public abstract XMLGregorianCalendar newXMLGregorianCalendar(); + */ + public abstract XMLGregorianCalendar newXMLGregorianCalendar(); - /** - *

    Create a new XMLGregorianCalendar by parsing the String as a lexical representation.

    - * + /** + *

    Create a new XMLGregorianCalendar by parsing the String as a lexical representation.

    + * *

    Parsing the lexical string representation is defined in * XML Schema 1.0 Part 2, Section 3.2.[7-14].1, * Lexical Representation.

    @@ -721,344 +725,344 @@ *

    Except for the noted lexical/canonical representation mismatches * listed in * XML Schema 1.0 errata, Section 3.2.7.2.

    - * - * @param lexicalRepresentation Lexical representation of one the eight XML Schema date/time datatypes. - * - * @return XMLGregorianCalendar created from the lexicalRepresentation. - * - * @throws IllegalArgumentException If the lexicalRepresentation is not a valid XMLGregorianCalendar. - * @throws NullPointerException If lexicalRepresentation is null. - */ - public abstract XMLGregorianCalendar newXMLGregorianCalendar(final String lexicalRepresentation); + * + * @param lexicalRepresentation Lexical representation of one the eight XML Schema date/time datatypes. + * + * @return XMLGregorianCalendar created from the lexicalRepresentation. + * + * @throws IllegalArgumentException If the lexicalRepresentation is not a valid XMLGregorianCalendar. + * @throws NullPointerException If lexicalRepresentation is null. + */ + public abstract XMLGregorianCalendar newXMLGregorianCalendar(final String lexicalRepresentation); - /** - *

    Create an XMLGregorianCalendar from a {@link GregorianCalendar}.

    - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
    - * Field by Field Conversion from - * {@link GregorianCalendar} to an {@link XMLGregorianCalendar} - *
    java.util.GregorianCalendar fieldjavax.xml.datatype.XMLGregorianCalendar field
    ERA == GregorianCalendar.BC ? -YEAR : YEAR{@link XMLGregorianCalendar#setYear(int year)}
    MONTH + 1{@link XMLGregorianCalendar#setMonth(int month)}
    DAY_OF_MONTH{@link XMLGregorianCalendar#setDay(int day)}
    HOUR_OF_DAY, MINUTE, SECOND, MILLISECOND{@link XMLGregorianCalendar#setTime(int hour, int minute, int second, BigDecimal fractional)}
    - * (ZONE_OFFSET + DST_OFFSET) / (60*1000)
    - * (in minutes) - *
    {@link XMLGregorianCalendar#setTimezone(int offset)}* - *
    - *

    *conversion loss of information. It is not possible to represent - * a java.util.GregorianCalendar daylight savings timezone id in the - * XML Schema 1.0 date/time datatype representation.

    - * - *

    To compute the return value's TimeZone field, - *

    * @@ -109,7 +104,7 @@ * this method to print a lot of debug messages * to System.err about what it is doing and where it is looking at.

    * - *

    If you have problems loading {@link DocumentBuilder}s, try:

    + *

    If you have problems loading {@link SAXParser}s, try:

    *
          * java -Djaxp.debug=1 YourProgram ....
          * 
    @@ -117,21 +112,17 @@ * * @return A new instance of a SAXParserFactory. * - * @throws FactoryConfigurationError if the implementation is - * not available or cannot be instantiated. + * @throws FactoryConfigurationError in case of {@linkplain + * java.util.ServiceConfigurationError service configuration error} or if + * the implementation is not available or cannot be instantiated. */ public static SAXParserFactory newInstance() { - try { - return (SAXParserFactory) FactoryFinder.find( + return FactoryFinder.find( /* The default property name according to the JAXP spec */ - "javax.xml.parsers.SAXParserFactory", + SAXParserFactory.class, /* The fallback implementation class name */ "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl"); - } catch (FactoryFinder.ConfigurationError e) { - throw new FactoryConfigurationError(e.getException(), - e.getMessage()); - } } /** @@ -169,13 +160,9 @@ * @since 1.6 */ public static SAXParserFactory newInstance(String factoryClassName, ClassLoader classLoader){ - try { //do not fallback if given classloader can't find the class, throw exception - return (SAXParserFactory) FactoryFinder.newInstance(factoryClassName, classLoader, false); - } catch (FactoryFinder.ConfigurationError e) { - throw new FactoryConfigurationError(e.getException(), - e.getMessage()); - } + return FactoryFinder.newInstance(SAXParserFactory.class, + factoryClassName, classLoader, false); } /** @@ -266,22 +253,22 @@ * A list of the core features and properties can be found at * http://www.saxproject.org/

    * - *

    All implementations are required to support the {@link javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING} feature. - * When the feature is

    - * - * + *

    All implementations are required to support the {@link javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING} feature. + * When the feature is

    + * + * * @param name The name of the feature to be set. * @param value The value of the feature to be set. * @@ -320,17 +307,6 @@ SAXNotSupportedException; - - /*

    Get current state of canonicalization.

    - * - * @return current state canonicalization control - */ - /* - public boolean getCanonicalization() { - return canonicalState; - } - */ - /** * Gets the {@link Schema} object specified through * the {@link #setSchema(Schema schema)} method. @@ -357,17 +333,6 @@ ); } - /**

    Set canonicalization control to true or - * false.

    - * - * @param state of canonicalization - */ - /* - public void setCanonicalization(boolean state) { - canonicalState = state; - } - */ - /** *

    Set the {@link Schema} to be used by parsers created * from this factory.

    @@ -400,7 +365,7 @@ * Such configuration will cause a {@link SAXException} * exception when those properties are set on a {@link SAXParser}.

    * - *

    Note for implmentors

    + *

    Note for implementors

    *

    * A parser must be able to work with any {@link Schema} * implementation. However, parsers and schemas are allowed diff --git a/src/javax/xml/stream/FactoryFinder.java b/src/javax/xml/stream/FactoryFinder.java --- a/src/javax/xml/stream/FactoryFinder.java +++ b/src/javax/xml/stream/FactoryFinder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,15 +25,16 @@ package javax.xml.stream; -import java.io.BufferedReader; import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Iterator; import java.util.Properties; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; /** - *

    Implements pluggable Datatypes.

    + *

    Implements pluggable streams.

    * *

    This class is duplicated for each JAXP subpackage so keep it in * sync. It is package private for secure class loading.

    @@ -52,19 +53,19 @@ /** * Cache for properties in java.home/lib/jaxp.properties */ - static Properties cacheProps = new Properties(); + final private static Properties cacheProps = new Properties(); /** * Flag indicating if properties from java.home/lib/jaxp.properties * have been cached. */ - static volatile boolean firstTime = true; + private static volatile boolean firstTime = true; /** * Security support class use to check access control before * getting certain system resources. */ - static SecuritySupport ss = new SecuritySupport(); + final private static SecuritySupport ss = new SecuritySupport(); // Define system property "jaxp.debug" to get output static { @@ -103,25 +104,25 @@ try { if (cl == null) { if (useBSClsLoader) { - return Class.forName(className, true, FactoryFinder.class.getClassLoader()); + return Class.forName(className, false, FactoryFinder.class.getClassLoader()); } else { cl = ss.getContextClassLoader(); if (cl == null) { throw new ClassNotFoundException(); } else { - return cl.loadClass(className); + return Class.forName(className, false, cl); } } } else { - return cl.loadClass(className); + return Class.forName(className, false, cl); } } catch (ClassNotFoundException e1) { if (doFallback) { // Use current class loader - should always be bootstrap CL - return Class.forName(className, true, FactoryFinder.class.getClassLoader()); + return Class.forName(className, false, FactoryFinder.class.getClassLoader()); } else { throw e1; @@ -133,6 +134,9 @@ * Create an instance of a class. Delegates to method * getProviderClass() in order to load the class. * + * @param type Base class / Service interface of the factory to + * instantiate. + * * @param className Name of the concrete class corresponding to the * service provider * @@ -142,16 +146,19 @@ * @param doFallback True if the current ClassLoader should be tried as * a fallback if the class is not found using cl */ - static Object newInstance(String className, ClassLoader cl, boolean doFallback) - throws ConfigurationError + static T newInstance(Class type, String className, ClassLoader cl, boolean doFallback) + throws FactoryConfigurationError { - return newInstance(className, cl, doFallback, false); + return newInstance(type, className, cl, doFallback, false); } /** * Create an instance of a class. Delegates to method * getProviderClass() in order to load the class. * + * @param type Base class / Service interface of the factory to + * instantiate. + * * @param className Name of the concrete class corresponding to the * service provider * @@ -164,9 +171,12 @@ * @param useBSClsLoader True if cl=null actually meant bootstrap classLoader. This parameter * is needed since DocumentBuilderFactory/SAXParserFactory defined null as context classLoader. */ - static Object newInstance(String className, ClassLoader cl, boolean doFallback, boolean useBSClsLoader) - throws ConfigurationError + static T newInstance(Class type, String className, ClassLoader cl, + boolean doFallback, boolean useBSClsLoader) + throws FactoryConfigurationError { + assert type != null; + // make sure we have access to restricted packages if (System.getSecurityManager() != null) { if (className != null && className.startsWith(DEFAULT_PACKAGE)) { @@ -176,20 +186,23 @@ } try { - Class providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader); + Class providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader); + if (!type.isAssignableFrom(providerClass)) { + throw new ClassCastException(className + " cannot be cast to " + type.getName()); + } Object instance = providerClass.newInstance(); if (debug) { // Extra check to avoid computing cl strings dPrint("created new instance of " + providerClass + " using ClassLoader: " + cl); } - return instance; + return type.cast(instance); } catch (ClassNotFoundException x) { - throw new ConfigurationError( + throw new FactoryConfigurationError( "Provider " + className + " not found", x); } catch (Exception x) { - throw new ConfigurationError( + throw new FactoryConfigurationError( "Provider " + className + " could not be instantiated: " + x, x); } @@ -200,17 +213,18 @@ * * @return Class object of factory, never null * - * @param factoryId Name of the factory to find, same as - * a property name + * @param type Base class / Service interface of the + * factory to find. + * * @param fallbackClassName Implementation class name, if nothing else * is found. Use null to mean no fallback. * * Package private so this code can be shared. */ - static Object find(String factoryId, String fallbackClassName) - throws ConfigurationError + static T find(Class type, String fallbackClassName) + throws FactoryConfigurationError { - return find(factoryId, null, fallbackClassName); + return find(type, type.getName(), null, fallbackClassName); } /** @@ -218,6 +232,9 @@ * entry point. * @return Class object of factory, never null * + * @param type Base class / Service interface of the + * factory to find. + * * @param factoryId Name of the factory to find, same as * a property name * @@ -229,8 +246,8 @@ * * Package private so this code can be shared. */ - static Object find(String factoryId, ClassLoader cl, String fallbackClassName) - throws ConfigurationError + static T find(Class type, String factoryId, ClassLoader cl, String fallbackClassName) + throws FactoryConfigurationError { dPrint("find factoryId =" + factoryId); @@ -239,7 +256,9 @@ String systemProp = ss.getSystemProperty(factoryId); if (systemProp != null) { dPrint("found system property, value=" + systemProp); - return newInstance(systemProp, null, true); + // There's a bug here - because 'cl' is ignored. + // This will be handled separately. + return newInstance(type, systemProp, null, true); } } catch (SecurityException se) { @@ -250,7 +269,6 @@ // $java.home/lib/jaxp.properties if former not present String configFile = null; try { - String factoryClassName = null; if (firstTime) { synchronized (cacheProps) { if (firstTime) { @@ -269,130 +287,80 @@ if (ss.doesFileExist(f)) { dPrint("Read properties file "+f); cacheProps.load(ss.getFileInputStream(f)); + } + } } } } - } - } - factoryClassName = cacheProps.getProperty(factoryId); + final String factoryClassName = cacheProps.getProperty(factoryId); if (factoryClassName != null) { dPrint("found in " + configFile + " value=" + factoryClassName); - return newInstance(factoryClassName, null, true); + // There's a bug here - because 'cl' is ignored. + // This will be handled separately. + return newInstance(type, factoryClassName, null, true); } } catch (Exception ex) { if (debug) ex.printStackTrace(); } - // Try Jar Service Provider Mechanism - Object provider = findJarServiceProvider(factoryId); - if (provider != null) { - return provider; + if (type.getName().equals(factoryId)) { + // Try Jar Service Provider Mechanism + final T provider = findServiceProvider(type); + if (provider != null) { + return provider; + } + } else { + // We're in the case where a 'custom' factoryId was provided, + // and in every case where that happens, we expect that + // fallbackClassName will be null. + assert fallbackClassName == null; } if (fallbackClassName == null) { - throw new ConfigurationError( + throw new FactoryConfigurationError( "Provider for " + factoryId + " cannot be found", null); } dPrint("loaded from fallback value: " + fallbackClassName); - return newInstance(fallbackClassName, cl, true); + return newInstance(type, fallbackClassName, cl, true); } /* - * Try to find provider using Jar Service Provider Mechanism + * Try to find provider using the ServiceLoader API + * + * @param type Base class / Service interface of the factory to find. * * @return instance of provider class if found or null */ - private static Object findJarServiceProvider(String factoryId) - throws ConfigurationError - { - String serviceId = "META-INF/services/" + factoryId; - InputStream is = null; - - // First try the Context ClassLoader - ClassLoader cl = ss.getContextClassLoader(); - boolean useBSClsLoader = false; - if (cl != null) { - is = ss.getResourceAsStream(cl, serviceId); - - // If no provider found then try the current ClassLoader - if (is == null) { - cl = FactoryFinder.class.getClassLoader(); - is = ss.getResourceAsStream(cl, serviceId); - useBSClsLoader = true; - } - } else { - // No Context ClassLoader, try the current ClassLoader - cl = FactoryFinder.class.getClassLoader(); - is = ss.getResourceAsStream(cl, serviceId); - useBSClsLoader = true; - } - - if (is == null) { - // No provider found - return null; - } - - if (debug) { // Extra check to avoid computing cl strings - dPrint("found jar resource=" + serviceId + " using ClassLoader: " + cl); - } - - BufferedReader rd; + private static T findServiceProvider(final Class type) { try { - rd = new BufferedReader(new InputStreamReader(is, "UTF-8")); - } - catch (java.io.UnsupportedEncodingException e) { - rd = new BufferedReader(new InputStreamReader(is)); - } - - String factoryClassName = null; - try { - // XXX Does not handle all possible input as specified by the - // Jar Service Provider specification - factoryClassName = rd.readLine(); - rd.close(); - } catch (IOException x) { - // No provider found - return null; - } - - if (factoryClassName != null && !"".equals(factoryClassName)) { - dPrint("found in resource, value=" + factoryClassName); - - // Note: here we do not want to fall back to the current - // ClassLoader because we want to avoid the case where the - // resource file was found using one ClassLoader and the - // provider class was instantiated using a different one. - return newInstance(factoryClassName, cl, false, useBSClsLoader); - } - - // No provider found - return null; - } - - static class ConfigurationError extends Error { - private Exception exception; - - /** - * Construct a new instance with the specified detail string and - * exception. - */ - ConfigurationError(String msg, Exception x) { - super(msg); - this.exception = x; - } - - Exception getException() { - return exception; - } - /** - * use the exception chaining mechanism of JDK1.4 - */ - @Override - public Throwable getCause() { - return exception; - } - } + return AccessController.doPrivileged(new PrivilegedAction() { + @Override + public T run() { + final ServiceLoader serviceLoader = ServiceLoader.load(type); + final Iterator iterator = serviceLoader.iterator(); + if (iterator.hasNext()) { + return iterator.next(); + } else { + return null; + } + } + }); + } catch(ServiceConfigurationError e) { + // It is not possible to wrap an error directly in + // FactoryConfigurationError - so we need to wrap the + // ServiceConfigurationError in a RuntimeException. + // The alternative would be to modify the logic in + // FactoryConfigurationError to allow setting a + // Throwable as the cause, but that could cause + // compatibility issues down the road. + final RuntimeException x = new RuntimeException( + "Provider for " + type + " cannot be created", e); + final FactoryConfigurationError error = + new FactoryConfigurationError(x, x.getMessage()); + throw error; + } + } } diff --git a/src/javax/xml/stream/XMLEventFactory.java b/src/javax/xml/stream/XMLEventFactory.java --- a/src/javax/xml/stream/XMLEventFactory.java +++ b/src/javax/xml/stream/XMLEventFactory.java @@ -23,14 +23,14 @@ */ /* - * Copyright (c) 2009 by Oracle Corporation. All Rights Reserved. + * Copyright (c) 2009, 2013, by Oracle Corporation. All Rights Reserved. */ package javax.xml.stream; -import javax.xml.stream.events.*; +import java.util.Iterator; import javax.xml.namespace.NamespaceContext; import javax.xml.namespace.QName; -import java.util.Iterator; +import javax.xml.stream.events.*; /** * This interface defines a utility class for creating instances of * XMLEvents @@ -54,48 +54,59 @@ /** - * Create a new instance of the factory + * Creates a new instance of the factory in exactly the same manner as the + * {@link #newFactory()} method. * @throws FactoryConfigurationError if an instance of this factory cannot be loaded */ public static XMLEventFactory newInstance() throws FactoryConfigurationError { - return (XMLEventFactory) FactoryFinder.find( - JAXPFACTORYID, - DEFAULIMPL); + return FactoryFinder.find(XMLEventFactory.class, DEFAULIMPL); } /** * Create a new instance of the factory. + *

    * This static method creates a new factory instance. * This method uses the following ordered lookup procedure to determine * the XMLEventFactory implementation class to load: + *

    + *
      + *
    • * Use the javax.xml.stream.XMLEventFactory system property. + *
    • + *
    • * Use the properties file "lib/stax.properties" in the JRE directory. * This configuration file is in standard java.util.Properties format * and contains the fully qualified name of the implementation class * with the key being the system property defined above. - * Use the Services API (as detailed in the JAR specification), if available, - * to determine the classname. The Services API will look for a classname - * in the file META-INF/services/javax.xml.stream.XMLEventFactory in jars - * available to the runtime. - * Platform default XMLEventFactory instance. - * + *
    • + *
    • + * Use the service-provider loading facilities, defined by the + * {@link java.util.ServiceLoader} class, to attempt to locate and load an + * implementation of the service. + *
    • + *
    • + * Otherwise, the system-default implementation is returned. + *
    • + *
    + *

    * Once an application has obtained a reference to a XMLEventFactory it * can use the factory to configure and obtain stream instances. - * + *

    + *

    * Note that this is a new method that replaces the deprecated newInstance() method. * No changes in behavior are defined by this replacement method relative to * the deprecated method. - * - * @throws FactoryConfigurationError if an instance of this factory cannot be loaded + *

    + * @throws FactoryConfigurationError in case of {@linkplain + * java.util.ServiceConfigurationError service configuration error} or if + * the implementation is not available or cannot be instantiated. */ public static XMLEventFactory newFactory() throws FactoryConfigurationError { - return (XMLEventFactory) FactoryFinder.find( - JAXPFACTORYID, - DEFAULIMPL); + return FactoryFinder.find(XMLEventFactory.class, DEFAULIMPL); } /** @@ -116,40 +127,59 @@ public static XMLEventFactory newInstance(String factoryId, ClassLoader classLoader) throws FactoryConfigurationError { - try { - //do not fallback if given classloader can't find the class, throw exception - return (XMLEventFactory) FactoryFinder.find(factoryId, classLoader, null); - } catch (FactoryFinder.ConfigurationError e) { - throw new FactoryConfigurationError(e.getException(), - e.getMessage()); - } + //do not fallback if given classloader can't find the class, throw exception + return FactoryFinder.find(XMLEventFactory.class, factoryId, classLoader, null); } /** * Create a new instance of the factory. * If the classLoader argument is null, then the ContextClassLoader is used. + *

    + * This method uses the following ordered lookup procedure to determine + * the XMLEventFactory implementation class to load: + *

    + *
      + *
    • + * Use the value of the system property identified by {@code factoryId}. + *
    • + *
    • + * Use the properties file "lib/stax.properties" in the JRE directory. + * This configuration file is in standard java.util.Properties format + * and contains the fully qualified name of the implementation class + * with the key being the given {@code factoryId}. + *
    • + *
    • + * If {@code factoryId} is "javax.xml.stream.XMLEventFactory", + * use the service-provider loading facilities, defined by the + * {@link java.util.ServiceLoader} class, to attempt to locate and load an + * implementation of the service. + *
    • + *
    • + * Otherwise, throws a {@link FactoryConfigurationError}. + *
    • + *
    * + *

    * Note that this is a new method that replaces the deprecated - * newInstance(String factoryId, ClassLoader classLoader) method. + * {@link #newInstance(java.lang.String, java.lang.ClassLoader) + * newInstance(String factoryId, ClassLoader classLoader)} method. * No changes in behavior are defined by this replacement method relative * to the deprecated method. + *

    * * @param factoryId Name of the factory to find, same as * a property name * @param classLoader classLoader to use * @return the factory implementation - * @throws FactoryConfigurationError if an instance of this factory cannot be loaded + * @throws FactoryConfigurationError in case of {@linkplain + * java.util.ServiceConfigurationError service configuration error} or if + * the implementation is not available or cannot be instantiated. */ public static XMLEventFactory newFactory(String factoryId, - ClassLoader classLoader) + ClassLoader classLoader) throws FactoryConfigurationError { - try { - //do not fallback if given classloader can't find the class, throw exception - return (XMLEventFactory) FactoryFinder.find(factoryId, classLoader, null); - } catch (FactoryFinder.ConfigurationError e) { - throw new FactoryConfigurationError(e.getException(), - e.getMessage()); - } + //do not fallback if given classloader can't find the class, throw exception + return FactoryFinder.find(XMLEventFactory.class, factoryId, classLoader, null); } /** diff --git a/src/javax/xml/stream/XMLInputFactory.java b/src/javax/xml/stream/XMLInputFactory.java --- a/src/javax/xml/stream/XMLInputFactory.java +++ b/src/javax/xml/stream/XMLInputFactory.java @@ -23,13 +23,13 @@ */ /* - * Copyright (c) 2009 by Oracle Corporation. All Rights Reserved. + * Copyright (c) 2009, 2013, by Oracle Corporation. All Rights Reserved. */ package javax.xml.stream; +import javax.xml.stream.util.XMLEventAllocator; import javax.xml.transform.Source; -import javax.xml.stream.util.XMLEventAllocator; /** * Defines an abstract implementation of a factory for getting streams. @@ -144,48 +144,59 @@ protected XMLInputFactory(){} /** - * Create a new instance of the factory. + * Creates a new instance of the factory in exactly the same manner as the + * {@link #newFactory()} method. * @throws FactoryConfigurationError if an instance of this factory cannot be loaded */ public static XMLInputFactory newInstance() throws FactoryConfigurationError { - return (XMLInputFactory) FactoryFinder.find( - "javax.xml.stream.XMLInputFactory", - DEFAULIMPL); + return FactoryFinder.find(XMLInputFactory.class, DEFAULIMPL); } /** * Create a new instance of the factory. + *

    * This static method creates a new factory instance. * This method uses the following ordered lookup procedure to determine * the XMLInputFactory implementation class to load: + *

    + *
      + *
    • * Use the javax.xml.stream.XMLInputFactory system property. + *
    • + *
    • * Use the properties file "lib/stax.properties" in the JRE directory. * This configuration file is in standard java.util.Properties format * and contains the fully qualified name of the implementation class * with the key being the system property defined above. - * Use the Services API (as detailed in the JAR specification), if available, - * to determine the classname. The Services API will look for a classname - * in the file META-INF/services/javax.xml.stream.XMLInputFactory in jars - * available to the runtime. - * Platform default XMLInputFactory instance. - * + *
    • + *
    • + * Use the service-provider loading facilities, defined by the + * {@link java.util.ServiceLoader} class, to attempt to locate and load an + * implementation of the service. + *
    • + *
    • + * Otherwise, the system-default implementation is returned. + *
    • + *
    + *

    * Once an application has obtained a reference to a XMLInputFactory it * can use the factory to configure and obtain stream instances. - * + *

    + *

    * Note that this is a new method that replaces the deprecated newInstance() method. * No changes in behavior are defined by this replacement method relative to * the deprecated method. - * - * @throws FactoryConfigurationError if an instance of this factory cannot be loaded + *

    + * @throws FactoryConfigurationError in case of {@linkplain + * java.util.ServiceConfigurationError service configuration error} or if + * the implementation is not available or cannot be instantiated. */ public static XMLInputFactory newFactory() throws FactoryConfigurationError { - return (XMLInputFactory) FactoryFinder.find( - "javax.xml.stream.XMLInputFactory", - DEFAULIMPL); + return FactoryFinder.find(XMLInputFactory.class, DEFAULIMPL); } /** @@ -206,40 +217,60 @@ public static XMLInputFactory newInstance(String factoryId, ClassLoader classLoader) throws FactoryConfigurationError { - try { - //do not fallback if given classloader can't find the class, throw exception - return (XMLInputFactory) FactoryFinder.find(factoryId, classLoader, null); - } catch (FactoryFinder.ConfigurationError e) { - throw new FactoryConfigurationError(e.getException(), - e.getMessage()); - } + //do not fallback if given classloader can't find the class, throw exception + return FactoryFinder.find(XMLInputFactory.class, factoryId, classLoader, null); } /** * Create a new instance of the factory. * If the classLoader argument is null, then the ContextClassLoader is used. + *

    + * This method uses the following ordered lookup procedure to determine + * the XMLInputFactory implementation class to load: + *

    + *
      + *
    • + * Use the value of the system property identified by {@code factoryId}. + *
    • + *
    • + * Use the properties file "lib/stax.properties" in the JRE directory. + * This configuration file is in standard java.util.Properties format + * and contains the fully qualified name of the implementation class + * with the key being the given {@code factoryId}. + *
    • + *
    • + * If {@code factoryId} is "javax.xml.stream.XMLInputFactory", + * use the service-provider loading facilities, defined by the + * {@link java.util.ServiceLoader} class, to attempt to locate and load an + * implementation of the service. + *
    • + *
    • + * Otherwise, throws a {@link FactoryConfigurationError}. + *
    • + *
    * + *

    * Note that this is a new method that replaces the deprecated - * newInstance(String factoryId, ClassLoader classLoader) method. + * {@link #newInstance(java.lang.String, java.lang.ClassLoader) + * newInstance(String factoryId, ClassLoader classLoader)} method. * No changes in behavior are defined by this replacement method relative * to the deprecated method. + *

    * * @param factoryId Name of the factory to find, same as * a property name * @param classLoader classLoader to use * @return the factory implementation + * @throws FactoryConfigurationError in case of {@linkplain + * java.util.ServiceConfigurationError service configuration error} or if + * the implementation is not available or cannot be instantiated. * @throws FactoryConfigurationError if an instance of this factory cannot be loaded */ public static XMLInputFactory newFactory(String factoryId, ClassLoader classLoader) throws FactoryConfigurationError { - try { - //do not fallback if given classloader can't find the class, throw exception - return (XMLInputFactory) FactoryFinder.find(factoryId, classLoader, null); - } catch (FactoryFinder.ConfigurationError e) { - throw new FactoryConfigurationError(e.getException(), - e.getMessage()); - } + //do not fallback if given classloader can't find the class, throw exception + return FactoryFinder.find(XMLInputFactory.class, factoryId, classLoader, null); } /** diff --git a/src/javax/xml/stream/XMLOutputFactory.java b/src/javax/xml/stream/XMLOutputFactory.java --- a/src/javax/xml/stream/XMLOutputFactory.java +++ b/src/javax/xml/stream/XMLOutputFactory.java @@ -23,7 +23,7 @@ */ /* - * Copyright (c) 2009 by Oracle Corporation. All Rights Reserved. + * Copyright (c) 2009, 2013, by Oracle Corporation. All Rights Reserved. */ package javax.xml.stream; @@ -120,46 +120,58 @@ protected XMLOutputFactory(){} /** - * Create a new instance of the factory. + * Creates a new instance of the factory in exactly the same manner as the + * {@link #newFactory()} method. * @throws FactoryConfigurationError if an instance of this factory cannot be loaded */ public static XMLOutputFactory newInstance() throws FactoryConfigurationError { - return (XMLOutputFactory) FactoryFinder.find("javax.xml.stream.XMLOutputFactory", - DEFAULIMPL); + return FactoryFinder.find(XMLOutputFactory.class, DEFAULIMPL); } /** * Create a new instance of the factory. + *

    * This static method creates a new factory instance. This method uses the * following ordered lookup procedure to determine the XMLOutputFactory * implementation class to load: + *

    + *
      + *
    • * Use the javax.xml.stream.XMLOutputFactory system property. + *
    • + *
    • * Use the properties file "lib/stax.properties" in the JRE directory. * This configuration file is in standard java.util.Properties format * and contains the fully qualified name of the implementation class * with the key being the system property defined above. - * Use the Services API (as detailed in the JAR specification), if available, - * to determine the classname. The Services API will look for a classname - * in the file META-INF/services/javax.xml.stream.XMLOutputFactory in jars - * available to the runtime. - * Platform default XMLOutputFactory instance. - * + *
    • + *
    • + * Use the service-provider loading facilities, defined by the + * {@link java.util.ServiceLoader} class, to attempt to locate and load an + * implementation of the service. + *
    • + *
    • + * Otherwise, the system-default implementation is returned. + *
    • + *

      * Once an application has obtained a reference to a XMLOutputFactory it * can use the factory to configure and obtain stream instances. - * + *

      + *

      * Note that this is a new method that replaces the deprecated newInstance() method. * No changes in behavior are defined by this replacement method relative to the * deprecated method. - * - * @throws FactoryConfigurationError if an instance of this factory cannot be loaded + *

      + * @throws FactoryConfigurationError in case of {@linkplain + * java.util.ServiceConfigurationError service configuration error} or if + * the implementation is not available or cannot be instantiated. */ public static XMLOutputFactory newFactory() throws FactoryConfigurationError { - return (XMLOutputFactory) FactoryFinder.find("javax.xml.stream.XMLOutputFactory", - DEFAULIMPL); + return FactoryFinder.find(XMLOutputFactory.class, DEFAULIMPL); } /** @@ -179,42 +191,59 @@ public static XMLInputFactory newInstance(String factoryId, ClassLoader classLoader) throws FactoryConfigurationError { - try { - //do not fallback if given classloader can't find the class, throw exception - return (XMLInputFactory) FactoryFinder.find(factoryId, classLoader, null); - } catch (FactoryFinder.ConfigurationError e) { - throw new FactoryConfigurationError(e.getException(), - e.getMessage()); - } + //do not fallback if given classloader can't find the class, throw exception + return FactoryFinder.find(XMLInputFactory.class, factoryId, classLoader, null); } /** * Create a new instance of the factory. * If the classLoader argument is null, then the ContextClassLoader is used. + *

      + * This method uses the following ordered lookup procedure to determine + * the XMLOutputFactory implementation class to load: + *

      + *
        + *
      • + * Use the value of the system property identified by {@code factoryId}. + *
      • + *
      • + * Use the properties file "lib/stax.properties" in the JRE directory. + * This configuration file is in standard java.util.Properties format + * and contains the fully qualified name of the implementation class + * with the key being the given {@code factoryId}. + *
      • + *
      • + * If {@code factoryId} is "javax.xml.stream.XMLOutputFactory", + * use the service-provider loading facilities, defined by the + * {@link java.util.ServiceLoader} class, to attempt to locate and load an + * implementation of the service. + *
      • + *
      • + * Otherwise, throws a {@link FactoryConfigurationError}. + *
      • + *
      * + *

      * Note that this is a new method that replaces the deprecated - * newInstance(String factoryId, ClassLoader classLoader) method. - * - * No changes in behavior are defined by this replacement method relative - * to the deprecated method. - * + * {@link #newInstance(java.lang.String, java.lang.ClassLoader) + * newInstance(String factoryId, ClassLoader classLoader)} method. + * No changes in behavior are defined by this replacement method relative + * to the deprecated method. + *

      * * @param factoryId Name of the factory to find, same as * a property name * @param classLoader classLoader to use * @return the factory implementation - * @throws FactoryConfigurationError if an instance of this factory cannot be loaded + * @throws FactoryConfigurationError in case of {@linkplain + * java.util.ServiceConfigurationError service configuration error} or if + * the implementation is not available or cannot be instantiated. */ public static XMLOutputFactory newFactory(String factoryId, ClassLoader classLoader) throws FactoryConfigurationError { - try { - //do not fallback if given classloader can't find the class, throw exception - return (XMLOutputFactory) FactoryFinder.find(factoryId, classLoader, null); - } catch (FactoryFinder.ConfigurationError e) { - throw new FactoryConfigurationError(e.getException(), - e.getMessage()); - } + //do not fallback if given classloader can't find the class, throw exception + return FactoryFinder.find(XMLOutputFactory.class, factoryId, classLoader, null); } /** diff --git a/src/javax/xml/transform/FactoryFinder.java b/src/javax/xml/transform/FactoryFinder.java --- a/src/javax/xml/transform/FactoryFinder.java +++ b/src/javax/xml/transform/FactoryFinder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,13 +25,15 @@ package javax.xml.transform; -import java.io.BufferedReader; import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Iterator; import java.util.Properties; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; /** *

      Implements pluggable Datatypes.

      @@ -53,7 +55,7 @@ /** * Cache for properties in java.home/lib/jaxp.properties */ - static Properties cacheProps = new Properties(); + private final static Properties cacheProps = new Properties(); /** * Flag indicating if properties from java.home/lib/jaxp.properties @@ -65,7 +67,7 @@ * Security support class use to check access control before * getting certain system resources. */ - static SecuritySupport ss = new SecuritySupport(); + private final static SecuritySupport ss = new SecuritySupport(); // Define system property "jaxp.debug" to get output static { @@ -98,31 +100,31 @@ * * Use bootstrap classLoader if cl = null and useBSClsLoader is true */ - static private Class getProviderClass(String className, ClassLoader cl, + static private Class getProviderClass(String className, ClassLoader cl, boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException { try { if (cl == null) { if (useBSClsLoader) { - return Class.forName(className, true, FactoryFinder.class.getClassLoader()); + return Class.forName(className, false, FactoryFinder.class.getClassLoader()); } else { cl = ss.getContextClassLoader(); if (cl == null) { throw new ClassNotFoundException(); } else { - return cl.loadClass(className); + return Class.forName(className, false, cl); } } } else { - return cl.loadClass(className); + return Class.forName(className, false, cl); } } catch (ClassNotFoundException e1) { if (doFallback) { // Use current class loader - should always be bootstrap CL - return Class.forName(className, true, FactoryFinder.class.getClassLoader()); + return Class.forName(className, false, FactoryFinder.class.getClassLoader()); } else { throw e1; @@ -134,24 +136,8 @@ * Create an instance of a class. Delegates to method * getProviderClass() in order to load the class. * - * @param className Name of the concrete class corresponding to the - * service provider - * - * @param cl ClassLoader used to load the factory class. If null - * current Thread's context classLoader is used to load the factory class. - * - * @param doFallback True if the current ClassLoader should be tried as - * a fallback if the class is not found using cl - */ - static Object newInstance(String className, ClassLoader cl, boolean doFallback) - throws ConfigurationError - { - return newInstance(className, cl, doFallback, false, false); - } - - /** - * Create an instance of a class. Delegates to method - * getProviderClass() in order to load the class. + * @param type Base class / Service interface of the factory to + * instantiate. * * @param className Name of the concrete class corresponding to the * service provider @@ -162,14 +148,15 @@ * @param doFallback True if the current ClassLoader should be tried as * a fallback if the class is not found using cl * - * @param useBSClsLoader True if cl=null actually meant bootstrap classLoader. This parameter - * is needed since DocumentBuilderFactory/SAXParserFactory defined null as context classLoader. - * * @param useServicesMechanism True use services mechanism */ - static Object newInstance(String className, ClassLoader cl, boolean doFallback, boolean useBSClsLoader, boolean useServicesMechanism) - throws ConfigurationError + static T newInstance(Class type, String className, ClassLoader cl, + boolean doFallback, boolean useServicesMechanism) + throws TransformerFactoryConfigurationError { + assert type != null; + + boolean useBSClsLoader = false; // make sure we have access to restricted packages if (System.getSecurityManager() != null) { if (className != null && className.startsWith(DEFAULT_PACKAGE)) { @@ -179,10 +166,13 @@ } try { - Class providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader); + Class providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader); + if (!type.isAssignableFrom(providerClass)) { + throw new ClassCastException(className + " cannot be cast to " + type.getName()); + } Object instance = null; if (!useServicesMechanism) { - instance = newInstanceNoServiceLoader(providerClass); + instance = newInstanceNoServiceLoader(type, providerClass); } if (instance == null) { instance = providerClass.newInstance(); @@ -191,63 +181,87 @@ dPrint("created new instance of " + providerClass + " using ClassLoader: " + cl); } - return instance; + return type.cast(instance); } catch (ClassNotFoundException x) { - throw new ConfigurationError( - "Provider " + className + " not found", x); + throw new TransformerFactoryConfigurationError(x, + "Provider " + className + " not found"); } catch (Exception x) { - throw new ConfigurationError( - "Provider " + className + " could not be instantiated: " + x, - x); + throw new TransformerFactoryConfigurationError(x, + "Provider " + className + " could not be instantiated: " + x); } } + /** * Try to construct using newTransformerFactoryNoServiceLoader * method if available. */ - private static Object newInstanceNoServiceLoader( - Class providerClass - ) { + private static T newInstanceNoServiceLoader(Class type, Class providerClass) { // Retain maximum compatibility if no security manager. if (System.getSecurityManager() == null) { return null; } try { - Method creationMethod = - providerClass.getDeclaredMethod( - "newTransformerFactoryNoServiceLoader" - ); - return creationMethod.invoke(null, (Object[])null); - } catch (NoSuchMethodException exc) { + final Method creationMethod = + providerClass.getDeclaredMethod( + "newTransformerFactoryNoServiceLoader" + ); + final int modifiers = creationMethod.getModifiers(); + + // Do not call the method if it's not public static. + if (!Modifier.isPublic(modifiers) || !Modifier.isStatic(modifiers)) { return null; - } catch (Exception exc) { - return null; + } + + // Only call the method if it's declared to return an instance of + // TransformerFactory + final Class returnType = creationMethod.getReturnType(); + if (type.isAssignableFrom(returnType)) { + final Object result = creationMethod.invoke(null, (Object[])null); + return type.cast(result); + } else { + // This should not happen, as + // TransformerFactoryImpl.newTransformerFactoryNoServiceLoader is + // declared to return TransformerFactory. + throw new ClassCastException(returnType + " cannot be cast to " + type); + } + } catch (ClassCastException e) { + throw new TransformerFactoryConfigurationError(e, e.getMessage()); + } catch (NoSuchMethodException exc) { + return null; + } catch (Exception exc) { + return null; } } + /** * Finds the implementation Class object in the specified order. Main * entry point. * @return Class object of factory, never null * - * @param factoryId Name of the factory to find, same as - * a property name + * @param type Base class / Service interface of the + * factory to find. + * * @param fallbackClassName Implementation class name, if nothing else * is found. Use null to mean no fallback. * * Package private so this code can be shared. */ - static Object find(String factoryId, String fallbackClassName) - throws ConfigurationError + static T find(Class type, String fallbackClassName) + throws TransformerFactoryConfigurationError { + assert type != null; + + final String factoryId = type.getName(); + dPrint("find factoryId =" + factoryId); // Use the system property first try { String systemProp = ss.getSystemProperty(factoryId); if (systemProp != null) { dPrint("found system property, value=" + systemProp); - return newInstance(systemProp, null, true, false, true); + return newInstance(type, systemProp, null, true, true); } } catch (SecurityException se) { @@ -256,7 +270,6 @@ // try to read from $java.home/lib/jaxp.properties try { - String factoryClassName = null; if (firstTime) { synchronized (cacheProps) { if (firstTime) { @@ -271,11 +284,11 @@ } } } - factoryClassName = cacheProps.getProperty(factoryId); + final String factoryClassName = cacheProps.getProperty(factoryId); if (factoryClassName != null) { dPrint("found in $java.home/jaxp.properties, value=" + factoryClassName); - return newInstance(factoryClassName, null, true, false, true); + return newInstance(type, factoryClassName, null, true, true); } } catch (Exception ex) { @@ -283,113 +296,54 @@ } // Try Jar Service Provider Mechanism - Object provider = findJarServiceProvider(factoryId); + T provider = findServiceProvider(type); if (provider != null) { return provider; } if (fallbackClassName == null) { - throw new ConfigurationError( - "Provider for " + factoryId + " cannot be found", null); + throw new TransformerFactoryConfigurationError(null, + "Provider for " + factoryId + " cannot be found"); } dPrint("loaded from fallback value: " + fallbackClassName); - return newInstance(fallbackClassName, null, true, false, true); + return newInstance(type, fallbackClassName, null, true, true); } /* - * Try to find provider using Jar Service Provider Mechanism + * Try to find provider using the ServiceLoader. + * + * @param type Base class / Service interface of the factory to find. * * @return instance of provider class if found or null */ - private static Object findJarServiceProvider(String factoryId) - throws ConfigurationError + private static T findServiceProvider(final Class type) + throws TransformerFactoryConfigurationError { - String serviceId = "META-INF/services/" + factoryId; - InputStream is = null; - - // First try the Context ClassLoader - ClassLoader cl = ss.getContextClassLoader(); - boolean useBSClsLoader = false; - if (cl != null) { - is = ss.getResourceAsStream(cl, serviceId); - - // If no provider found then try the current ClassLoader - if (is == null) { - cl = FactoryFinder.class.getClassLoader(); - is = ss.getResourceAsStream(cl, serviceId); - useBSClsLoader = true; - } - } else { - // No Context ClassLoader, try the current ClassLoader - cl = FactoryFinder.class.getClassLoader(); - is = ss.getResourceAsStream(cl, serviceId); - useBSClsLoader = true; - } - - if (is == null) { - // No provider found - return null; - } - - if (debug) { // Extra check to avoid computing cl strings - dPrint("found jar resource=" + serviceId + " using ClassLoader: " + cl); - } - - BufferedReader rd; - try { - rd = new BufferedReader(new InputStreamReader(is, "UTF-8")); - } - catch (java.io.UnsupportedEncodingException e) { - rd = new BufferedReader(new InputStreamReader(is)); - } - - String factoryClassName = null; - try { - // XXX Does not handle all possible input as specified by the - // Jar Service Provider specification - factoryClassName = rd.readLine(); - rd.close(); - } catch (IOException x) { - // No provider found - return null; - } - - if (factoryClassName != null && !"".equals(factoryClassName)) { - dPrint("found in resource, value=" + factoryClassName); - - // Note: here we do not want to fall back to the current - // ClassLoader because we want to avoid the case where the - // resource file was found using one ClassLoader and the - // provider class was instantiated using a different one. - return newInstance(factoryClassName, cl, false, useBSClsLoader, true); - } - - // No provider found - return null; - } - - static class ConfigurationError extends Error { - private Exception exception; - - /** - * Construct a new instance with the specified detail string and - * exception. - */ - ConfigurationError(String msg, Exception x) { - super(msg); - this.exception = x; - } - - Exception getException() { - return exception; - } - /** - * use the exception chaining mechanism of JDK1.4 - */ - @Override - public Throwable getCause() { - return exception; + try { + return AccessController.doPrivileged(new PrivilegedAction() { + public T run() { + final ServiceLoader serviceLoader = ServiceLoader.load(type); + final Iterator iterator = serviceLoader.iterator(); + if (iterator.hasNext()) { + return iterator.next(); + } else { + return null; + } + } + }); + } catch(ServiceConfigurationError e) { + // It is not possible to wrap an error directly in + // FactoryConfigurationError - so we need to wrap the + // ServiceConfigurationError in a RuntimeException. + // The alternative would be to modify the logic in + // FactoryConfigurationError to allow setting a + // Throwable as the cause, but that could cause + // compatibility issues down the road. + final RuntimeException x = new RuntimeException( + "Provider for " + type + " cannot be created", e); + final TransformerFactoryConfigurationError error = + new TransformerFactoryConfigurationError(x, x.getMessage()); + throw error; } } - } diff --git a/src/javax/xml/transform/TransformerFactory.java b/src/javax/xml/transform/TransformerFactory.java --- a/src/javax/xml/transform/TransformerFactory.java +++ b/src/javax/xml/transform/TransformerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -52,8 +52,8 @@ /** *

      Obtain a new instance of a TransformerFactory. - * This static method creates a new factory instance - * This method uses the following ordered lookup procedure to determine + * This static method creates a new factory instance.

      + *

      This method uses the following ordered lookup procedure to determine * the TransformerFactory implementation class to * load:

      *
        @@ -67,7 +67,7 @@ * format and contains the fully qualified name of the * implementation class with the key being the system property defined * above. - * + *
        * The jaxp.properties file is read only once by the JAXP implementation * and it's values are then cached for future use. If the file does not exist * when the first attempt is made to read from it, no further attempts are @@ -75,14 +75,12 @@ * of any property in jaxp.properties after it has been read for the first time. * *
      • - * Use the Services API (as detailed in the JAR specification), if - * available, to determine the classname. The Services API will look - * for a classname in the file - * META-INF/services/javax.xml.transform.TransformerFactory - * in jars available to the runtime. + * Use the service-provider loading facilities, defined by the + * {@link java.util.ServiceLoader} class, to attempt to locate and load an + * implementation of the service. *
      • *
      • - * Platform default TransformerFactory instance. + * Otherwise, the system-default implementation is returned. *
      • *
      * @@ -92,22 +90,18 @@ * * @return new TransformerFactory instance, never null. * - * @throws TransformerFactoryConfigurationError Thrown if the implementation - * is not available or cannot be instantiated. + * @throws TransformerFactoryConfigurationError Thrown in case of {@linkplain + * java.util.ServiceConfigurationError service configuration error} or if + * the implementation is not available or cannot be instantiated. */ public static TransformerFactory newInstance() throws TransformerFactoryConfigurationError { - try { - return (TransformerFactory) FactoryFinder.find( + + return FactoryFinder.find( /* The default property name according to the JAXP spec */ - "javax.xml.transform.TransformerFactory", + TransformerFactory.class, /* The fallback implementation class name, XSLTC */ "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"); - } catch (FactoryFinder.ConfigurationError e) { - throw new TransformerFactoryConfigurationError( - e.getException(), - e.getMessage()); - } } /** @@ -147,14 +141,10 @@ */ public static TransformerFactory newInstance(String factoryClassName, ClassLoader classLoader) throws TransformerFactoryConfigurationError{ - try { - //do not fallback if given classloader can't find the class, throw exception - return (TransformerFactory) FactoryFinder.newInstance(factoryClassName, classLoader, false); - } catch (FactoryFinder.ConfigurationError e) { - throw new TransformerFactoryConfigurationError( - e.getException(), - e.getMessage()); - } + + //do not fallback if given classloader can't find the class, throw exception + return FactoryFinder.newInstance(TransformerFactory.class, + factoryClassName, classLoader, false, false); } /** *

      Process the Source into a Transformer diff --git a/src/javax/xml/validation/SchemaFactory.java b/src/javax/xml/validation/SchemaFactory.java --- a/src/javax/xml/validation/SchemaFactory.java +++ b/src/javax/xml/validation/SchemaFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,15 +27,14 @@ import java.io.File; import java.net.URL; - import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; - import org.w3c.dom.ls.LSResourceResolver; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXNotSupportedException; +import org.xml.sax.SAXParseException; /** * Factory that creates {@link Schema} objects. Entry-point to @@ -79,7 +78,7 @@ * and has a significant effect on the parsing process, it is impossible * to define the DTD validation as a process independent from parsing. * For this reason, this specification does not define the semantics for - * the XML DTD. This doesn't prohibit implentors from implementing it + * the XML DTD. This doesn't prohibit implementors from implementing it * in a way they see fit, but users are warned that any DTD * validation implemented on this interface necessarily deviate from * the XML DTD semantics as defined in the XML 1.0. @@ -147,14 +146,17 @@ * is looked for. If present, the value is processed just like above. * *

    • - *

      The class loader is asked for service provider provider-configuration files matching - * javax.xml.validation.SchemaFactory in the resource directory META-INF/services. - * See the JAR File Specification for file format and parsing rules. - * Each potential service provider is required to implement the method:

      - *
      -     *        {@link #isSchemaLanguageSupported(String schemaLanguage)}
      -     *     
      - * The first service provider found in class loader order that supports the specified schema language is returned. + * Use the service-provider loading facilities, defined by the + * {@link java.util.ServiceLoader} class, to attempt to locate and load an + * implementation of the service.
      + * Each potential service provider is required to implement the method + * {@link #isSchemaLanguageSupported(String schemaLanguage)}. + *
      + * The first service provider found that supports the specified schema + * language is returned. + *
      + * In case of {@link java.util.ServiceConfigurationError} a + * {@link SchemaFactoryConfigurationError} will be thrown. *
    • *
    • * Platform default SchemaFactory is located @@ -186,10 +188,12 @@ * If no implementation of the schema language is available. * @throws NullPointerException * If the schemaLanguage parameter is null. + * @throws SchemaFactoryConfigurationError + * If a configuration error is encountered. * * @see #newInstance(String schemaLanguage, String factoryClassName, ClassLoader classLoader) */ - public static final SchemaFactory newInstance(String schemaLanguage) { + public static SchemaFactory newInstance(String schemaLanguage) { ClassLoader cl; cl = ss.getContextClassLoader(); @@ -275,19 +279,19 @@ } - /** - *

      Is specified schema supported by this SchemaFactory?

      - * - * @param schemaLanguage Specifies the schema language which the returned SchemaFactory will understand. + /** + *

      Is specified schema supported by this SchemaFactory?

      + * + * @param schemaLanguage Specifies the schema language which the returned SchemaFactory will understand. * schemaLanguage must specify a valid schema language. - * - * @return true if SchemaFactory supports schemaLanguage, else false. - * - * @throws NullPointerException If schemaLanguage is null. - * @throws IllegalArgumentException If schemaLanguage.length() == 0 - * or schemaLanguage does not specify a valid schema language. - */ - public abstract boolean isSchemaLanguageSupported(String schemaLanguage); + * + * @return true if SchemaFactory supports schemaLanguage, else false. + * + * @throws NullPointerException If schemaLanguage is null. + * @throws IllegalArgumentException If schemaLanguage.length() == 0 + * or schemaLanguage does not specify a valid schema language. + */ + public abstract boolean isSchemaLanguageSupported(String schemaLanguage); /** * Look up the value of a feature flag. diff --git a/src/javax/xml/validation/SchemaFactoryConfigurationError.java b/src/javax/xml/validation/SchemaFactoryConfigurationError.java new file mode 100644 --- /dev/null +++ b/src/javax/xml/validation/SchemaFactoryConfigurationError.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package javax.xml.validation; + +/** + * Thrown when a problem with configuration with the Schema Factories + * exists. This error will typically be thrown when the class of a + * schema factory specified in the system properties cannot be found + * or instantiated. + * @since 1.8 + */ +public final class SchemaFactoryConfigurationError extends Error { + + static final long serialVersionUID = 3531438703147750126L; + + /** + * Create a new SchemaFactoryConfigurationError with no + * detail message. + */ + public SchemaFactoryConfigurationError() { + } + + + /** + * Create a new SchemaFactoryConfigurationError with + * the String specified as an error message. + * + * @param message The error message for the exception. + */ + public SchemaFactoryConfigurationError(String message) { + super(message); + } + + /** + * Create a new SchemaFactoryConfigurationError with the + * given Throwable base cause. + * + * @param cause The exception or error to be encapsulated in a + * SchemaFactoryConfigurationError. + */ + public SchemaFactoryConfigurationError(Throwable cause) { + super(cause); + } + + /** + * Create a new SchemaFactoryConfigurationError with the + * given Throwable base cause and detail message. + * + * @param cause The exception or error to be encapsulated in a + * SchemaFactoryConfigurationError. + * @param message The detail message. + */ + public SchemaFactoryConfigurationError(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/src/javax/xml/validation/SchemaFactoryFinder.java b/src/javax/xml/validation/SchemaFactoryFinder.java --- a/src/javax/xml/validation/SchemaFactoryFinder.java +++ b/src/javax/xml/validation/SchemaFactoryFinder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,19 +25,16 @@ package javax.xml.validation; -import java.io.BufferedReader; import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.lang.reflect.Method; -import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; import java.net.URL; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.NoSuchElementException; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Properties; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; /** * Implementation of {@link SchemaFactory#newInstance(String)}. @@ -53,17 +50,17 @@ /** *

      Take care of restrictions imposed by java security model

      */ - private static SecuritySupport ss = new SecuritySupport(); + private static final SecuritySupport ss = new SecuritySupport(); private static final String DEFAULT_PACKAGE = "com.sun.org.apache.xerces.internal"; /** *

      Cache properties for performance.

      */ - private static Properties cacheProps = new Properties(); + private static final Properties cacheProps = new Properties(); - /** - *

      First time requires initialization overhead.

      - */ - private static volatile boolean firstTime = true; + /** + *

      First time requires initialization overhead.

      + */ + private static volatile boolean firstTime = true; static { // Use try/catch block to support applets @@ -115,7 +112,7 @@ return; } } catch( Throwable unused ) { - ; // getContextClassLoader() undefined in JDK1.1 + // getContextClassLoader() undefined in JDK1.1 } if( classLoader==ClassLoader.getSystemClassLoader() ) { @@ -138,9 +135,13 @@ * * @throws NullPointerException * If the schemaLanguage parameter is null. + * @throws SchemaFactoryConfigurationError + * If a configuration error is encountered. */ public SchemaFactory newFactory(String schemaLanguage) { - if(schemaLanguage==null) throw new NullPointerException(); + if(schemaLanguage==null) { + throw new NullPointerException(); + } SchemaFactory f = _newFactory(schemaLanguage); if (f != null) { debugPrintln("factory '" + f.getClass().getName() + "' was found for " + schemaLanguage); @@ -183,7 +184,6 @@ String configFile = javah + File.separator + "lib" + File.separator + "jaxp.properties"; - String factoryClassName = null ; // try to read from $java.home/lib/jaxp.properties try { @@ -199,7 +199,7 @@ } } } - factoryClassName = cacheProps.getProperty(propertyName); + final String factoryClassName = cacheProps.getProperty(propertyName); debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties"); if (factoryClassName != null) { @@ -214,21 +214,15 @@ } } - // try META-INF/services files - Iterator sitr = createServiceFileIterator(); - while(sitr.hasNext()) { - URL resource = (URL)sitr.next(); - debugPrintln("looking into " + resource); - try { - sf = loadFromService(schemaLanguage,resource.toExternalForm(), - ss.getURLInputStream(resource)); - if(sf!=null) return sf; - } catch(IOException e) { - if( debug ) { - debugPrintln("failed to read "+resource); - e.printStackTrace(); - } - } + // Try with ServiceLoader + final SchemaFactory factoryImpl = findServiceProvider(schemaLanguage); + + // The following assertion should always be true. + // Uncomment it, recompile, and run with -ea in case of doubts: + // assert factoryImpl == null || factoryImpl.isSchemaLanguageSupported(schemaLanguage); + + if (factoryImpl != null) { + return factoryImpl; } // platform default @@ -246,8 +240,8 @@ * @param className Name of class to create. * @return Created class or null. */ - private Class createClass(String className) { - Class clazz; + private Class createClass(String className) { + Class clazz; // make sure we have access to restricted packages boolean internal = false; if (System.getSecurityManager() != null) { @@ -256,25 +250,27 @@ } } - try { - if (classLoader != null && !internal) { - clazz = classLoader.loadClass(className); - } else { - clazz = Class.forName(className); - } - } catch (Throwable t) { - if(debug) t.printStackTrace(); - return null; + try { + if (classLoader != null && !internal) { + clazz = Class.forName(className, false, classLoader); + } else { + clazz = Class.forName(className); } + } catch (Throwable t) { + if(debug) { + t.printStackTrace(); + } + return null; + } - return clazz; + return clazz; } /** *

      Creates an instance of the specified and returns it.

      * * @param className - * fully qualified class name to be instanciated. + * fully qualified class name to be instantiated. * * @return null * if it fails. Error messages will be printed by this method. @@ -289,7 +285,7 @@ debugPrintln("createInstance(" + className + ")"); // get Class from className - Class clazz = createClass(className); + Class clazz = createClass(className); if (clazz == null) { debugPrintln("failed to getClass(" + className + ")"); return null; @@ -298,9 +294,13 @@ // instantiate Class as a SchemaFactory try { - if (!useServicesMechanism) { - schemaFactory = (SchemaFactory) newInstanceNoServiceLoader(clazz); - } + if (!SchemaFactory.class.isAssignableFrom(clazz)) { + throw new ClassCastException(clazz.getName() + + " cannot be cast to " + SchemaFactory.class); + } + if (!useServicesMechanism) { + schemaFactory = newInstanceNoServiceLoader(clazz); + } if (schemaFactory == null) { schemaFactory = (SchemaFactory) clazz.newInstance(); } @@ -326,11 +326,12 @@ return schemaFactory; } + /** - * Try to construct using newTransformerFactoryNoServiceLoader + * Try to construct using newXMLSchemaFactoryNoServiceLoader * method if available. */ - private static Object newInstanceNoServiceLoader( + private static SchemaFactory newInstanceNoServiceLoader( Class providerClass ) { // Retain maximum compatibility if no security manager. @@ -338,196 +339,87 @@ return null; } try { - Method creationMethod = + final Method creationMethod = providerClass.getDeclaredMethod( "newXMLSchemaFactoryNoServiceLoader" ); - return creationMethod.invoke(null, (Object[])null); - } catch (NoSuchMethodException exc) { + final int modifiers = creationMethod.getModifiers(); + + // Do not call the method if it's not public static. + if (!Modifier.isStatic(modifiers) || !Modifier.isPublic(modifiers)) { return null; - } catch (Exception exc) { - return null; - } - } + } - /** Iterator that lazily computes one value and returns it. */ - private static abstract class SingleIterator implements Iterator { - private boolean seen = false; - - public final void remove() { throw new UnsupportedOperationException(); } - public final boolean hasNext() { return !seen; } - public final Object next() { - if(seen) throw new NoSuchElementException(); - seen = true; - return value(); - } - - protected abstract Object value(); - } - - /** - * Looks up a value in a property file - * while producing all sorts of debug messages. - * - * @return null - * if there was an error. - */ - private SchemaFactory loadFromProperty( String keyName, String resourceName, InputStream in ) - throws IOException { - debugPrintln("Reading "+resourceName ); - - Properties props=new Properties(); - props.load(in); - in.close(); - String factoryClassName = props.getProperty(keyName); - if(factoryClassName != null){ - debugPrintln("found "+keyName+" = " + factoryClassName); - return createInstance(factoryClassName); - } else { - debugPrintln(keyName+" is not in the property file"); + // Only calls "newXMLSchemaFactoryNoServiceLoader" if it's + // declared to return an instance of SchemaFactory. + final Class returnType = creationMethod.getReturnType(); + if (SERVICE_CLASS.isAssignableFrom(returnType)) { + return SERVICE_CLASS.cast(creationMethod.invoke(null, (Object[])null)); + } else { + // Should not happen since + // XMLSchemaFactory.newXMLSchemaFactoryNoServiceLoader is + // declared to return XMLSchemaFactory. + throw new ClassCastException(returnType + + " cannot be cast to " + SERVICE_CLASS); + } + } catch(ClassCastException e) { + throw new SchemaFactoryConfigurationError(e.getMessage(), e); + } catch (NoSuchMethodException exc) { + return null; + } catch (Exception exc) { return null; } } - /** - *

      Look up a value in a property file.

      - * - *

      Set debug to true to trace property evaluation.

      - * - * @param schemaLanguage Schema Language to support. - * @param inputName Name of InputStream. - * @param in InputStream of properties. - * - * @return SchemaFactory as determined by keyName value or null if there was an error. - * - * @throws IOException If IO error reading from in. - */ - private SchemaFactory loadFromService( - String schemaLanguage, - String inputName, - InputStream in) - throws IOException { - - SchemaFactory schemaFactory = null; - final Class[] stringClassArray = {"".getClass()}; - final Object[] schemaLanguageObjectArray = {schemaLanguage}; - final String isSchemaLanguageSupportedMethod = "isSchemaLanguageSupported"; - - debugPrintln("Reading " + inputName); - - // read from InputStream until a match is found - BufferedReader configFile = new BufferedReader(new InputStreamReader(in)); - String line = null; - while ((line = configFile.readLine()) != null) { - // '#' is comment char - int comment = line.indexOf("#"); - switch (comment) { - case -1: break; // no comment - case 0: line = ""; break; // entire line is a comment - default: line = line.substring(0, comment); break; // trim comment - } - - // trim whitespace - line = line.trim(); - - // any content left on line? - if (line.length() == 0) { - continue; - } - - // line content is now the name of the class - Class clazz = createClass(line); - if (clazz == null) { - continue; - } - - // create an instance of the Class - try { - schemaFactory = (SchemaFactory) clazz.newInstance(); - } catch (ClassCastException classCastExcpetion) { - schemaFactory = null; - continue; - } catch (InstantiationException instantiationException) { - schemaFactory = null; - continue; - } catch (IllegalAccessException illegalAccessException) { - schemaFactory = null; - continue; - } - - // does this Class support desired Schema? - try { - Method isSchemaLanguageSupported = clazz.getMethod(isSchemaLanguageSupportedMethod, stringClassArray); - Boolean supported = (Boolean) isSchemaLanguageSupported.invoke(schemaFactory, schemaLanguageObjectArray); - if (supported.booleanValue()) { - break; - } - } catch (NoSuchMethodException noSuchMethodException) { - - } catch (IllegalAccessException illegalAccessException) { - - } catch (InvocationTargetException invocationTargetException) { - - } - schemaFactory = null; + // Call isSchemaLanguageSupported with initial context. + private boolean isSchemaLanguageSupportedBy(final SchemaFactory factory, + final String schemaLanguage, + AccessControlContext acc) { + return AccessController.doPrivileged(new PrivilegedAction() { + public Boolean run() { + return factory.isSchemaLanguageSupported(schemaLanguage); } - - // clean up - configFile.close(); - - // return new instance of SchemaFactory or null - return schemaFactory; + }, acc); } /** - * Returns an {@link Iterator} that enumerates all - * the META-INF/services files that we care. + * Finds a service provider subclass of SchemaFactory that supports the + * given schema language using the ServiceLoader. + * + * @param schemaLanguage The schema language for which we seek a factory. + * @return A SchemaFactory supporting the specified schema language, or null + * if none is found. + * @throws SchemaFactoryConfigurationError if a configuration error is found. */ - private Iterator createServiceFileIterator() { - if (classLoader == null) { - return new SingleIterator() { - protected Object value() { - ClassLoader classLoader = SchemaFactoryFinder.class.getClassLoader(); - //return (ClassLoader.getSystemResource( SERVICE_ID )); - return ss.getResourceAsURL(classLoader, SERVICE_ID); + private SchemaFactory findServiceProvider(final String schemaLanguage) { + assert schemaLanguage != null; + // store current context. + final AccessControlContext acc = AccessController.getContext(); + try { + return AccessController.doPrivileged(new PrivilegedAction() { + public SchemaFactory run() { + final ServiceLoader loader = + ServiceLoader.load(SERVICE_CLASS); + for (SchemaFactory factory : loader) { + // restore initial context to call + // factory.isSchemaLanguageSupported + if (isSchemaLanguageSupportedBy(factory, schemaLanguage, acc)) { + return factory; + } + } + return null; // no factory found. } - }; - } else { - try { - //final Enumeration e = classLoader.getResources(SERVICE_ID); - final Enumeration e = ss.getResources(classLoader, SERVICE_ID); - if(!e.hasMoreElements()) { - debugPrintln("no "+SERVICE_ID+" file was found"); - } - - // wrap it into an Iterator. - return new Iterator() { - public void remove() { - throw new UnsupportedOperationException(); - } - - public boolean hasNext() { - return e.hasMoreElements(); - } - - public Object next() { - return e.nextElement(); - } - }; - } catch (IOException e) { - debugPrintln("failed to enumerate resources "+SERVICE_ID); - if(debug) e.printStackTrace(); - return new ArrayList().iterator(); // empty iterator - } + }); + } catch (ServiceConfigurationError error) { + throw new SchemaFactoryConfigurationError( + "Provider for " + SERVICE_CLASS + " cannot be created", error); } } - private static final Class SERVICE_CLASS = SchemaFactory.class; - private static final String SERVICE_ID = "META-INF/services/" + SERVICE_CLASS.getName(); + private static final Class SERVICE_CLASS = SchemaFactory.class; - - private static String which( Class clazz ) { + private static String which( Class clazz ) { return which( clazz.getName(), clazz.getClassLoader() ); } diff --git a/src/javax/xml/xpath/XPathFactory.java b/src/javax/xml/xpath/XPathFactory.java --- a/src/javax/xml/xpath/XPathFactory.java +++ b/src/javax/xml/xpath/XPathFactory.java @@ -90,7 +90,7 @@ * @throws RuntimeException When there is a failure in creating an * XPathFactory for the default object model. */ - public static final XPathFactory newInstance() { + public static XPathFactory newInstance() { try { return newInstance(DEFAULT_OBJECT_MODEL_URI); @@ -121,14 +121,17 @@ * If present, the value is processed just like above. *
    • *
    • - * The class loader is asked for service provider provider-configuration files matching javax.xml.xpath.XPathFactory - * in the resource directory META-INF/services. - * See the JAR File Specification for file format and parsing rules. - * Each potential service provider is required to implement the method: - *
      -    *       {@link #isObjectModelSupported(String objectModel)}
      -    *     
      - * The first service provider found in class loader order that supports the specified object model is returned. + * Use the service-provider loading facilities, defined by the + * {@link java.util.ServiceLoader} class, to attempt to locate and load an + * implementation of the service. + *
      + * Each potential service provider is required to implement the method + * {@link #isObjectModelSupported(String objectModel)}. + * The first service provider found that supports the specified object + * model is returned. + *
      + * In case of {@link java.util.ServiceConfigurationError} an + * {@link XPathFactoryConfigurationException} will be thrown. *
    • *
    • * Platform default XPathFactory is located in a platform specific way. @@ -152,43 +155,41 @@ * * @return Instance of an XPathFactory. * - * @throws XPathFactoryConfigurationException If the specified object model is unavailable. + * @throws XPathFactoryConfigurationException If the specified object model + * is unavailable, or if there is a configuration error. * @throws NullPointerException If uri is null. - * @throws IllegalArgumentException If uri is null + * @throws IllegalArgumentException If uri is null * or uri.length() == 0. */ - public static final XPathFactory newInstance(final String uri) + public static XPathFactory newInstance(final String uri) throws XPathFactoryConfigurationException { if (uri == null) { - throw new NullPointerException( - "XPathFactory#newInstance(String uri) cannot be called with uri == null" - ); + throw new NullPointerException( + "XPathFactory#newInstance(String uri) cannot be called with uri == null"); } - if (uri.length() == 0) { - throw new IllegalArgumentException( - "XPathFactory#newInstance(String uri) cannot be called with uri == \"\"" - ); - } + if (uri.length() == 0) { + throw new IllegalArgumentException( + "XPathFactory#newInstance(String uri) cannot be called with uri == \"\""); + } - ClassLoader classLoader = ss.getContextClassLoader(); + ClassLoader classLoader = ss.getContextClassLoader(); if (classLoader == null) { //use the current class loader classLoader = XPathFactory.class.getClassLoader(); } - XPathFactory xpathFactory = new XPathFactoryFinder(classLoader).newFactory(uri); + XPathFactory xpathFactory = new XPathFactoryFinder(classLoader).newFactory(uri); - if (xpathFactory == null) { - throw new XPathFactoryConfigurationException( - "No XPathFactory implementation found for the object model: " - + uri - ); - } + if (xpathFactory == null) { + throw new XPathFactoryConfigurationException( + "No XPathFactory implementation found for the object model: " + + uri); + } - return xpathFactory; + return xpathFactory; } /** @@ -242,16 +243,14 @@ ClassLoader cl = classLoader; if (uri == null) { - throw new NullPointerException( - "XPathFactory#newInstance(String uri) cannot be called with uri == null" - ); + throw new NullPointerException( + "XPathFactory#newInstance(String uri) cannot be called with uri == null"); } - if (uri.length() == 0) { - throw new IllegalArgumentException( - "XPathFactory#newInstance(String uri) cannot be called with uri == \"\"" - ); - } + if (uri.length() == 0) { + throw new IllegalArgumentException( + "XPathFactory#newInstance(String uri) cannot be called with uri == \"\""); + } if (cl == null) { cl = ss.getContextClassLoader(); @@ -260,31 +259,32 @@ XPathFactory f = new XPathFactoryFinder(cl).createInstance(factoryClassName); if (f == null) { - throw new XPathFactoryConfigurationException( - "No XPathFactory implementation found for the object model: " - + uri - ); + throw new XPathFactoryConfigurationException( + "No XPathFactory implementation found for the object model: " + + uri); } //if this factory supports the given schemalanguage return this factory else thrown exception - if(f.isObjectModelSupported(uri)){ + if (f.isObjectModelSupported(uri)) { return f; - }else{ - throw new XPathFactoryConfigurationException("Factory " + factoryClassName + " doesn't support given " + uri + " object model"); + } else { + throw new XPathFactoryConfigurationException("Factory " + + factoryClassName + " doesn't support given " + uri + + " object model"); } } - /** - *

      Is specified object model supported by this XPathFactory?

      - * - * @param objectModel Specifies the object model which the returned XPathFactory will understand. - * - * @return true if XPathFactory supports objectModel, else false. - * - * @throws NullPointerException If objectModel is null. - * @throws IllegalArgumentException If objectModel.length() == 0. - */ - public abstract boolean isObjectModelSupported(String objectModel); + /** + *

      Is specified object model supported by this XPathFactory?

      + * + * @param objectModel Specifies the object model which the returned XPathFactory will understand. + * + * @return true if XPathFactory supports objectModel, else false. + * + * @throws NullPointerException If objectModel is null. + * @throws IllegalArgumentException If objectModel.length() == 0. + */ + public abstract boolean isObjectModelSupported(String objectModel); /** *

      Set a feature for this XPathFactory and @@ -314,8 +314,8 @@ * it creates cannot support this feature. * @throws NullPointerException if name is null. */ - public abstract void setFeature(String name, boolean value) - throws XPathFactoryConfigurationException; + public abstract void setFeature(String name, boolean value) + throws XPathFactoryConfigurationException; /** *

      Get the state of the named feature.

      @@ -339,8 +339,8 @@ * it creates cannot support this feature. * @throws NullPointerException if name is null. */ - public abstract boolean getFeature(String name) - throws XPathFactoryConfigurationException; + public abstract boolean getFeature(String name) + throws XPathFactoryConfigurationException; /** *

      Establish a default variable resolver.

      @@ -359,19 +359,19 @@ public abstract void setXPathVariableResolver(XPathVariableResolver resolver); /** - *

      Establish a default function resolver.

      - * - *

      Any XPath objects constructed from this factory will - * use the specified resolver by default.

      - * - *

      A NullPointerException is thrown if - * resolver is null.

      - * - * @param resolver XPath function resolver. - * - * @throws NullPointerException If resolver is - * null. - */ + *

      Establish a default function resolver.

      + * + *

      Any XPath objects constructed from this factory will + * use the specified resolver by default.

      + * + *

      A NullPointerException is thrown if + * resolver is null.

      + * + * @param resolver XPath function resolver. + * + * @throws NullPointerException If resolver is + * null. + */ public abstract void setXPathFunctionResolver(XPathFunctionResolver resolver); /** diff --git a/src/javax/xml/xpath/XPathFactoryFinder.java b/src/javax/xml/xpath/XPathFactoryFinder.java --- a/src/javax/xml/xpath/XPathFactoryFinder.java +++ b/src/javax/xml/xpath/XPathFactoryFinder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,20 +25,16 @@ package javax.xml.xpath; -import java.io.BufferedReader; import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.lang.reflect.Method; -import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; import java.net.URL; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.NoSuchElementException; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Properties; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; /** * Implementation of {@link XPathFactory#newInstance(String)}. @@ -50,7 +46,7 @@ class XPathFactoryFinder { private static final String DEFAULT_PACKAGE = "com.sun.org.apache.xpath.internal"; - private static SecuritySupport ss = new SecuritySupport() ; + private static final SecuritySupport ss = new SecuritySupport() ; /** debug support code. */ private static boolean debug = false; static { @@ -65,12 +61,12 @@ /** *

      Cache properties for performance.

      */ - private static Properties cacheProps = new Properties(); + private static final Properties cacheProps = new Properties(); - /** - *

      First time requires initialization overhead.

      - */ - private volatile static boolean firstTime = true; + /** + *

      First time requires initialization overhead.

      + */ + private volatile static boolean firstTime = true; /** *

      Conditional debug printing.

      @@ -93,9 +89,8 @@ * to find XPathFactory.

      * * @param loader - * to be used to load resource, {@link XPathFactory}, and - * {@link SchemaFactoryLoader} implementations during - * the resolution process. + * to be used to load resource and {@link XPathFactory} + * implementations during the resolution process. * If this parameter is null, the default system class loader * will be used. */ @@ -113,7 +108,7 @@ return; } } catch( Throwable unused ) { - ; // getContextClassLoader() undefined in JDK1.1 + // getContextClassLoader() undefined in JDK1.1 } if( classLoader==ClassLoader.getSystemClassLoader() ) { @@ -126,7 +121,7 @@ /** *

      Creates a new {@link XPathFactory} object for the specified - * schema language.

      + * object model.

      * * @param uri * Identifies the underlying object model. @@ -136,8 +131,10 @@ * @throws NullPointerException * If the parameter is null. */ - public XPathFactory newFactory(String uri) { - if(uri==null) throw new NullPointerException(); + public XPathFactory newFactory(String uri) throws XPathFactoryConfigurationException { + if (uri == null) { + throw new NullPointerException(); + } XPathFactory f = _newFactory(uri); if (f != null) { debugPrintln("factory '" + f.getClass().getName() + "' was found for " + uri); @@ -154,8 +151,8 @@ * * @return {@link XPathFactory} for the given object model. */ - private XPathFactory _newFactory(String uri) { - XPathFactory xpathFactory; + private XPathFactory _newFactory(String uri) throws XPathFactoryConfigurationException { + XPathFactory xpathFactory = null; String propertyName = SERVICE_CLASS.getName() + ":" + uri; @@ -166,7 +163,9 @@ if(r!=null) { debugPrintln("The value is '"+r+"'"); xpathFactory = createInstance(r, true); - if(xpathFactory != null) return xpathFactory; + if (xpathFactory != null) { + return xpathFactory; + } } else debugPrintln("The property is undefined."); } catch( Throwable t ) { @@ -180,8 +179,6 @@ String configFile = javah + File.separator + "lib" + File.separator + "jaxp.properties"; - String factoryClassName = null ; - // try to read from $java.home/lib/jaxp.properties try { if(firstTime){ @@ -196,7 +193,7 @@ } } } - factoryClassName = cacheProps.getProperty(propertyName); + final String factoryClassName = cacheProps.getProperty(propertyName); debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties"); if (factoryClassName != null) { @@ -211,23 +208,16 @@ } } - // try META-INF/services files - Iterator sitr = createServiceFileIterator(); - while(sitr.hasNext()) { - URL resource = (URL)sitr.next(); - debugPrintln("looking into " + resource); - try { - xpathFactory = loadFromService(uri, resource.toExternalForm(), - ss.getURLInputStream(resource)); - if (xpathFactory != null) { - return xpathFactory; - } - } catch(IOException e) { - if( debug ) { - debugPrintln("failed to read "+resource); - e.printStackTrace(); - } - } + // Try with ServiceLoader + assert xpathFactory == null; + xpathFactory = findServiceProvider(uri); + + // The following assertion should always be true. + // Uncomment it, recompile, and run with -ea in case of doubts: + // assert xpathFactory == null || xpathFactory.isObjectModelSupported(uri); + + if (xpathFactory != null) { + return xpathFactory; } // platform default @@ -245,8 +235,8 @@ * @param className Name of class to create. * @return Created class or null. */ - private Class createClass(String className) { - Class clazz; + private Class createClass(String className) { + Class clazz; // make sure we have access to restricted packages boolean internal = false; if (System.getSecurityManager() != null) { @@ -258,47 +248,54 @@ // use approprite ClassLoader try { if (classLoader != null && !internal) { - clazz = classLoader.loadClass(className); + clazz = Class.forName(className, false, classLoader); } else { clazz = Class.forName(className); } } catch (Throwable t) { - if(debug) t.printStackTrace(); - return null; + if(debug) { + t.printStackTrace(); + } + return null; } - return clazz; + return clazz; } /** *

      Creates an instance of the specified and returns it.

      * * @param className - * fully qualified class name to be instanciated. + * fully qualified class name to be instantiated. * * @return null * if it fails. Error messages will be printed by this method. */ - XPathFactory createInstance( String className ) { + XPathFactory createInstance( String className ) + throws XPathFactoryConfigurationException + { return createInstance( className, false ); } - XPathFactory createInstance( String className, boolean useServicesMechanism ) { + + XPathFactory createInstance( String className, boolean useServicesMechanism ) + throws XPathFactoryConfigurationException + { XPathFactory xPathFactory = null; debugPrintln("createInstance(" + className + ")"); // get Class from className - Class clazz = createClass(className); + Class clazz = createClass(className); if (clazz == null) { - debugPrintln("failed to getClass(" + className + ")"); - return null; + debugPrintln("failed to getClass(" + className + ")"); + return null; } debugPrintln("loaded " + className + " from " + which(clazz)); // instantiate Class as a XPathFactory try { if (!useServicesMechanism) { - xPathFactory = (XPathFactory) newInstanceNoServiceLoader(clazz); + xPathFactory = newInstanceNoServiceLoader(clazz); } if (xPathFactory == null) { xPathFactory = (XPathFactory) clazz.newInstance(); @@ -329,203 +326,94 @@ * Try to construct using newXPathFactoryNoServiceLoader * method if available. */ - private static Object newInstanceNoServiceLoader( + private static XPathFactory newInstanceNoServiceLoader( Class providerClass - ) { + ) throws XPathFactoryConfigurationException { // Retain maximum compatibility if no security manager. if (System.getSecurityManager() == null) { return null; } try { Method creationMethod = - providerClass.getDeclaredMethod( - "newXPathFactoryNoServiceLoader" - ); - return creationMethod.invoke(null, (Object[])null); - } catch (NoSuchMethodException exc) { + providerClass.getDeclaredMethod( + "newXPathFactoryNoServiceLoader" + ); + final int modifiers = creationMethod.getModifiers(); + + // Do not call "newXPathFactoryNoServiceLoader" if it's + // not public static. + if (!Modifier.isStatic(modifiers) || !Modifier.isPublic(modifiers)) { return null; - } catch (Exception exc) { - return null; - } - } - - /** - *

      Look up a value in a property file.

      - * - *

      Set debug to true to trace property evaluation.

      - * - * @param objectModel URI of object model to support. - * @param inputName Name of InputStream. - * @param in InputStream of properties. - * - * @return XPathFactory as determined by keyName value or null if there was an error. - * - * @throws IOException If IO error reading from in. - */ - private XPathFactory loadFromService( - String objectModel, - String inputName, - InputStream in) - throws IOException { - - XPathFactory xPathFactory = null; - final Class[] stringClassArray = {"".getClass()}; - final Object[] objectModelObjectArray = {objectModel}; - final String isObjectModelSupportedMethod = "isObjectModelSupported"; - - debugPrintln("Reading " + inputName); - - // read from InputStream until a match is found - BufferedReader configFile = new BufferedReader(new InputStreamReader(in)); - String line = null; - while ((line = configFile.readLine()) != null) { - // '#' is comment char - int comment = line.indexOf("#"); - switch (comment) { - case -1: break; // no comment - case 0: line = ""; break; // entire line is a comment - default: line = line.substring(0, comment); break; // trim comment - } - - // trim whitespace - line = line.trim(); - - // any content left on line? - if (line.length() == 0) { - continue; - } - - // line content is now the name of the class - Class clazz = createClass(line); - if (clazz == null) { - continue; - } - - // create an instance of the Class - try { - xPathFactory = (XPathFactory) clazz.newInstance(); - } catch (ClassCastException classCastExcpetion) { - xPathFactory = null; - continue; - } catch (InstantiationException instantiationException) { - xPathFactory = null; - continue; - } catch (IllegalAccessException illegalAccessException) { - xPathFactory = null; - continue; - } - - // does this Class support desired object model? - try { - Method isObjectModelSupported = clazz.getMethod(isObjectModelSupportedMethod, stringClassArray); - Boolean supported = (Boolean) isObjectModelSupported.invoke(xPathFactory, objectModelObjectArray); - if (supported.booleanValue()) { - break; - } - - } catch (NoSuchMethodException noSuchMethodException) { - - } catch (IllegalAccessException illegalAccessException) { - - } catch (InvocationTargetException invocationTargetException) { - - } - xPathFactory = null; } - // clean up - configFile.close(); - - // return new instance of XPathFactory or null - return xPathFactory; - } - - /** Iterator that lazily computes one value and returns it. */ - private static abstract class SingleIterator implements Iterator { - private boolean seen = false; - - public final void remove() { throw new UnsupportedOperationException(); } - public final boolean hasNext() { return !seen; } - public final Object next() { - if(seen) throw new NoSuchElementException(); - seen = true; - return value(); - } - - protected abstract Object value(); - } - - /** - * Looks up a value in a property file - * while producing all sorts of debug messages. - * - * @return null - * if there was an error. - */ - private XPathFactory loadFromProperty( String keyName, String resourceName, InputStream in ) - throws IOException { - debugPrintln("Reading "+resourceName ); - - Properties props = new Properties(); - props.load(in); - in.close(); - String factoryClassName = props.getProperty(keyName); - if(factoryClassName != null){ - debugPrintln("found "+keyName+" = " + factoryClassName); - return createInstance(factoryClassName, true); - } else { - debugPrintln(keyName+" is not in the property file"); + // Only calls "newXPathFactoryNoServiceLoader" if it's + // declared to return an instance of XPathFactory. + final Class returnType = creationMethod.getReturnType(); + if (SERVICE_CLASS.isAssignableFrom(returnType)) { + return SERVICE_CLASS.cast(creationMethod.invoke(null, (Object[])null)); + } else { + // Should not happen since + // XPathFactoryImpl.newXPathFactoryNoServiceLoader is + // declared to return XPathFactory. + throw new ClassCastException(returnType + + " cannot be cast to " + SERVICE_CLASS); + } + } catch (ClassCastException e) { + throw new XPathFactoryConfigurationException(e); + } catch (NoSuchMethodException exc) { + return null; + } catch (Exception exc) { return null; } } + // Call isObjectModelSupportedBy with initial context. + private boolean isObjectModelSupportedBy(final XPathFactory factory, + final String objectModel, + AccessControlContext acc) { + return AccessController.doPrivileged(new PrivilegedAction() { + public Boolean run() { + return factory.isObjectModelSupported(objectModel); + } + }, acc); + } + /** - * Returns an {@link Iterator} that enumerates all - * the META-INF/services files that we care. + * Finds a service provider subclass of XPathFactory that supports the + * given object model using the ServiceLoader. + * + * @param objectModel URI of object model to support. + * @return An XPathFactory supporting the specified object model, or null + * if none is found. + * @throws XPathFactoryConfigurationException if a configuration error is found. */ - private Iterator createServiceFileIterator() { - if (classLoader == null) { - return new SingleIterator() { - protected Object value() { - ClassLoader classLoader = XPathFactoryFinder.class.getClassLoader(); - return ss.getResourceAsURL(classLoader, SERVICE_ID); - //return (ClassLoader.getSystemResource( SERVICE_ID )); + private XPathFactory findServiceProvider(final String objectModel) + throws XPathFactoryConfigurationException { + + assert objectModel != null; + // store current context. + final AccessControlContext acc = AccessController.getContext(); + try { + return AccessController.doPrivileged(new PrivilegedAction() { + public XPathFactory run() { + final ServiceLoader loader = + ServiceLoader.load(SERVICE_CLASS); + for (XPathFactory factory : loader) { + // restore initial context to call + // factory.isObjectModelSupportedBy + if (isObjectModelSupportedBy(factory, objectModel, acc)) { + return factory; + } + } + return null; // no factory found. } - }; - } else { - try { - //final Enumeration e = classLoader.getResources(SERVICE_ID); - final Enumeration e = ss.getResources(classLoader, SERVICE_ID); - if(!e.hasMoreElements()) { - debugPrintln("no "+SERVICE_ID+" file was found"); - } - - // wrap it into an Iterator. - return new Iterator() { - public void remove() { - throw new UnsupportedOperationException(); - } - - public boolean hasNext() { - return e.hasMoreElements(); - } - - public Object next() { - return e.nextElement(); - } - }; - } catch (IOException e) { - debugPrintln("failed to enumerate resources "+SERVICE_ID); - if(debug) e.printStackTrace(); - return new ArrayList().iterator(); // empty iterator - } + }); + } catch (ServiceConfigurationError error) { + throw new XPathFactoryConfigurationException(error); } } - private static final Class SERVICE_CLASS = XPathFactory.class; - private static final String SERVICE_ID = "META-INF/services/" + SERVICE_CLASS.getName(); - - + private static final Class SERVICE_CLASS = XPathFactory.class; private static String which( Class clazz ) { return which( clazz.getName(), clazz.getClassLoader() );