Note: I created a JIRA issue, JDO-545, for this. This email is just
a copy of it...
See my changes inline below, enclosed by "<<<" and ">>>", and my
comments.
-matthew
On Oct 1, 2007, at 2:07 PM, Craig L Russell wrote:
Javadogs,
I've completed the specification update for these methods. Please
take a look and comment:
Note that this section doesn't exactly match what Matthew has
already implemented, but really close...
<proposed>
PersistenceManagerFactory methods
The methods in this section provide for bootstrapping the
PersistenceManagerFactory by configuration according to the
properties documented in Section 11.1. Users have a choice of
configuration techniques:
The application provides a Map of properties used to construct a
PersistenceManagerFactory
The application provides a Map of override properties and <<<the
name of a resource in standard Java Properties format, the name of
a named PersistenceManagerFactory, or a JPA persistence unit
name>>> that are used to construct a PersistenceManagerFactory
The application provides the name of a resource in standard Java
Properties format whose contents define the properties for the
PersistenceManagerFactory
The application provides an InputStream in standard Java Properties
format whose contents define the properties for the
PersistenceManagerFactory
The application provides a File whose contents are in standard Java
Properties format which define the properties for the
PersistenceManagerFactory
The application provides a JNDI name and context in which the name
is defined
The application provides a resource named META-INF/jdoconfig.xml
and optionally META-INF/services/
javax.jdo.PersistenceManagerFactory which contain configuration
information
The application provides a resource named META-INF/persistence.xml
and optionally META-INF/services/
javax.persistence.EntityManagerFactory which contain configuration
information
For the cases of InputStream, File, and resource name, a Properties
instance is constructed by JDOHelper and passed to one of the
getPersistenceManagerFactory(Map) methods. When using these
techniques, each configuration of PersistenceManagerFactory is
contained <<<either in a Java Properties resource, in a META-INF/
jdoconfig.xml and optionally a META-INF/services/
javax.jdo.PersistenceManagerFactory, or in a META-INF/
persistence.xml and optionally a META-INF/services/
javax.persistence.EntityManagerFactory. The >>> propsLoader
parameter is used only to load the resource to construct the Map.
Once the Map with configuration information is obtained, the loader
is used for loading all other resour<<<c>>>es,
Comment: I'm confused about what the "propsLoader" and the "loader"
are. I'll assume the "propsLoader" is the ClassLoader used to load
resources required to build the Properties instance (aka "Map"), and
the "loader" is the ClassLoader used to load classes and whatever
other resources whose loading can't be controlled.
including the persistence.xml, jdoconfig.xml, services locators,
and PersistenceManagerFactory class.
Comment: This is not true; some of these resources are loaded by
propsLoader, some are not. If all native JDO means of creating a
Properties instance (aka "Map") fail, we delegate to
javax.persistence.Persistence, and we can't tell
Persistence.createEntityManagerFactory(..) which ClassLoader to use
for resource loading and which to use for class loading; in fact, we
can't tell it anything about which ClassLoader(s) to use.
In order to build the Properties instance (aka "Map"), the following
resources are loaded by the propsLoader: the Java Properties
resource with the given name, and if that's not found, all META-INF/
jdoconfig.xml files on the classpath. If, after constructing a
Properties instance there is no
javax.jdo.PersistenceManagerFactoryClass property, then the
propsLoader is used again to load a META-INF/services/
javax.jdo.PersistenceManagerFactory; the exact one is determined by
ClassLoader's getResourceAsStream(String) method. If no META-INF/
jdoconfig.xml files are found or no PersistenceManagerFactory is
found with the requested name, then JDOHelper delegates to
javax.persistence.Persistence.createEntityManagerFactory(String) to
look up the META-INF/persistence.xml file with the persistence unit
name given, then casts the returned EntityManagerFactory to a
PersistenceManagerFactory.
A8.6-22 [public static
PersistenceManagerFactory getPersistenceManagerFactory
(Map props, String name, ClassLoader loader);]
A8.6-23 [public static
PersistenceManagerFactory getPersistenceManagerFactory
(Map props, String name);]
These methods create a new copy of the Map parameter, add the
property “javax.jdo.option.Name” with the value of the name
parameter and delegate to the corresponding JDOHelper method taking
a Map parameter.
Comment: This is new and not yet coded. I understand the motivation
to add these methods (which is Persistence.createEntityManagerFactory
(String,Map)), so I'm cool with adding these, but we'll have to
tighten up the description of the overriding Properties, especially
in the case that no Java Properties resource is found and the named
PMF is not found. Do we pass the given Map down to
Persistence.createEntityManagerFactory(String,Map)? I am inclined to
say yes (see end comments plus the patch).
A8.6-13 [public static
PersistenceManagerFactory getPersistenceManagerFactory
(File propsFile);]
A8.6-14 [public static
PersistenceManagerFactory getPersistenceManagerFactory
(File propsFile, ClassLoader loader);]
A8.6-17 [public static
PersistenceManagerFactory getPersistenceManagerFactory
(InputStream stream);]
A8.6-18 [public static
PersistenceManagerFactory getPersistenceManagerFactory
(InputStream stream, ClassLoader loader);]
These methods use the parameter(s) passed as arguments to construct
a Properties instance, and then delegate to the JDOHelper method
getPersistenceManagerFactory that takes a Map parameter.
A8.6-15 [public static
PersistenceManagerFactory getPersistenceManagerFactory
(String propsResourceName);]
A8.6-16 [public static
PersistenceManagerFactory getPersistenceManagerFactory
(String propsResourceName, ClassLoader loader);]
A8.6-21[public static
PersistenceManagerFactory getPersistenceManagerFactory
(String propsResourceName, ClassLoader propsLoader,
ClassLoader pmfLoader);]
These methods use the propsLoader getResourceAsStream method with
the propsResourceName as an argument to obtain an InputStream,
construct a new Properties instance, then delegate to the
corresponding JDOHelper method getPersistenceManagerFactory that
takes a Map parameter.
Comment: Correct.
If the named resource does not exist, JDOHelper constructs a new
Map, adds a property named “javax.jdo.option.Name” with the value
of the propsResourceName parameter, and then delegates to the
corresponding JDOHelper method taking a Map parameter.
Comment: Minor detail (see below). JDOHelper conveniently converts
a null name to an empty string.
The empty string (not a null string) identifies the default
PersistenceManagerFactory name.
Comment: JDOHelper's gerPersistenceManagerFactory(String[,ClassLoader
[,ClassLoader]]) methods treat the empty string and a null string
equally. Officially, the empty string is the name of the anonymous
or unnamed PMF; null strings are just converted to empty strings for
convenience. We should probably add a constant
ANONYMOUS_PERSISTENCE_MANAGER_FACTOR_NAME (or
UNNAMED_PERSISTENCE_MANAGER_FACTORY_NAME) String with the value "" to
javax.jdo.Constants just so that it's explicit in the code. What do
you think?
A8.6-2 [public static
PersistenceManagerFactory getPersistenceManagerFactory
(Map props);]
A8.6-3 [This method delegates to the corresponding method with a
class loader parameter, using the calling thread’s current
contextClassLoader as the class loader.]
A8.6-1 [public static
PersistenceManagerFactory getPersistenceManagerFactory
(Map props, ClassLoader loader);]
This method returns a PersistenceManagerFactory based on entries
contained in the Map parameter. Three map entries are significant
to the processing of this method:
javax.jdo.option.PersistenceUnitName: If not null, this identifies
the name of the persistence unit to be accessed. The
Persistence.createEntityManagerFactory method is called with the
props and PersistenceUnitName as parameters. The result is cast to
PersistenceManagerFactory and returned to the user.
javax.jdo.option.Name: If not null, this identifies the name of the
PersistenceManagerFactory listed in jdoconfig. The class loader
loads META-INF/jdoconfig.xml and finds the
PersistenceManagerFactory by name. If the value of the entry
javax.jdo.option.Name is the empty string, this identifies the
unnamed PersistenceManagerFactory listed in jdoconfig. JDOHelper
constructs a Map instance with the contents of the jdoconfig entry
and overrides its contents with the props parameter. Processing
continues with PersistenceManagerFactoryClass.
javax.jdo.PersistenceManagerFactoryClass:
If not null, this identifies the name of the
PersistenceManagerFactory class. JDOHelper uses the loader to
resolve the PersistenceManagerFactory class name.
A8.6-6 [If the class named by the PersistenceManagerFactoryClass
property cannot be found, or is not accessible to the user, then
JDOFatalUserException is thrown.] JDOHelper invokes the static
method getPersistenceManagerFactory in the named class, passing the
props as the parameter. A8.6-7 [If there is no public static
implementation of the getPersistenceManagerFactory(Map) method, ]
A8.6-5 [or if there are any exceptions while trying to call the
static method,] A8.6-8 [or if the implementation of the static
getPersistenceManagerFactory(Map) method throws an exception, then
JDOFatalInternalException is thrown]. The nested exception
indicates the root cause of the exception.
Comment: Correct so far.
If null, JDOHelper loads the PersistenceManagerFactory class using
the services lookup pattern. That is, resources named META-INF/
services/<<<javax.jdo.>>>PersistenceManagerFactory are loaded by
the loader and each class in turn is used to call its static
getPersistenceManagerFactory method, passing the props as the
parameter. If one of the resources succeeds in returning a non-null
PersistenceManagerFactory, it is returned to the user and any
exceptions (thrown by other resources) are ignored. If no resource
succeeds, JDOFatalUserException exception is thrown to the user,
with a nested exception for each failure.
Comment: The implementation only attempts to find one resource named
META-INF/services/javax.jdo/PersistenceManagerFactory (via
ClassLoader's getResourceAsStream(String) method), not all of them.
Are you suggesting that we change this?
The standard key values for the properties are found in Section 11.1.
Comment: To implement support for user-supplied, property-overriding
Maps, I suggest that we make the existing methods
getPersistenceManagerFactory(String name)
getPersistenceManagerFactory(String name, ClassLoader resourceLoader)
getPersistenceManagerFactory(String name, ClassLoader resourceLoader,
ClassLoader pmfLoader)
and the new methods
getPersistenceManagerFactory(Map overrides, String name)
getPersistenceManagerFactory(Map overrides, String name, ClassLoader
resourceLoader)
convenience methods that delegate to the meaty method
getPersistenceManagerFactory(Map overrides, String name, ClassLoader
resourceLoader, ClassLoader pmfLoader)
If I understand all of this correctly, then the last operation of
this method before delegating to
getPersistenceManagerFactory(Map props, ClassLoader resourceLoader,
ClassLoader pmfLoader)
is to replace any values in props with values from overrides. If
this method ends up delegating instead to
Persistence.createEntityManagerFactory(String,Map), then it will pass
the overriding properties as the Map in that method.
I implemented this while going through this post. Please review the
attached patch to make sure that I got it right.
Note that it is only in JDOHelper's getPersistenceManagerFactory(Map
props, ClassLoader resourceLoader, ClassLoader pmfLoader method that
META-INF/services lookup is used, and then only when there is no
javax.jdo.PersistenceManagerFactoryClass property in props!
JDO implementations are permitted to define key values of their
own. A8.6-9 [Any key values not recognized by the implementation
must be ignored.] A8.6-10 [Key values that are recognized but not
supported by an implementation must result in a
JDOFatalUserException thrown by the method.]
A8.6-11 [The returned PersistenceManagerFactory is not configurable
(the setXXX methods will throw an exception).] A8.6-12 [JDO
implementations might manage a map of instantiated
PersistenceManagerFactory instances based on specified property key
values, and return a previously instantiated
PersistenceManagerFactory instance. In this case, the properties of
the returned instance must exactly match the requested properties.]
A8.6-19 [public static
PersistenceManagerFactory getPersistenceManagerFactory
(String jndiLocation, Context context);]
A8.6-20 [EMAIL PROTECTED] public static
PersistenceManagerFactory getPersistenceManagerFactory
(String jndiLocation, Context context, ClassLoader loader);]
These methods look up the PersistenceManagerFactory using the
naming context and name supplied. The implementation’s factory
method is not called. The behavior of this method depends on the
implementation of the context and its interaction with the saved
PersistenceManagerFactory object. As with the other factory
methods, the returned PersistenceManagerFactory is not configurable.
</proposed>
Craig Russell
Architect, Sun Java Enterprise System http://java.sun.com/products/jdo
408 276-5638 mailto:[EMAIL PROTECTED]
P.S. A good JDO? O, Gasp!