Index: test/schema/Positive0-jdoconfig.xml =================================================================== --- test/schema/Positive0-jdoconfig.xml (revision 0) +++ test/schema/Positive0-jdoconfig.xml (revision 0) @@ -0,0 +1,245 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: test/schema/negative0-jdoconfig.xml =================================================================== --- test/schema/negative0-jdoconfig.xml (revision 0) +++ test/schema/negative0-jdoconfig.xml (revision 0) @@ -0,0 +1,7 @@ + + + + + Index: test/java/javax/jdo/JDOHelperConfigTest.java =================================================================== --- test/java/javax/jdo/JDOHelperConfigTest.java (revision 0) +++ test/java/javax/jdo/JDOHelperConfigTest.java (revision 0) @@ -0,0 +1,239 @@ +package javax.jdo; + +import junit.framework.TestSuite; + +import javax.jdo.util.AbstractTest; +import javax.jdo.util.BatchTestRunner; +import java.util.HashMap; +import java.util.Map; +import java.util.Iterator; + +/** + * Tests class javax.jdo.JDOHelper for META-INF/jdoconfig.xml compliance. + */ +public class JDOHelperConfigTest extends AbstractTest implements Constants { + + public static void main(String args[]) { + BatchTestRunner.run(JDOHelperConfigTest.class); + } + + public static TestSuite suite() { + return new TestSuite(JDOHelperConfigTest.class); + } + + /** + * Tests JDOHelper.getNamedPersistenceManagerFactoryProperties using file + * Positive0-jdoconfig.xml and PU name + * "persistence-unit-name.positive0.pmf0" + */ + public void testGetNamedPMFProperties_positive0_pmf0() { + String jdoconfigResourceName = "Positive0-jdoconfig.xml"; + ClassLoader loader = getClass().getClassLoader(); + + Map expected = prepareInitialExpectedMap("positive0.pmf0", 2); + String name = (String) expected.get(PROPERTY_PERSISTENCE_UNIT_NAME); + + Map actual = JDOHelper.getNamedPersistenceManagerFactoryProperties( + name, + loader, + loader, + jdoconfigResourceName + ); + + assertNotNull("No properties found", actual); + assertEqualProperties(expected, actual); + } + + /** + * Tests JDOHelper.getNamedPersistenceManagerFactoryProperties using file + * Positive0-jdoconfig.xml and PU name + * "persistence-unit-name.positive0.pmf1" + */ + public void testGetNamedPMFProperties_positive0_pmf1() { + String jdoconfigResourceName = "Positive0-jdoconfig.xml"; + ClassLoader loader = getClass().getClassLoader(); + + Map expected = prepareInitialExpectedMap("positive0.pmf1", 2); + String name = (String) expected.get(PROPERTY_PERSISTENCE_UNIT_NAME); + + Map actual = JDOHelper.getNamedPersistenceManagerFactoryProperties( + name, + loader, + loader, + jdoconfigResourceName + ); + + assertNotNull("No properties found", actual); + assertEqualProperties(expected, actual); + } + + /** + * Tests JDOHelper.getNamedPersistenceManagerFactoryProperties using file + * Positive0-jdoconfig.xml and PU name + * "persistence-unit-name.positive0.pmf2" + */ + public void testGetNamedPMFProperties_positive0_pmf2() { + String jdoconfigResourceName = "Positive0-jdoconfig.xml"; + ClassLoader loader = getClass().getClassLoader(); + + Map expected = prepareInitialExpectedMap("positive0.pmf2", 2); + String name = (String) expected.get(PROPERTY_PERSISTENCE_UNIT_NAME); + + Map actual = JDOHelper.getNamedPersistenceManagerFactoryProperties( + name, + loader, + loader, + jdoconfigResourceName + ); + + assertNotNull("No properties found", actual); + assertEqualProperties(expected, actual); + } + + /** + * Tests JDOHelper.getNamedPersistenceManagerFactoryProperties using file + * Positive0-jdoconfig.xml and PU name + * "persistence-unit-name.positive0.pmf3" + */ + public void testGetNamedPMFProperties_positive0_pmf3() { + String jdoconfigResourceName = "Positive0-jdoconfig.xml"; + ClassLoader loader = getClass().getClassLoader(); + + Map expected = prepareInitialExpectedMap("positive0.pmf3", 2, 2); + String name = (String) expected.get(PROPERTY_PERSISTENCE_UNIT_NAME); + + Map actual = JDOHelper.getNamedPersistenceManagerFactoryProperties( + name, + loader, + loader, + jdoconfigResourceName + ); + + assertNotNull("No properties found", actual); + assertEqualProperties(expected, actual); + } + + /** + * Tests JDOHelper.getNamedPersistenceManagerFactoryProperties using file + * Positive0-jdoconfig.xml and PU name + * "persistence-unit-name.positive0.pmf4" + */ + public void testGetNamedPMFProperties_positive0_pmf4() { + String jdoconfigResourceName = "Positive0-jdoconfig.xml"; + ClassLoader loader = getClass().getClassLoader(); + + Map expected = prepareInitialExpectedMap("positive0.pmf4", 0, 2); + String name = (String) expected.get(PROPERTY_PERSISTENCE_UNIT_NAME); + + Map actual = JDOHelper.getNamedPersistenceManagerFactoryProperties( + name, + loader, + loader, + jdoconfigResourceName + ); + + assertNotNull("No properties found", actual); + assertEqualProperties(expected, actual); + } + + public Map prepareInitialExpectedMap(String testVariant) { + return prepareInitialExpectedMap(testVariant, 0, 0); + } + public Map prepareInitialExpectedMap(String testVariant, int numListeners) { + return prepareInitialExpectedMap(testVariant, numListeners, 0); + } + public Map prepareInitialExpectedMap( + String testVariant, + int numListeners, + int numProperties + ) { + Map expected = new HashMap(); + + expected.put(PROPERTY_PERSISTENCE_MANAGER_FACTORY_CLASS, + "class." + testVariant); + expected.put( + PROPERTY_CONNECTION_DRIVER_NAME, + "connection-driver-name." + testVariant); + expected.put( + PROPERTY_CONNECTION_FACTORY_NAME, + "connection-factory-name." + testVariant); + expected.put( + PROPERTY_CONNECTION_FACTORY2_NAME, + "connection-factory2-name." + testVariant); + expected.put( + PROPERTY_CONNECTION_PASSWORD, + "connection-password." + testVariant); + expected.put( + PROPERTY_CONNECTION_URL, + "connection-url." + testVariant); + expected.put( + PROPERTY_CONNECTION_USER_NAME, + "connection-user-name." + testVariant); + expected.put( + PROPERTY_IGNORE_CACHE, + "ignore-cache." + testVariant); + expected.put( + PROPERTY_MAPPING, + "mapping." + testVariant); + expected.put( + PROPERTY_MULTITHREADED, + "multithreaded." + testVariant); + expected.put( + PROPERTY_NONTRANSACTIONAL_READ, + "nontransactional-read." + testVariant); + expected.put( + PROPERTY_NONTRANSACTIONAL_WRITE, + "nontransactional-write." + testVariant); + expected.put( + PROPERTY_OPTIMISTIC, + "optimistic." + testVariant); + expected.put( + PROPERTY_PERSISTENCE_UNIT_NAME, + "persistence-unit-name." + testVariant); + expected.put( + PROPERTY_RESTORE_VALUES, + "restore-values." + testVariant); + expected.put( + PROPERTY_RETAIN_VALUES, + "retain-values." + testVariant); + expected.put( + PROPERTY_DETACH_ALL_ON_COMMIT, + "detach-all-on-commit." + testVariant); + + // listeners + for (int i = 0; i < numListeners; i++) { + expected.put( + PROPERTY_PREFIX_INSTANCE_LIFECYCLE_LISTENER + + "listener." + testVariant + ".listener" + i, + "classes." + testVariant + ".classes" + i + ); + } + + // properties + for (int i = 0; i < numProperties; i++) { + expected.put( + "property." + testVariant + ".name" + i, + "property." + testVariant + ".value" + i + ); + } + + return expected; + } + + static void assertEqualProperties(Map expected, Map actual) { + Iterator i = expected.entrySet().iterator(); + while (i.hasNext()) { + Map.Entry entry = (Map.Entry) i.next(); + String key = (String) entry.getKey(); + String expectedValue = (String) entry.getValue(); + String actualValue = (String) actual.get(key); + + assertEquals( + "Actual property at [" + key + "] with value [" + + actualValue + "] not equal to expected value [" + + expectedValue + "]", + expectedValue, + actualValue); + } + } +} Index: src/schema/javax/jdo/jdoconfig_2_1.xsd =================================================================== --- src/schema/javax/jdo/jdoconfig_2_1.xsd (revision 0) +++ src/schema/javax/jdo/jdoconfig_2_1.xsd (revision 0) @@ -0,0 +1,172 @@ + + + + + + + This is the XML Schema for the JDO configuration file. + + + + + + + + The root configuration element for JDO. + + + + + + + + Standard JDO PersistenceManagerFactory + configuration properties. + Vendor-specific properties are set using + additional vendor-specific attributes and/or + property elements. + + + + + + + + Vendor-specific properties. + + + + + + + + + + javax.jdo.listener.InstanceLifecycleListener + instance configuration. + There is one + instance-lifecycle-listener element + per listener instance. + Only one instance of the listener + class is supported in this + configuration file. + If multiple instances of the same + listener class is required, then the + API + PersistenceManagerFactory.addInstanceLifecycleListener(...) + must be used. + + If attribute "classes" is missing, + all persistence-capable instances + are observed, + otherwise it is a comma- or + whitespace-delimited list of + persistence-capable + classes whose instances' will be + observed. + + + + + + + + + + + + + + + + + + + + These are attributes corresponding to the standard properties + defined in JDO 2.1. + Any other attributes present, if unrecognized by a JDO + implementation, may be silently ignored. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: src/schema/javax/jdo/jdoconfig_2_1.dtd =================================================================== --- src/schema/javax/jdo/jdoconfig_2_1.dtd (revision 0) +++ src/schema/javax/jdo/jdoconfig_2_1.dtd (revision 0) @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: src/java/javax/jdo/PersistenceManagerFactory.java =================================================================== --- src/java/javax/jdo/PersistenceManagerFactory.java (revision 513879) +++ src/java/javax/jdo/PersistenceManagerFactory.java (working copy) @@ -25,6 +25,8 @@ import java.util.Properties; import java.util.Collection; +import java.io.Serializable; + import javax.jdo.datastore.DataStoreCache; import javax.jdo.listener.InstanceLifecycleListener; @@ -54,21 +56,8 @@ * @version 2.1 */ -public interface PersistenceManagerFactory extends java.io.Serializable { +public interface PersistenceManagerFactory extends Serializable { - /** - * The value for TransactionType to specify that transactions - * are managed by the Java Transactions API, as documented in - * JSR-220. - */ - public static final String JTA = "JTA"; - - /** - * The value for TransactionType to specify that transactions - * are managed by the javax.jdo.Transaction instance, similar - * to the usage as documented in JSR-220. - */ - public static final String RESOURCE_LOCAL = "RESOURCE_LOCAL"; /** Close this PersistenceManagerFactory. Check for * JDOPermission("closePersistenceManagerFactory") and if not authorized, @@ -425,8 +414,8 @@ * This has the same semantics as the same-named property in * JSR-220 EntityManagerFactory. * @see #getTransactionType() - * @see #JTA - * @see #RESOURCE_LOCAL + * @see Constants#JTA + * @see Constants#RESOURCE_LOCAL * @since 2.1 * @param name the TransactionType * @throws JDOUserException if the parameter is not a permitted value Index: src/java/javax/jdo/JDOHelper.java =================================================================== --- src/java/javax/jdo/JDOHelper.java (revision 513879) +++ src/java/javax/jdo/JDOHelper.java (working copy) @@ -22,6 +22,14 @@ package javax.jdo; +import org.xml.sax.SAXException; + +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.Element; + import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -31,16 +39,20 @@ import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; +import java.security.AccessController; +import java.security.PrivilegedAction; + +import java.net.URL; + +import java.util.Collection; import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Properties; +import java.util.HashMap; +import java.util.Enumeration; +import java.util.Collections; -import java.security.AccessController; -import java.security.PrivilegedAction; - import javax.jdo.spi.I18NHelper; import javax.jdo.spi.JDOImplHelper; import javax.jdo.spi.JDOImplHelper.StateInterrogationBooleanReturn; @@ -54,7 +66,12 @@ import javax.rmi.PortableRemoteObject; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.FactoryConfigurationError; + /** * This class can be used by a JDO-aware application to call the JDO behavior * of PersistenceCapable instances without declaring them to be @@ -69,13 +86,100 @@ * * @version 2.1 */ -public class JDOHelper extends Object { - +public class JDOHelper extends Object implements Constants { + + /** + * A mapping from jdoconfig.xsd element attributes to PMF properties. + */ + static final Map ATTRIBUTE_PROPERTY_XREF + = createAttributePropertyXref(); + + /** + * The standard XML schema type. + */ + protected static final String XSD_TYPE + = "http://www.w3.org/2001/XMLSchema"; + + /** + * The JAXP schema language property. + */ + protected static final String SCHEMA_LANGUAGE_PROP + = "http://java.sun.com/xml/jaxp/properties/schemaLanguage"; + + /** + * External schema location property. + */ + protected static final String SCHEMA_LOCATION_PROP + = "http://apache.org/xml/properties/schema/external-schemaLocation"; + /** The Internationalization message helper. */ private final static I18NHelper msg = I18NHelper.getInstance ("javax.jdo.Bundle"); //NOI18N + /** + * Creates a map from jdoconfig.xsd element attributes to PMF properties. + * @return An unmodifiable Map of jdoconfig.xsd element attributes to PMF + * properties. + */ + static Map createAttributePropertyXref() { + Map xref = new HashMap(); + + xref.put( + PMF_ATTRIBUTE_CLASS, + PROPERTY_PERSISTENCE_MANAGER_FACTORY_CLASS); + xref.put( + PMF_ATTRIBUTE_CONNECTION_DRIVER_NAME, + PROPERTY_CONNECTION_DRIVER_NAME); + xref.put( + PMF_ATTRIBUTE_CONNECTION_FACTORY_NAME, + PROPERTY_CONNECTION_FACTORY_NAME); + xref.put( + PMF_ATTRIBUTE_CONNECTION_FACTORY2_NAME, + PROPERTY_CONNECTION_FACTORY2_NAME); + xref.put( + PMF_ATTRIBUTE_CONNECTION_PASSWORD, + PROPERTY_CONNECTION_PASSWORD); + xref.put( + PMF_ATTRIBUTE_CONNECTION_URL, + PROPERTY_CONNECTION_URL); + xref.put( + PMF_ATTRIBUTE_CONNECTION_USER_NAME, + PROPERTY_CONNECTION_USER_NAME); + xref.put( + PMF_ATTRIBUTE_IGNORE_CACHE, + PROPERTY_IGNORE_CACHE); + xref.put( + PMF_ATTRIBUTE_MAPPING, + PROPERTY_MAPPING); + xref.put( + PMF_ATTRIBUTE_MULTITHREADED, + PROPERTY_MULTITHREADED); + xref.put( + PMF_ATTRIBUTE_NONTRANSACTIONAL_READ, + PROPERTY_NONTRANSACTIONAL_READ); + xref.put( + PMF_ATTRIBUTE_NONTRANSACTIONAL_WRITE, + PROPERTY_NONTRANSACTIONAL_WRITE); + xref.put( + PMF_ATTRIBUTE_OPTIMISTIC, + PROPERTY_OPTIMISTIC); + xref.put( + PMF_ATTRIBUTE_PERSISTENCE_UNIT_NAME, + PROPERTY_PERSISTENCE_UNIT_NAME); + xref.put( + PMF_ATTRIBUTE_RESTORE_VALUES, + PROPERTY_RESTORE_VALUES); + xref.put( + PMF_ATTRIBUTE_RETAIN_VALUES, + PROPERTY_RETAIN_VALUES); + xref.put( + PMF_ATTRIBUTE_DETACH_ALL_ON_COMMIT, + PROPERTY_DETACH_ALL_ON_COMMIT); + + return Collections.unmodifiableMap(xref); + } + /** The JDOImplHelper instance used for handling non-binary-compatible * implementations. */ @@ -487,23 +591,35 @@ } } - /** Get a PersistenceManagerFactory based on a Properties + /** Get the anonymous PersistenceManagerFactory configured via the standard + * configuration file resource "META-INF/jdoconfig.xml", using the current thread's context class loader + * to locate the configuration file resource(s). + * @return the anonymous PersistenceManagerFactory. + * @since 2.1 + * @see #getPersistenceManagerFactory(String,ClassLoader) + */ + public static PersistenceManagerFactory getPersistenceManagerFactory() { + ClassLoader cl = getContextClassLoader(); + return getPersistenceManagerFactory ((String)null, cl); + } + + /** Get a PersistenceManagerFactory based on a Properties * instance, using the current thread's context class loader to locate the * PersistenceManagerFactory class. * @return the PersistenceManagerFactory. - * @param props a Properties instance with properties of the + * @param props a Properties instance with properties of the * PersistenceManagerFactory. - * @see #getPersistenceManagerFactory(Map,ClassLoader) + * @see #getPersistenceManagerFactory(java.util.Map,ClassLoader) */ public static PersistenceManagerFactory getPersistenceManagerFactory (Map props) { ClassLoader cl = getContextClassLoader(); return getPersistenceManagerFactory (props, cl); } - + /** * Get a PersistenceManagerFactory based on a - * Properties instance and a class loader. + * Map instance and a class loader. * The following are standard key values: *
"javax.jdo.PersistenceManagerFactoryClass" *
"javax.jdo.option.Optimistic", @@ -520,8 +636,20 @@ *
"javax.jdo.option.ConnectionFactory2Name", *
"javax.jdo.option.Mapping", *
"javax.jdo.mapping.Catalog", - *
"javax.jdo.mapping.Schema". - *

JDO implementations + *
"javax.jdo.mapping.Schema", + *
"javax.jdo.option.PersistenceUnitName". + * + * and properties of the form + *
javax.jdo.option.InstanceLifecycleListener.{listenerClass}={pcClasses} + * where {listenerClass} is the fully qualified name of a + * class that implements + * {@link javax.jdo.listener.InstanceLifecycleListener}, and + * {pcClasses} is an optional comma- or whitespace-delimited + * list of persistence-capable classes to be observed; the absence of a + * value for a property of this form means that instances of all + * persistence-capable classes will be observed by an instance of the given + * listener class. + *

JDO implementations * are permitted to define key values of their own. Any key values not * recognized by the implementation must be ignored. Key values that are * recognized but not supported by an implementation must result in a @@ -584,64 +712,72 @@ } /** - * Returns a {@link PersistenceManagerFactory} configured based + * Returns a named {@link PersistenceManagerFactory} with the given persistence unit name or, + * if not found, a {@link PersistenceManagerFactory} configured based * on the properties stored in the resource at - * propsResource. This method is equivalent to + * name. This method is equivalent to * invoking {@link * #getPersistenceManagerFactory(String,ClassLoader)} with * Thread.currentThread().getContextClassLoader() as * the loader argument. + * If multiple persistence units with the name given are found, a {@link JDOFatalUserException} is thrown. * @since 2.0 - * @param propsResource the resource containing the Properties + * @param name the persistence unit name or resource containing the Properties * @return the PersistenceManagerFactory */ public static PersistenceManagerFactory getPersistenceManagerFactory - (String propsResource) { - return getPersistenceManagerFactory (propsResource, + (String name) { + return getPersistenceManagerFactory (name, getContextClassLoader()); } /** - * Returns a {@link PersistenceManagerFactory} configured based + * Returns a named {@link PersistenceManagerFactory} with the given persistence unit name or, + * if not found, a {@link PersistenceManagerFactory} configured based * on the properties stored in the resource at - * propsResource. Loads the resource via + * name. Loads the resource via * loader, and creates a {@link * PersistenceManagerFactory} with loader. Any * IOExceptions thrown during resource loading will * be wrapped in a {@link JDOFatalUserException}. + * If multiple persistence units with the name given are found, a {@link JDOFatalUserException} is thrown. * @since 2.0 - * @param propsResource the resource containing the Properties - * @param loader the class loader to use to load both the propsResource and + * @param name the persistence unit name or resource containing the Properties + * @param loader the class loader to use to load both the name and * the PersistenceManagerFactory class * @return the PersistenceManagerFactory */ public static PersistenceManagerFactory getPersistenceManagerFactory - (String propsResource, ClassLoader loader) { - return getPersistenceManagerFactory(propsResource, loader, loader); + (String name, ClassLoader loader) { + return getPersistenceManagerFactory(name, loader, loader); } /** - * Returns a {@link PersistenceManagerFactory} configured based + * Returns a named {@link PersistenceManagerFactory} with the given + * persistence unit name or, + * if not found, a {@link PersistenceManagerFactory} configured based * on the properties stored in the resource at - * propsResource. Loads the Properties via - * propsLoader, and creates a {@link + * name. Loads the Properties via + * resourceLoader, and creates a {@link * PersistenceManagerFactory} with pmfLoader. Any - * IOExceptions thrown during resource loading will + * exceptions thrown during resource loading will * be wrapped in a {@link JDOFatalUserException}. + * If multiple persistence units with the requested name are found, a + * {@link JDOFatalUserException} is thrown. * @since 2.0 - * @param propsResource the resource containing the Properties - * @param propsLoader the class loader to use to load the propsResource + * @param name the persistence unit name or resource containing the Properties + * @param resourceLoader the class loader to use to load the name * @param pmfLoader the class loader to use to load the * PersistenceManagerFactory class * @return the PersistenceManagerFactory */ public static PersistenceManagerFactory getPersistenceManagerFactory - (String propsResource, ClassLoader propsLoader, ClassLoader pmfLoader) { - - if (propsResource == null) + (String name, ClassLoader resourceLoader, ClassLoader pmfLoader) { + + if (name == null) throw new JDOFatalUserException (msg.msg ( "EXC_GetPMFNullResource")); //NOI18N - if (propsLoader == null) + if (resourceLoader == null) throw new JDOFatalUserException (msg.msg ( "EXC_GetPMFNullPropsLoader")); //NOI18N if (pmfLoader == null) @@ -651,14 +787,14 @@ Properties props = new Properties (); InputStream in = null; try { - in = propsLoader.getResourceAsStream (propsResource); - if (in == null) - throw new JDOFatalUserException (msg.msg ( - "EXC_GetPMFNoResource", propsResource, propsLoader)); //NOI18N - props.load (in); + in = resourceLoader.getResourceAsStream (name); + if (in != null) { + props.load (in); + return getPersistenceManagerFactory (props, pmfLoader); + } } catch (IOException ioe) { throw new JDOFatalUserException (msg.msg ( - "EXC_GetPMFIOExceptionRsrc", propsResource), ioe); //NOI18N + "EXC_GetPMFIOExceptionRsrc", name), ioe); //NOI18N } finally { if (in != null) @@ -667,11 +803,572 @@ } catch (IOException ioe) { } } - return getPersistenceManagerFactory (props, pmfLoader); + // JDO 2.1: try to find named PMF + PersistenceManagerFactory pmf = getNamedPersistenceManagerFactory( + name == null ? "" : name.trim(), + resourceLoader, + pmfLoader + ); + if (pmf != null) { + return pmf; + } + // else + throw new JDOFatalUserException (msg.msg ( + "EXC_GetPMFNoResource", name, resourceLoader)); //NOI18N } + + /** Find and return