Allow users to supply property-overriding Map instances when configuring from 
resources (properties, jdoconfig.xml, or persistence.xml)
---------------------------------------------------------------------------------------------------------------------------------------

                 Key: JDO-545
                 URL: https://issues.apache.org/jira/browse/JDO-545
             Project: JDO
          Issue Type: Improvement
          Components: api2, api2-legacy
    Affects Versions: JDO 2 maintenance release 1
            Reporter: Matthew T. Adams
            Assignee: Matthew T. Adams
             Fix For: JDO 2 maintenance release 1


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>



-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.

Reply via email to