There are three ways to obtain a persistence manager factory:
By calling the getPersistenceManagerFactory method in JDOHelper
By finding the persistence manager factory in JNDI
By construction
The first two methods provide a persistence manager factory that is preconfigured and unmodifiable. The last method provides a persistence manager factory that can be configured until it produces the first persistence manager. After it produces the first persistence manager, the factory becomes unmodifiable.
Although the JDOHelper class is described in detail in the next chapter, this is the appropriate place to describe its method for producing persistence manager factories. JDOHelper is a utility class that contains, among other things, the following method:
public static PersistenceManagerFactory getPersistenceManagerFactory( java.util.Properties props)
Calling the getPersistenceManagerFactory method in JDOHelper is likely to be popular because the code that uses it is vendor independent. The vendor dependencies are placed into the properties where they are easy to manage. The method accepts a list of standard JDO property names and accepts any vendor-specific property names that the implementation recognizes. This method ignores property names that the JDO implementation does not recognize.
The following list contains all the standard property names:
javax.jdo.PersistenceManagerFactoryClass javax.jdo.option.Optimistic javax.jdo.option.RetainValues javax.jdo.option.RestoreValues javax.jdo.option.NontransactionalRead javax.jdo.option.NontransactionalWrite javax.jdo.option.Multithreaded javax.jdo.option.IgnoreCache javax.jdo.option.ConnectionUserName javax.jdo.option.ConnectionPassword javax.jdo.option.ConnectionURL javax.jdo.option.ConnectionDriverName javax.jdo.option.ConnectionFactoryName javax.jdo.option.ConnectionFactory2Name
As can be seen by comparing this list to Figure 6-1, the list includes the Boolean and string properties of the PersistenceManagerFactory interface.
The standard property javax.jdo.PersistenceManagerFactoryClass is required and takes the fully qualified class name of the vendor's class that implements the PersistenceManagerFactory interface. If the named class cannot be found, the method throws a JDOFatalUserException that wraps the underlying java.lang.ClassNotFoundException.
The next seven properties are Boolean. When any of them is not specified, the implementation's default value is used. The implementation's default value may be true or false, and the default value can vary from property to property. All implementations support setting the standard Boolean properties to false. If the implementation doesn't support the configuration contained in the properties, then the getPersistenceManagerFactory method throws a JDOUnsupportedOptionException. For example, if the implementation does not support the setting
javax.jdo.option.NontransactionalRead=true
then the getPersistenceManagerFactory method throws the JDOUnsupportedOptionException. Chapter 7 describes the JDO exceptions.
The last six properties are the six connection properties in the PersistenceManagerFactory interface that take string values. A later section of this chapter, "Configuring a Persistence Manager Factory," describes in detail these six connection properties as well as the two connection factory properties that are Object types.
There are several ways that the property settings acquire vendor dependencies. To begin with, the class named in the javax.jdo.PersistenceManagerFactoryClass property is clearly dependent on the implementation. Less obvious is the dependency introduced by utilizing a true value for one or more of the five transactional properties. One implementation may support an optimistic transaction, while another may not. Other vendor dependencies arise because different implementations use different connection properties. One may expect a value for javax.jdo.option.ConnectionURL, while another may expect a value for javax.jdo.option.ConnectionFactoryName. Finally, each implementation may expect its own set of vendor-specific properties that may or may not be optional. Although property files have vendor dependencies, it is beneficial to have the dependencies encapsulated within the property file.
Generally, the application stores the properties in a property file that it loads before calling the getPersistenceManagerFactory method in JDOHelper. Listing 6-2 presents an example of the code required to obtain a PersistenceManagerFactory object by using a properties file. The TestJDOHelper class shown in Listing 6-2 has only three methods, main, getPMF, and loadProperties. The main method calls the getPMF method to get a persistence manager factory, and uses the factory to get a persistence manager. The getPMF method calls loadProperties to load a property file found in the class path into a Properties object. Using the Properties object, the getPMF method then calls the getPersistenceManagerFactory method in JDOHelper. This example is included in the JDO Learning Tools programs that are described in Chapter 8.
Listing 6-2: Obtaining a PersistenceManagerFactory from JDOHelper
package com.ysoft.jdo.book.factory; import java.util.*; import java.io.*; import javax.jdo.*; public class TestJDOHelper { public static PersistenceManagerFactory getPMF(String propFileName) { Properties props; try { props = loadProperties(propFileName); props.list(System.out); } catch (Exception e) { System.out.println("Caught exception trying to load properties file"); e.printStackTrace(System.out); return null; } return JDOHelper.getPersistenceManagerFactory(props); } public static Properties loadProperties(String propFileName) throws IOException { ClassLoader cl = Thread.currentThread().getContextClassLoader(); InputStream stream = cl.getResourceAsStream(propFileName); if (stream == null) throw new IOException("File not found: " + propFileName); Properties props = new Properties(); props.load(stream); stream.close(); return props; } public static void main(String [] args) { if (args.length < 1) { System.out.println( "usage: java com.ysoft.jdo.book.factory.TestJDOHelper " + "<property file name>"); System.exit(1); } PersistenceManagerFactory pmf = getPMF(args[0]); if (pmf != null && pmf.getPersistenceManager() != null) System.out.println("Got the PMF okay"); } }
After the persistence manager factory is obtained from the getPersistenceManagerFactory method in JDOHelper, its properties cannot be modified. Its configuration is frozen. If the application wants to configure the factory, it must configure the properties prior to calling the getPersistenceManagerFactory method. Even when the factory is frozen, it still returns persistence managers that are not frozen. For example, the application can still configure the IgnoreCache property in the PersistenceManager interface even though the factory that produced the persistence manager is frozen.
When a managed transaction is active, the factory may enlist the persistence manager in the managed transaction. In this case, some of the properties in the Transaction interface are not configurable because the JDO transaction is active. A later section in this chapter, "JDO's Support for Container-Managed Transactions," explains when the factory enlists the persistence manager in the managed transaction.
An example of a properties file for opening a connection to the reference implementation is show in Listing 6-3. The connection properties specify a connection to the b-tree datastore used by the reference implementation. All seven Boolean properties are specified because in some cases the reference implementations default values are true.
Listing 6-3: Example of a Properties File Used to Obtain a PersistenceManagerFactory
# JDORI JDO Properties configuration javax.jdo.PersistenceManagerFactoryClass=com.sun.jdori.fostore.FOStorePMF javax.jdo.option.ConnectionURL=fostore:FOStoreTestDB javax.jdo.option.ConnectionUserName=JDO javax.jdo.option.ConnectionPassword=book javax.jdo.option.IgnoreCache=false javax.jdo.option.Multithreaded=false javax.jdo.option.NontransactionalRead=false javax.jdo.option.NontransactionalWrite=false javax.jdo.option.Optimistic=false javax.jdo.option.RetainValues=false javax.jdo.option.RestoreValues=false
Assuming that one or more persistence manager factories are stored in JNDI, each of them can be retrieved using the JNDI name. This is the traditional method of obtaining a connection within EJB code. The use of JNDI supports J2EE's separation of code from deployment configuration. The code to perform the lookup in JNDI is shown in Listing 6-4.
Listing 6-4: Code to Find a PersistenceManagerFactory in JNDI
String jndiName = "someName"; // pick the name you'll use PersistenceManagerFactory pmf = null; try { pmf = (PersistenceManagerFactory) new InitialContext().lookup(jndiName); } catch (NamingException ne) { // handle exception ... }
Storing the persistence manager factory into JNDI is dependent on the configuration tools available. In some cases, the J2EE application server has options to configure JNDI.
In the JDO Learning Tools, the JndiLocator class in the com.ysoft.jdo.book.factory package has a utility method, getPMF, that takes two parameters, the JNDI name of the PersistenceManagerFactory object and the file name of a properties file. If getPMF does not find the factory in JNDI, it calls the getPersistenceManagerFactory method in JDOHelper to create one, which it stores in JNDI before returning.
In order to construct a PersistenceManagerFactory, the application must construct the vendor's class that implements this interface. The vendor defines the parameters required by the constructor. Construction has a couple of advantages over the other methods of obtaining a persistence manager factory. Unlike the other methods, it yields a persistence manager factory that can be configured. The factory remains configurable until it returns the first persistence manager. As in the other cases, a persistence manager and its transaction can still be configured after the factory's configuration is frozen. The second advantage of construction arises when there is some amount of vendor-specific work to do anyway. In some cases, the application may want to configure properties specified by the vendor's implementation class. For example, there might be a vendor-specified way to configure logging.