View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software 
12   * distributed under the License is distributed on an "AS IS" BASIS, 
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
14   * See the License for the specific language governing permissions and 
15   * limitations under the License.
16   */
17  
18  /*
19   * JDOHelper.java
20   *
21   */
22   
23  package javax.jdo;
24  
25  import org.xml.sax.SAXException;
26  import org.xml.sax.SAXParseException;
27  import org.xml.sax.ErrorHandler;
28  import org.w3c.dom.Document;
29  import org.w3c.dom.Element;
30  import org.w3c.dom.NodeList;
31  import org.w3c.dom.Node;
32  import org.w3c.dom.NamedNodeMap;
33  
34  import javax.jdo.spi.I18NHelper;
35  import javax.jdo.spi.JDOImplHelper;
36  import javax.jdo.spi.JDOImplHelper.StateInterrogationBooleanReturn;
37  import javax.jdo.spi.JDOImplHelper.StateInterrogationObjectReturn;
38  import javax.jdo.spi.PersistenceCapable;
39  import javax.jdo.spi.StateInterrogation;
40  import javax.naming.Context;
41  import javax.naming.InitialContext;
42  import javax.naming.NamingException;
43  import javax.rmi.PortableRemoteObject;
44  import javax.xml.parsers.DocumentBuilder;
45  import javax.xml.parsers.DocumentBuilderFactory;
46  import javax.xml.parsers.FactoryConfigurationError;
47  import javax.xml.parsers.ParserConfigurationException;
48  import java.lang.reflect.InvocationTargetException;
49  import java.lang.reflect.Method;
50  import java.net.URL;
51  import java.security.AccessController;
52  import java.security.PrivilegedAction;
53  import java.security.PrivilegedActionException;
54  import java.security.PrivilegedExceptionAction;
55  import java.util.Map;
56  import java.util.HashMap;
57  import java.util.Collections;
58  import java.util.Collection;
59  import java.util.Iterator;
60  import java.util.List;
61  import java.util.ArrayList;
62  import java.util.Properties;
63  import java.util.Enumeration;
64  import java.io.IOException;
65  import java.io.InputStream;
66  import java.io.BufferedReader;
67  import java.io.InputStreamReader;
68  import java.io.File;
69  import java.io.FileInputStream;
70  import java.io.FileNotFoundException;
71  
72  
73  /***
74   * This class can be used by a JDO-aware application to call the JDO behavior
75   * of <code>PersistenceCapable</code> instances without declaring them to be
76   * <code>PersistenceCapable</code>.
77   * <P>It is also used to acquire a <code>PersistenceManagerFactory</code> via 
78   * various methods.
79   * <P>This helper class defines static methods that allow a JDO-aware
80   * application to examine the runtime state of instances.  For example,
81   * an application can discover whether the instance is persistent, 
82   * transactional, dirty, new, deleted, or detached; and to get its associated
83   * <code>PersistenceManager</code> if it has one.
84   * 
85   * @version 2.1
86   */
87  public class JDOHelper implements Constants {
88  
89      /***
90       * A mapping from jdoconfig.xsd element attributes to PMF properties.
91       */
92      static final Map ATTRIBUTE_PROPERTY_XREF
93          = createAttributePropertyXref();
94  
95      /*** The Internationalization message helper.
96       */
97      private final static I18NHelper msg = 
98          I18NHelper.getInstance ("javax.jdo.Bundle"); //NOI18N
99  
100     /***
101      * Creates a map from jdoconfig.xsd element attributes to PMF properties.
102      * @return An unmodifiable Map of jdoconfig.xsd element attributes to PMF
103      * properties.
104      */
105     static Map createAttributePropertyXref() {
106         Map xref = new HashMap();
107 
108         xref.put(
109             PMF_ATTRIBUTE_CLASS,
110             PROPERTY_PERSISTENCE_MANAGER_FACTORY_CLASS);
111         xref.put(
112             PMF_ATTRIBUTE_CONNECTION_DRIVER_NAME,
113             PROPERTY_CONNECTION_DRIVER_NAME);
114         xref.put(
115             PMF_ATTRIBUTE_CONNECTION_FACTORY_NAME,
116             PROPERTY_CONNECTION_FACTORY_NAME);
117         xref.put(
118             PMF_ATTRIBUTE_CONNECTION_FACTORY2_NAME,
119             PROPERTY_CONNECTION_FACTORY2_NAME);
120         xref.put(
121             PMF_ATTRIBUTE_CONNECTION_PASSWORD,
122             PROPERTY_CONNECTION_PASSWORD);
123         xref.put(
124             PMF_ATTRIBUTE_CONNECTION_URL,
125             PROPERTY_CONNECTION_URL);
126         xref.put(
127             PMF_ATTRIBUTE_CONNECTION_USER_NAME,
128             PROPERTY_CONNECTION_USER_NAME);
129         xref.put(
130             PMF_ATTRIBUTE_IGNORE_CACHE,
131             PROPERTY_IGNORE_CACHE);
132         xref.put(
133             PMF_ATTRIBUTE_MAPPING,
134             PROPERTY_MAPPING);
135         xref.put(
136             PMF_ATTRIBUTE_MULTITHREADED,
137             PROPERTY_MULTITHREADED);
138         xref.put(
139             PMF_ATTRIBUTE_NONTRANSACTIONAL_READ,
140             PROPERTY_NONTRANSACTIONAL_READ);
141         xref.put(
142             PMF_ATTRIBUTE_NONTRANSACTIONAL_WRITE,
143             PROPERTY_NONTRANSACTIONAL_WRITE);
144         xref.put(
145             PMF_ATTRIBUTE_OPTIMISTIC,
146             PROPERTY_OPTIMISTIC);
147         xref.put(
148             PMF_ATTRIBUTE_PERSISTENCE_UNIT_NAME,
149             PROPERTY_PERSISTENCE_UNIT_NAME);
150         xref.put(
151             PMF_ATTRIBUTE_NAME,
152             PROPERTY_NAME);
153         xref.put(
154             PMF_ATTRIBUTE_RESTORE_VALUES,
155             PROPERTY_RESTORE_VALUES);
156         xref.put(
157             PMF_ATTRIBUTE_RETAIN_VALUES,
158             PROPERTY_RETAIN_VALUES);
159         xref.put(
160             PMF_ATTRIBUTE_DETACH_ALL_ON_COMMIT,
161             PROPERTY_DETACH_ALL_ON_COMMIT);
162         xref.put(
163             PMF_ATTRIBUTE_SERVER_TIME_ZONE_ID,
164             PROPERTY_SERVER_TIME_ZONE_ID);
165 
166         return Collections.unmodifiableMap(xref);
167     }
168 
169     /*** The JDOImplHelper instance used for handling non-binary-compatible
170      *  implementations.
171      */
172     private static JDOImplHelper implHelper = (JDOImplHelper)
173         AccessController.doPrivileged(
174             new PrivilegedAction () {
175                 public Object run () {
176                     return JDOImplHelper.getInstance();
177                 }
178             }
179         );
180 
181     /*** The singleton instance of JDOHelper.
182      * @since 2.1
183      */
184     private static JDOHelper instance = new JDOHelper();
185 
186     /***
187      * Return the singleton instance of JDOHelper. This instance is 
188      * thread-safe.
189      * @since 2.1
190      * @return the thread-safe singleton JDOHelper
191      */
192     public static JDOHelper getInstance() {
193         return instance;
194     }
195 
196     /*** Some applications might prefer to use instance
197      * methods instead of static methods.
198      * @since 2.1
199      */
200     public JDOHelper() {}
201 
202     /*** The stateless instance used for handling non-binary-compatible
203     *  implementations of getPersistenceManager.
204     */
205     static StateInterrogationObjectReturn getPersistenceManager =
206         new StateInterrogationObjectReturn() {
207             public Object get(Object pc, StateInterrogation si) {
208                 return si.getPersistenceManager(pc);
209             }
210         };
211 
212    /*** The stateless instance used for handling non-binary-compatible
213     *  implementations of getObjectId.
214     */
215     static StateInterrogationObjectReturn getObjectId =
216         new StateInterrogationObjectReturn() {
217             public Object get(Object pc, StateInterrogation si) {
218                 return si.getObjectId(pc);
219             }
220         };
221 
222    /*** The stateless instance used for handling non-binary-compatible
223     *  implementations of getTransactionalObjectId.
224     */
225     static StateInterrogationObjectReturn getTransactionalObjectId =
226         new StateInterrogationObjectReturn() {
227             public Object get(Object pc, StateInterrogation si) {
228                 return si.getTransactionalObjectId(pc);
229             }
230         };
231 
232    /*** The stateless instance used for handling non-binary-compatible
233     *  implementations of getVersion.
234     */
235     static StateInterrogationObjectReturn getVersion =
236         new StateInterrogationObjectReturn() {
237             public Object get(Object pc, StateInterrogation si) {
238                 return si.getVersion(pc);
239             }
240         };
241 
242    /*** The stateless instance used for handling non-binary-compatible
243     *  implementations of isPersistent.
244     */
245     static StateInterrogationBooleanReturn isPersistent =
246         new StateInterrogationBooleanReturn() {
247             public Boolean is(Object pc, StateInterrogation si) {
248                 return si.isPersistent(pc);
249             }
250         };
251 
252    /*** The stateless instance used for handling non-binary-compatible
253     *  implementations of isTransactional.
254     */
255     static StateInterrogationBooleanReturn isTransactional =
256         new StateInterrogationBooleanReturn() {
257             public Boolean is(Object pc, StateInterrogation si) {
258                 return si.isTransactional(pc);
259             }
260         };
261 
262    /*** The stateless instance used for handling non-binary-compatible
263     *  implementations of isDirty.
264     */
265     static StateInterrogationBooleanReturn isDirty =
266         new StateInterrogationBooleanReturn() {
267             public Boolean is(Object pc, StateInterrogation si) {
268                 return si.isDirty(pc);
269             }
270         };
271 
272    /*** The stateless instance used for handling non-binary-compatible
273     *  implementations of isNew.
274     */
275     static StateInterrogationBooleanReturn isNew =
276         new StateInterrogationBooleanReturn() {
277             public Boolean is(Object pc, StateInterrogation si) {
278                 return si.isNew(pc);
279             }
280         };
281 
282    /*** The stateless instance used for handling non-binary-compatible
283     *  implementations of isDeleted.
284     */
285     static StateInterrogationBooleanReturn isDeleted =
286         new StateInterrogationBooleanReturn() {
287             public Boolean is(Object pc, StateInterrogation si) {
288                 return si.isDeleted(pc);
289             }
290         };
291 
292    /*** The stateless instance used for handling non-binary-compatible
293     *  implementations of isDetached.
294     */
295     static StateInterrogationBooleanReturn isDetached =
296         new StateInterrogationBooleanReturn() {
297             public Boolean is(Object pc, StateInterrogation si) {
298                 return si.isDetached(pc);
299             }
300         };
301 
302     /*** Return the associated <code>PersistenceManager</code> if there is one.
303      * Transactional and persistent instances return the associated
304      * <code>PersistenceManager</code>.  
305      *
306      * <P>Transient non-transactional instances and instances of classes 
307      * that do not implement <code>PersistenceCapable</code> return 
308      * <code>null</code>.
309      * @see PersistenceCapable#jdoGetPersistenceManager()
310      * @param pc the <code>PersistenceCapable</code> instance.
311      * @return the <code>PersistenceManager</code> associated with the parameter
312      * instance.
313      */
314      public static PersistenceManager getPersistenceManager(Object pc) {
315         if (pc instanceof PersistenceCapable) {
316             return ((PersistenceCapable)pc).jdoGetPersistenceManager();
317         } else {
318             return (PersistenceManager)
319                 implHelper.nonBinaryCompatibleGet(pc, getPersistenceManager);
320         }
321       }
322     
323     /*** Explicitly mark the parameter instance and field dirty.
324      * Normally, <code>PersistenceCapable</code> classes are able to detect 
325      * changes made to their fields.  However, if a reference to an array is 
326      * given to a method outside the class, and the array is modified, then the
327      * persistent instance is not aware of the change.  This API allows the
328      * application to notify the instance that a change was made to a field.
329      *
330      * <P>Transient instances and instances of classes 
331      * that do not implement <code>PersistenceCapable</code> ignore this method.
332      * @see PersistenceCapable#jdoMakeDirty(String fieldName)
333      * @param pc the <code>PersistenceCapable</code> instance.
334      * @param fieldName the name of the field to be marked dirty.
335      */
336     public static void makeDirty(Object pc, String fieldName) {
337         if (pc instanceof PersistenceCapable) {
338             ((PersistenceCapable)pc).jdoMakeDirty(fieldName);
339         } else {
340              implHelper.nonBinaryCompatibleMakeDirty(pc, fieldName);
341         }
342     }
343     
344     /*** Return a copy of the JDO identity associated with the parameter 
345      * instance.
346      *
347      * <P>Persistent instances of <code>PersistenceCapable</code> classes have a
348      * JDO identity managed by the <code>PersistenceManager</code>.  This method
349      * returns a copy of the ObjectId that represents the JDO identity.  
350      * 
351      * <P>Transient instances and instances of classes that do not implement 
352      * <code>PersistenceCapable</code> return <code>null</code>.
353      *
354      * <P>The ObjectId may be serialized
355      * and later restored, and used with a <code>PersistenceManager</code> from 
356      * the same JDO implementation to locate a persistent instance with the same
357      * data store identity.
358      *
359      * <P>If the JDO identity is managed by the application, then the ObjectId 
360      * may be used with a <code>PersistenceManager</code> from any JDO 
361      * implementation that supports the <code>PersistenceCapable</code> class.
362      *
363      * <P>If the JDO identity is not managed by the application or the data 
364      * store, then the ObjectId returned is only valid within the current 
365      * transaction.
366      *<P>
367      * @see PersistenceManager#getObjectId(Object pc)
368      * @see PersistenceCapable#jdoGetObjectId()
369      * @see PersistenceManager#getObjectById(Object oid, boolean validate)
370      * @param pc the PersistenceCapable instance.
371      * @return a copy of the ObjectId of the parameter instance as of the 
372      * beginning of the transaction.
373      */
374     public static Object getObjectId(Object pc) {
375       if (pc instanceof PersistenceCapable) {
376           return ((PersistenceCapable)pc).jdoGetObjectId();
377         } else {
378             return implHelper.nonBinaryCompatibleGet(pc, getObjectId);
379         }
380     }
381 
382     /*** Get object ids for a collection of instances. For each instance
383      * in the parameter, the getObjectId method is called. This method
384      * returns one identity instance for each element 
385      * in the parameter. The order of iteration of the returned
386      * Collection exactly matches the order of iteration of the
387      * parameter Collection.
388      * @param pcs the persistence-capable instances
389      * @return the object ids of the parameters
390      * @see #getObjectId(Object pc)
391      * @see #getObjectIds(Object[] pcs)
392      * @since 2.0
393      */
394     public static Collection getObjectIds(Collection pcs) {
395         ArrayList result = new ArrayList();
396         for (Iterator it = pcs.iterator(); it.hasNext();) {
397             result.add(getObjectId(it.next()));
398         }
399         return result;
400     }
401 
402     /*** Get object ids for an array of instances. For each instance
403      * in the parameter, the getObjectId method is called. This method
404      * returns one identity instance for each element 
405      * in the parameter. The order of instances of the returned
406      * array exactly matches the order of instances of the
407      * parameter array.
408      * @param pcs the persistence-capable instances
409      * @return the object ids of the parameters
410      * @see #getObjectId(Object pc)
411      * @see #getObjectIds(Collection pcs)
412      * @since 2.0
413      */
414     public static Object[] getObjectIds(Object[] pcs) {
415         Object[] result = new Object[pcs.length];
416         for (int i = 0; i < pcs.length; ++i) {
417             result[i] = getObjectId(pcs[i]);
418         }
419         return result;
420     }
421 
422     /*** Return a copy of the JDO identity associated with the parameter 
423      * instance.
424      *
425      * @see PersistenceCapable#jdoGetTransactionalObjectId()
426      * @see PersistenceManager#getObjectById(Object oid, boolean validate)
427      * @param pc the <code>PersistenceCapable</code> instance.
428      * @return a copy of the ObjectId of the parameter instance as modified in 
429      * this transaction.
430      */
431     public static Object getTransactionalObjectId(Object pc) {
432       if (pc instanceof PersistenceCapable) {
433           return ((PersistenceCapable)pc).jdoGetTransactionalObjectId();
434         } else {
435             return implHelper.nonBinaryCompatibleGet(
436                 pc, getTransactionalObjectId);
437         }
438     }
439     
440     /***
441      * Return the version of the instance.
442      * @since 2.0
443      * @param pc the instance
444      * @return the version of the instance
445      */
446     public static Object getVersion (Object pc) {
447       if (pc instanceof PersistenceCapable) {
448           return ((PersistenceCapable)pc).jdoGetVersion();
449         } else {
450             return implHelper.nonBinaryCompatibleGet(pc, getVersion);
451         }
452     }
453     /*** Tests whether the parameter instance is dirty.
454      *
455      * Instances that have been modified, deleted, or newly 
456      * made persistent in the current transaction return <code>true</code>.
457      *
458      *<P>Transient instances and instances of classes that do not implement 
459      * <code>PersistenceCapable</code> return <code>false</code>.
460      *<P>
461      * @see javax.jdo.spi.StateManager#makeDirty(PersistenceCapable pc, 
462      * String fieldName)
463      * @see PersistenceCapable#jdoIsDirty()
464      * @param pc the <code>PersistenceCapable</code> instance.
465      * @return <code>true</code> if the parameter instance has been modified in 
466      * the current transaction.
467      */
468     public static boolean isDirty(Object pc) {
469       if (pc instanceof PersistenceCapable) {
470           return ((PersistenceCapable)pc).jdoIsDirty();
471         } else {
472             return implHelper.nonBinaryCompatibleIs(pc, isDirty);
473         }
474     }
475 
476     /*** Tests whether the parameter instance is transactional.
477      *
478      * Instances whose state is associated with the current transaction 
479      * return true. 
480      *
481      *<P>Transient instances and instances of classes that do not implement 
482      * <code>PersistenceCapable</code> return <code>false</code>.
483      * @see PersistenceCapable#jdoIsTransactional()
484      * @param pc the <code>PersistenceCapable</code> instance.
485      * @return <code>true</code> if the parameter instance is transactional.
486      */
487     public static boolean isTransactional(Object pc) {
488       if (pc instanceof PersistenceCapable) {
489           return ((PersistenceCapable)pc).jdoIsTransactional();
490         } else {
491             return implHelper.nonBinaryCompatibleIs(pc, isTransactional);
492         }
493     }
494 
495     /*** Tests whether the parameter instance is persistent.
496      *
497      * Instances that represent persistent objects in the data store 
498      * return <code>true</code>. 
499      *
500      *<P>Transient instances and instances of classes that do not implement 
501      * <code>PersistenceCapable</code> return <code>false</code>.
502      *<P>
503      * @see PersistenceManager#makePersistent(Object pc)
504      * @see PersistenceCapable#jdoIsPersistent()
505      * @param pc the <code>PersistenceCapable</code> instance.
506      * @return <code>true</code> if the parameter instance is persistent.
507      */
508     public static boolean isPersistent(Object pc) {
509       if (pc instanceof PersistenceCapable) {
510           return ((PersistenceCapable)pc).jdoIsPersistent();
511         } else {
512             return implHelper.nonBinaryCompatibleIs(pc, isPersistent);
513         }
514     }
515 
516     /*** Tests whether the parameter instance has been newly made persistent.
517      *
518      * Instances that have been made persistent in the current transaction 
519      * return <code>true</code>.
520      *
521      *<P>Transient instances and instances of classes that do not implement 
522      * <code>PersistenceCapable</code> return <code>false</code>.
523      *<P>
524      * @see PersistenceManager#makePersistent(Object pc)
525      * @see PersistenceCapable#jdoIsNew()
526      * @param pc the <code>PersistenceCapable</code> instance.
527      * @return <code>true</code> if the parameter instance was made persistent
528      * in the current transaction.
529      */
530     public static boolean isNew(Object pc) {
531       if (pc instanceof PersistenceCapable) {
532           return ((PersistenceCapable)pc).jdoIsNew();
533         } else {
534             return implHelper.nonBinaryCompatibleIs(pc, isNew);
535         }
536     }
537 
538     /*** Tests whether the parameter instance has been deleted.
539      *
540      * Instances that have been deleted in the current transaction return 
541      * <code>true</code>.
542      *
543      *<P>Transient instances and instances of classes that do not implement 
544      * <code>PersistenceCapable</code> return <code>false</code>.
545      *<P>
546      * @see PersistenceManager#deletePersistent(Object pc)
547      * @see PersistenceCapable#jdoIsDeleted()
548      * @param pc the <code>PersistenceCapable</code> instance.
549      * @return <code>true</code> if the parameter instance was deleted
550      * in the current transaction.
551      */
552     public static boolean isDeleted(Object pc) {
553       if (pc instanceof PersistenceCapable) {
554           return ((PersistenceCapable)pc).jdoIsDeleted();
555         } else {
556             return implHelper.nonBinaryCompatibleIs(pc, isDeleted);
557         }
558     }
559     
560     /***
561      * Tests whether the parameter instance has been detached.
562      * 
563      * Instances that have been detached return true.
564      * 
565      * <P>Transient instances return false.
566      * <P>
567      * @see PersistenceCapable#jdoIsDetached()
568      * @return <code>true</code> if this instance is detached.
569      * @since 2.0
570      * @param pc the instance
571      */
572     public static boolean isDetached(Object pc) {
573       if (pc instanceof PersistenceCapable) {
574           return ((PersistenceCapable)pc).jdoIsDetached();
575         } else {
576             return implHelper.nonBinaryCompatibleIs(pc, isDetached);
577         }
578     }
579 
580     /*** Get the anonymous <code>PersistenceManagerFactory</code> configured via
581      * the standard configuration file resource "META-INF/jdoconfig.xml", using
582      * the current thread's context class loader
583      * to locate the configuration file resource(s).
584      * @return the anonymous <code>PersistenceManagerFactory</code>.
585      * @since 2.1
586      * @see #getPersistenceManagerFactory(Map,String,ClassLoader,ClassLoader)
587      */
588     public static PersistenceManagerFactory getPersistenceManagerFactory() {
589         ClassLoader cl = getContextClassLoader();
590         return getPersistenceManagerFactory(
591                 null, ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME, cl, cl);
592     }
593 
594     /*** Get the anonymous <code>PersistenceManagerFactory</code> configured via
595      * the standard configuration file resource "META-INF/jdoconfig.xml", using
596      * the given class loader.
597      * @return the anonymous <code>PersistenceManagerFactory</code>.
598      * @param pmfClassLoader the ClassLoader used to load resources and classes
599      * @since 2.1
600      * @see #getPersistenceManagerFactory(Map,String,ClassLoader,ClassLoader)
601      */
602     public static PersistenceManagerFactory getPersistenceManagerFactory(
603             ClassLoader pmfClassLoader) {
604         return getPersistenceManagerFactory(
605                 null,
606                 ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME,
607                 pmfClassLoader, pmfClassLoader);
608     }
609 
610     /*** Get a <code>PersistenceManagerFactory</code> based on a <code>Properties</code>
611      * instance, using the current thread's context class loader to locate the
612      * <code>PersistenceManagerFactory</code> class.
613      * @return the <code>PersistenceManagerFactory</code>.
614      * @param props a <code>Properties</code> instance with properties of the
615      * <code>PersistenceManagerFactory</code>.
616      * @see #getPersistenceManagerFactory(java.util.Map,ClassLoader)
617      */
618     public static PersistenceManagerFactory getPersistenceManagerFactory
619             (Map props) {
620         return getPersistenceManagerFactory(
621                 null, props, getContextClassLoader());
622     }
623 
624 
625     /*** Get a <code>PersistenceManagerFactory</code> based on a 
626      * <code>Map</code> and a class loader.
627      * This method delegates to the getPersistenceManagerFactory
628      * method that takes a Map of overrides and a Map of properties,
629      * passing null as the overrides parameter.
630      * @see #getPersistenceManagerFactory(java.util.Map, java.util.Map, ClassLoader)
631      * @return the <code>PersistenceManagerFactory</code>.
632      * @param props a <code>Map</code> with properties of the 
633      * <code>PersistenceManagerFactory</code>.
634      * @param pmfClassLoader the class loader used to load the
635      * <code>PersistenceManagerFactory</code> class
636      * @since 1.0
637      */
638     public static PersistenceManagerFactory getPersistenceManagerFactory
639             (Map props, ClassLoader pmfClassLoader) {
640         return getPersistenceManagerFactory(
641                 null, props, pmfClassLoader);
642     }
643 
644     /***
645      * Get a <code>PersistenceManagerFactory</code> based on a 
646      * <code>Map</code> of overrides, a <code>Map</code> of 
647      * properties, and a class loader.
648      * The following are standard key names:
649      * <BR><code>"javax.jdo.PersistenceManagerFactoryClass"
650      * <BR>"javax.jdo.option.Optimistic",
651      * <BR>"javax.jdo.option.RetainValues",
652      * <BR>"javax.jdo.option.RestoreValues",
653      * <BR>"javax.jdo.option.IgnoreCache",
654      * <BR>"javax.jdo.option.NontransactionalRead",
655      * <BR>"javax.jdo.option.NontransactionalWrite",
656      * <BR>"javax.jdo.option.Multithreaded",
657      * <BR>"javax.jdo.option.ConnectionUserName",
658      * <BR>"javax.jdo.option.ConnectionPassword",
659      * <BR>"javax.jdo.option.ConnectionURL",
660      * <BR>"javax.jdo.option.ConnectionFactoryName",
661      * <BR>"javax.jdo.option.ConnectionFactory2Name",
662      * <BR>"javax.jdo.option.Mapping",
663      * <BR>"javax.jdo.mapping.Catalog",
664      * <BR>"javax.jdo.mapping.Schema",
665      * <BR>"javax.jdo.option.PersistenceUnitName".
666      * <BR>"javax.jdo.option.DetachAllOnCommit".
667      * <BR>"javax.jdo.option.CopyOnAttach".
668      * <BR>"javax.jdo.option.TransactionType".
669      * <BR>"javax.jdo.option.ServerTimeZoneID".
670      * <BR>"javax.jdo.option.Name".
671      * </code>
672      * and properties of the form
673      * <BR><code>javax.jdo.option.InstanceLifecycleListener.{listenerClass}[=[{pcClasses}]]</code>
674      * where <code>{listenerClass}</code> is the fully qualified name of a
675      * class that implements
676      * {@link javax.jdo.listener.InstanceLifecycleListener}, and
677      * <code>{pcClasses}</code> is an optional comma- or whitespace-delimited
678      * list of persistence-capable classes to be observed; the absence of a
679      * value for a property of this form means that instances of all
680      * persistence-capable classes will be observed by an instance of the given
681      * listener class.
682      * <P>JDO implementations
683      * are permitted to define key values of their own.  Any key values not
684      * recognized by the implementation must be ignored.  Key values that are
685      * recognized but not supported by an implementation must result in a
686      * <code>JDOFatalUserException</code> thrown by the method.
687      * <P>The returned <code>PersistenceManagerFactory</code> is not 
688      * configurable (the <code>set<I>XXX</I></code> methods will throw an 
689      * exception).
690      * <P>JDO implementations might manage a map of instantiated
691      * <code>PersistenceManagerFactory</code> instances based on specified 
692      * property key values, and return a previously instantiated 
693      * <code>PersistenceManagerFactory</code> instance.  In this case, the 
694      * properties of the returned instance must exactly match the requested 
695      * properties.
696      * @return the <code>PersistenceManagerFactory</code>.
697      * @param props a <code>Properties</code> instance with properties of the 
698      * <code>PersistenceManagerFactory</code>.
699      * @param pmfClassLoader the class loader to use to load the
700      * <code>PersistenceManagerFactory</code> class
701      * @since 2.1
702      */
703     protected static PersistenceManagerFactory getPersistenceManagerFactory
704             (Map overrides, Map props, ClassLoader pmfClassLoader) {
705         List exceptions = new ArrayList();
706         if (pmfClassLoader == null)
707             throw new JDOFatalUserException (msg.msg (
708                 "EXC_GetPMFNullLoader")); //NOI18N
709 
710         String pmfClassName = (String) props.get (
711                 PROPERTY_PERSISTENCE_MANAGER_FACTORY_CLASS);
712 
713         if (!isNullOrBlank(pmfClassName)) {
714             return invokeGetPersistenceManagerFactoryOnImplementation(
715                         pmfClassName, overrides, props, pmfClassLoader);
716 
717         } else {
718         // PMF class name null or blank -- try services
719         // for each file META-INF/services/javax.jdo.PersistenceManagerFactory
720         // try to invoke the getPersistenceManagerFactory method of the
721         // implementation class
722             try {
723                 Enumeration urls = getResources(pmfClassLoader, 
724                     SERVICE_LOOKUP_PMF_RESOURCE_NAME);
725                 while (urls.hasMoreElements()) {
726                     pmfClassName = getClassNameFromURL((URL)urls.nextElement());
727                         return invokeGetPersistenceManagerFactoryOnImplementation(
728                             pmfClassName, overrides, props, pmfClassLoader);
729                 }
730             } catch (Throwable ex) {
731                 // remember exceptions from failed pmf invocations
732                 exceptions.add(ex);
733             }
734         }
735 
736         // no PMF class name and no services
737         
738         throw new JDOFatalUserException(msg.msg(
739                 "EXC_GetPMFNoPMFClassNamePropertyOrPUNameProperty"),
740                 (Throwable[])
741                     exceptions.toArray(new Throwable[exceptions.size()]));
742     }
743 
744     /*** Get a class name from a URL. The URL is from getResources with 
745      * e.g. META-INF/services/javax.jdo.PersistenceManagerFactory as the
746      * parameter. Parse the file, removing blank lines, comment lines,
747      * and comments.
748      * @param url the URL of the services file
749      * @return the name of the class contained in the file
750      * @throws java.io.IOException
751      * @since 2.1
752      */
753 
754     protected static String getClassNameFromURL (URL url) 
755             throws IOException {
756         InputStream is = openStream(url);
757         BufferedReader reader = new BufferedReader(new InputStreamReader(is));
758         String line = null;
759         try {
760             while ((line = reader.readLine()) != null) {
761                 line = line.trim();
762                 if (line.length() == 0 || line.startsWith("#")) {
763                     continue;
764                 }
765                 // else assume first line of text is the PMF class name
766                 String[] tokens = line.split("//s");
767                 String pmfClassName = tokens[0];
768                 int indexOfComment = pmfClassName.indexOf("#");
769                 if (indexOfComment == -1) {
770                     return pmfClassName;
771                 }
772                 // else pmfClassName has a comment at the end of it -- remove
773                 return pmfClassName.substring(0, indexOfComment);
774             }
775             return null;
776         } finally {
777             try {
778                 reader.close();
779             }
780             catch (IOException x) {
781                 // gulp
782             }
783         }
784     }
785 
786     /***
787      * Returns a named {@link PersistenceManagerFactory} or persistence
788      * unit.
789      *
790      * @since 2.1
791      * @see #getPersistenceManagerFactory(Map,String,ClassLoader,ClassLoader)
792      */
793     public static PersistenceManagerFactory getPersistenceManagerFactory
794         (String name) {
795         ClassLoader cl = getContextClassLoader();
796         return getPersistenceManagerFactory(null, name, cl, cl);
797     }
798 
799     /***
800      * Returns a named {@link PersistenceManagerFactory} or persistence
801      * unit.
802      *
803      * @since 1.0
804      * @see #getPersistenceManagerFactory(Map,String,ClassLoader,ClassLoader)
805      */
806     public static PersistenceManagerFactory getPersistenceManagerFactory
807         (String name, ClassLoader loader) {
808         
809         return getPersistenceManagerFactory(null, name, loader, loader);
810     }
811 
812     /***
813      * Returns a named {@link PersistenceManagerFactory} or persistence
814      * unit.
815      *
816      * @since 2.0
817      * @see #getPersistenceManagerFactory(Map,String,ClassLoader,ClassLoader)
818      */
819     public static PersistenceManagerFactory getPersistenceManagerFactory
820         (String name, ClassLoader resourceLoader, ClassLoader pmfLoader) {
821 
822         return getPersistenceManagerFactory(
823                 null, name, resourceLoader, pmfLoader);
824     }
825 
826     /***
827      * Returns a named {@link PersistenceManagerFactory} or persistence
828      * unit.
829      *
830      * @since 2.1
831      * @see #getPersistenceManagerFactory(Map,String,ClassLoader,ClassLoader)
832      */
833     public static PersistenceManagerFactory getPersistenceManagerFactory
834             (Map overrides, String name) {
835 
836         ClassLoader cl = getContextClassLoader();
837         return getPersistenceManagerFactory(overrides, name, cl, cl);
838     }
839 
840     /***
841      * Returns a named {@link PersistenceManagerFactory} or persistence
842      * unit.
843      *
844      * @since 2.1
845      * @see #getPersistenceManagerFactory(Map,String,ClassLoader,ClassLoader)
846      */
847     public static PersistenceManagerFactory getPersistenceManagerFactory
848             (Map overrides, String name, ClassLoader resourceLoader) {
849 
850         return getPersistenceManagerFactory(
851                 overrides, name, resourceLoader, resourceLoader);
852     }
853     
854 
855     /***
856      * Returns a {@link PersistenceManagerFactory} configured based
857      * on the properties stored in the resource at
858      * <code>name</code>, or, if not found, returns a
859      * {@link PersistenceManagerFactory} with the given
860      * name or, if not found, returns a
861      * <code>javax.persistence.EntityManagerFactory</code> cast to a
862      * {@link PersistenceManagerFactory}.  If the name given is null or consists
863      * only of whitespace, it is interpreted as
864      * {@link Constants#ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME}.
865      * The following are standard key names:
866      * <BR><code>"javax.jdo.PersistenceManagerFactoryClass"
867      * <BR>"javax.jdo.option.Optimistic",
868      * <BR>"javax.jdo.option.RetainValues",
869      * <BR>"javax.jdo.option.RestoreValues",
870      * <BR>"javax.jdo.option.IgnoreCache",
871      * <BR>"javax.jdo.option.NontransactionalRead",
872      * <BR>"javax.jdo.option.NontransactionalWrite",
873      * <BR>"javax.jdo.option.Multithreaded",
874      * <BR>"javax.jdo.option.ConnectionUserName",
875      * <BR>"javax.jdo.option.ConnectionPassword",
876      * <BR>"javax.jdo.option.ConnectionURL",
877      * <BR>"javax.jdo.option.ConnectionFactoryName",
878      * <BR>"javax.jdo.option.ConnectionFactory2Name",
879      * <BR>"javax.jdo.option.Mapping",
880      * <BR>"javax.jdo.mapping.Catalog",
881      * <BR>"javax.jdo.mapping.Schema",
882      * <BR>"javax.jdo.option.PersistenceUnitName".
883      * <BR>"javax.jdo.option.DetachAllOnCommit".
884      * <BR>"javax.jdo.option.CopyOnAttach".
885      * <BR>"javax.jdo.option.TransactionType".
886      * <BR>"javax.jdo.option.ServerTimeZoneID".
887      * <BR>"javax.jdo.option.Name".
888      * </code>
889      * and properties of the form
890      * <BR><code>javax.jdo.option.InstanceLifecycleListener.{listenerClass}[=[{pcClasses}]]</code>
891      * where <code>{listenerClass}</code> is the fully qualified name of a
892      * class that implements
893      * {@link javax.jdo.listener.InstanceLifecycleListener}, and
894      * <code>{pcClasses}</code> is an optional comma- or whitespace-delimited
895      * list of persistence-capable classes to be observed; the absence of a
896      * value for a property of this form means that instances of all
897      * persistence-capable classes will be observed by an instance of the given
898      * listener class.
899      * <P>JDO implementations
900      * are permitted to define key values of their own.  Any key values not
901      * recognized by the implementation must be ignored.  Key values that are
902      * recognized but not supported by an implementation must result in a
903      * <code>JDOFatalUserException</code> thrown by the method.
904      * <P>The returned <code>PersistenceManagerFactory</code> is not 
905      * configurable (the <code>set<I>XXX</I></code> methods will throw an 
906      * exception).
907      * 
908      * This method loads the properties found at <code>name</code>, if any, via
909      * <code>resourceLoader</code>, and creates a {@link
910      * PersistenceManagerFactory} with <code>pmfLoader</code>. Any
911      * exceptions thrown during resource loading will
912      * be wrapped in a {@link JDOFatalUserException}.
913      * If multiple PMFs with the requested name are found, a
914      * {@link JDOFatalUserException} is thrown.
915      * @since 2.1
916      * @param overrides a Map containing properties that override properties
917      * defined in any resources loaded according to the "name" parameter
918      * @param name interpreted as the name of the resource containing the PMF
919      * properties, the name of the PMF, or the persistence unit name, in that
920      * order; if name is null, blank or whitespace, it is interpreted as
921      * indicating the anonymous {@link PersistenceManagerFactory}.
922      * @param resourceLoader the class loader to use to load properties file
923      * resources; must be non-null if <code>name</code> is non-null or blank
924      * @param pmfLoader the class loader to use to load the 
925      * {@link PersistenceManagerFactory} or
926      * <code>javax.persistence.EntityManagerFactory</code> classes
927      * @return the {@link PersistenceManagerFactory} with properties in the
928      * given resource, with the given name, or with the given persitence unit
929      * name
930      * @see Constants#ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME
931      */
932     public static PersistenceManagerFactory getPersistenceManagerFactory(
933             Map overrides,
934             String name,
935             ClassLoader resourceLoader,
936             ClassLoader pmfLoader) {
937         if (pmfLoader == null)
938             throw new JDOFatalUserException (msg.msg (
939                 "EXC_GetPMFNullPMFLoader")); //NOI18N
940         if (resourceLoader == null) {
941             throw new JDOFatalUserException(msg.msg(
942                 "EXC_GetPMFNullPropsLoader")); //NOI18N
943         }
944 
945         Map props = null;
946         // trim spaces from name and ensure non-null
947         name = (name == null?ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME:name.trim());
948         if (!ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME.equals(name)) {
949             props = loadPropertiesFromResource(resourceLoader, name);
950         }
951 
952         if (props != null) {
953             // add the SPI property to inform the implementation that
954             // the PMF was configured by the given resource name
955             // and not via named PMF for proper deserialization
956             props.put(PROPERTY_SPI_RESOURCE_NAME, name);
957             props.remove(PROPERTY_NAME);
958             return getPersistenceManagerFactory(overrides, props, pmfLoader);
959         }
960         // props were null; try getting from jdoconfig.xml
961         props = getPropertiesFromJdoconfig(name, pmfLoader);
962         if (props != null) {
963             // inform the impl that the config came from a jdoconfig.xml
964             // element with the given name
965             props.put(PROPERTY_NAME, name);
966             props.remove(PROPERTY_SPI_RESOURCE_NAME);
967             // we have loaded a Properties, delegate to implementation
968             return getPersistenceManagerFactory(overrides, props, pmfLoader);
969         }
970         // no properties found; last try to see if name is a JPA PU name
971         if (!ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME.equals(name)) {
972             props = new Properties();
973             props.put(PROPERTY_PERSISTENCE_UNIT_NAME, name);
974             return getPersistenceManagerFactory(overrides, props, pmfLoader);
975         }
976         
977         // no PMF found; give up
978         throw new JDOFatalUserException (msg.msg (
979             "EXC_NoPMFConfigurableViaPropertiesOrXML", name)); //NOI18N
980     }
981 
982     /*** Invoke the getPersistenceManagerFactory method on the implementation.
983      * If the overrides parameter to this method is not null, the static method 
984      * with Map overrides, Map properties parameters will be invoked.
985      * If the overrides parameter to this method is null,  the static method 
986      * with Map properties parameter will be invoked.
987      * @param pmfClassName the name of the implementation factory class
988      * @param overrides a Map of overrides
989      * @param properties a Map of properties
990      * @param cl the class loader to use to load the implementation class
991      * @return the PersistenceManagerFactory
992      */
993     protected static PersistenceManagerFactory
994         invokeGetPersistenceManagerFactoryOnImplementation(
995             String pmfClassName, Map overrides, Map properties, ClassLoader cl) {
996         if (overrides != null) {
997             // overrides is not null; use getPersistenceManagerFactory(Map overrides, Map props)
998             try {
999                 Class implClass = forName(pmfClassName, true, cl);
1000                 Method m = getMethod(implClass,
1001                         "getPersistenceManagerFactory",
1002                         new Class[]{Map.class, Map.class});
1003                 return (PersistenceManagerFactory) invoke(m,
1004                         null, new Object[]{overrides, properties});
1005 
1006             } catch (ClassNotFoundException e) {
1007                 throw new JDOFatalUserException(msg.msg(
1008                         "EXC_GetPMFClassNotFound", pmfClassName), e);
1009             } catch (NoSuchMethodException e) {
1010                 throw new JDOFatalInternalException(msg.msg(
1011                         "EXC_GetPMFNoSuchMethod2", pmfClassName), e);
1012             } catch (NullPointerException e) {
1013                 throw new JDOFatalInternalException (msg.msg(
1014                     "EXC_GetPMFNullPointerException", pmfClassName), e); //NOI18N
1015             } catch (IllegalAccessException e) {
1016                 throw new JDOFatalUserException(msg.msg(
1017                         "EXC_GetPMFIllegalAccess", pmfClassName), e);
1018             } catch (ClassCastException e) {
1019                 throw new JDOFatalInternalException (msg.msg(
1020                     "EXC_GetPMFClassCastException", pmfClassName), e); //NOI18N
1021             } catch (InvocationTargetException ite) {
1022                 Throwable nested = ite.getTargetException();
1023                 if (nested instanceof JDOException) {
1024                     throw (JDOException)nested;
1025                 } else throw new JDOFatalInternalException (msg.msg(
1026                     "EXC_GetPMFUnexpectedException"), ite); //NOI18N
1027             }
1028         } else {
1029             // overrides is null; use getPersistenceManagerFactory(Map props)
1030             try {
1031                 Class implClass = forName(pmfClassName, true, cl);
1032                 Method m = getMethod(implClass,
1033                         "getPersistenceManagerFactory",
1034                         new Class[]{Map.class});
1035                 return (PersistenceManagerFactory) invoke(m,
1036                         null, new Object[]{properties});
1037             } catch (ClassNotFoundException e) {
1038                 throw new JDOFatalUserException(msg.msg(
1039                         "EXC_GetPMFClassNotFound", pmfClassName), e);
1040             } catch (NoSuchMethodException e) {
1041                 throw new JDOFatalInternalException(msg.msg(
1042                         "EXC_GetPMFNoSuchMethod2", pmfClassName), e);
1043             } catch (NullPointerException e) {
1044                 throw new JDOFatalInternalException (msg.msg(
1045                     "EXC_GetPMFNullPointerException", pmfClassName), e); //NOI18N
1046             } catch (IllegalAccessException e) {
1047                 throw new JDOFatalUserException(msg.msg(
1048                         "EXC_GetPMFIllegalAccess", pmfClassName), e);
1049             } catch (ClassCastException e) {
1050                 throw new JDOFatalInternalException (msg.msg(
1051                     "EXC_GetPMFClassCastException", pmfClassName), e); //NOI18N
1052             } catch (InvocationTargetException ite) {
1053                 Throwable nested = ite.getTargetException();
1054                 if (nested instanceof JDOException) {
1055                     throw (JDOException)nested;
1056                 } else throw new JDOFatalInternalException (msg.msg(
1057                     "EXC_GetPMFUnexpectedException"), ite); //NOI18N
1058             }
1059         }
1060     }
1061 
1062     /*** Load a Properties instance by name from the class loader.
1063      * 
1064      * @param resourceLoader the class loader from which to load the properties
1065      * @param name the name of the resource
1066      * @return a Properties instance or null if no resource is found
1067      */
1068     protected static Map loadPropertiesFromResource(
1069             ClassLoader resourceLoader, String name) {
1070         InputStream in = null;
1071         Properties props = null;
1072         // try to load resources from properties file
1073         try {
1074             in = getResourceAsStream(resourceLoader, name);
1075             if (in != null) {
1076                 // then some kind of resource was found by the given name;
1077                 // assume that it's a properties file
1078                 props = new Properties();
1079                 ((Properties) props).load(in);
1080             }
1081         } catch (IOException ioe) {
1082             throw new JDOFatalUserException(msg.msg(
1083                 "EXC_GetPMFIOExceptionRsrc", name), ioe); //NOI18N
1084         } finally {
1085             if (in != null) {
1086                 try {
1087                     in.close();
1088                 } catch (IOException ioe) {
1089                 }
1090             }
1091         }
1092         return props;
1093     }
1094 
1095     /***
1096      * @see #getNamedPMFProperties(String,ClassLoader,String)
1097      * @since 2.1
1098      */
1099     protected static Map getPropertiesFromJdoconfig(
1100             String name,
1101             ClassLoader resourceLoader) {
1102         return getNamedPMFProperties(
1103             name, resourceLoader, JDOCONFIG_RESOURCE_NAME);
1104     }
1105 
1106     /***
1107      * Find and return the named {@link PersistenceManagerFactory}'s properties,
1108      * or null if not found.
1109      * If multiple named PMF property sets with
1110      * the given name are found (including anonymous ones), throw
1111      * {@link JDOFatalUserException}.
1112      * This method is here only to facilitate testing; the parameter
1113      * "jdoconfigResourceName" in public usage should always have the value
1114      * given in the constant {@link Constants#JDOCONFIG_RESOURCE_NAME}.
1115      *
1116      * @param name The persistence unit name; null is disallowed.
1117      * @param resourceLoader The ClassLoader used to load the standard JDO
1118      * configuration file.
1119      * @param jdoconfigResourceName The name of the configuration file to read.
1120      * In public usage, this should always be the value of
1121      * {@link Constants#JDOCONFIG_RESOURCE_NAME}.
1122      * @return The named <code>PersistenceManagerFactory</code> properties if
1123      * found, null if not.
1124      * @since 2.1
1125      * @throws JDOFatalUserException if multiple named PMF property sets are
1126      * found with the given name, or any other exception is encountered.
1127      */
1128     protected static Map getNamedPMFProperties(
1129             String name,
1130             ClassLoader resourceLoader,
1131             String jdoconfigResourceName) {
1132         // key is PU name, value is Map of PU properties
1133         Map/*<String,Map>*/ propertiesByNameInAllConfigs
1134                 = new HashMap/*<String,Map>*/();
1135         try {
1136             URL firstFoundConfigURL = null;
1137 
1138             // get all JDO configurations
1139             Enumeration resources =
1140                 getResources(resourceLoader, jdoconfigResourceName);
1141 
1142             if (resources.hasMoreElements()) {
1143                 ArrayList processedResources = new ArrayList();
1144 
1145                 // get ready to parse XML
1146                 DocumentBuilderFactory factory = getDocumentBuilderFactory();
1147                 do {
1148                     URL currentConfigURL = (URL) resources.nextElement();
1149                     if (processedResources.contains(currentConfigURL)) {
1150                         continue;
1151                     }
1152                     else {
1153                         processedResources.add(currentConfigURL);
1154                     }
1155                     
1156                     Map/*<String,Map>*/ propertiesByNameInCurrentConfig =
1157                         readNamedPMFProperties(
1158                             currentConfigURL,
1159                             name,
1160                             factory);
1161 
1162                     // try to detect duplicate requested PU
1163                     if (propertiesByNameInCurrentConfig.containsKey(name)) {
1164                         // possible dup -- check for it
1165                         if (firstFoundConfigURL == null) {
1166                             firstFoundConfigURL = currentConfigURL;
1167                         }
1168                         
1169                         if (propertiesByNameInAllConfigs.containsKey(name))
1170                             throw new JDOFatalUserException (msg.msg(
1171                                 "EXC_DuplicateRequestedNamedPMFFoundInDifferentConfigs",
1172                                 "".equals(name)
1173                                         ? "(anonymous)"
1174                                         : name,
1175                                 firstFoundConfigURL.toExternalForm(),
1176                                 currentConfigURL.toExternalForm())); //NOI18N
1177                     }
1178                     // no dups -- add found PUs to all PUs and keep going
1179                     propertiesByNameInAllConfigs
1180                         .putAll(propertiesByNameInCurrentConfig);
1181                 } while (resources.hasMoreElements());
1182             }
1183         }
1184         catch (FactoryConfigurationError e) {
1185             throw new JDOFatalUserException(
1186                 msg.msg("ERR_NoDocumentBuilderFactory"), e);
1187         }
1188         catch (IOException ioe) {
1189             throw new JDOFatalUserException (msg.msg (
1190                 "EXC_GetPMFIOExceptionRsrc", name), ioe); //NOI18N
1191         }
1192 
1193         // done with reading all config resources;
1194         // return what we found, which may very well be null
1195         return (Map) propertiesByNameInAllConfigs.get(name);
1196     }
1197 
1198 
1199     protected static DocumentBuilderFactory getDocumentBuilderFactory() {
1200         DocumentBuilderFactory factory =
1201                 implHelper.getRegisteredDocumentBuilderFactory();
1202         if (factory == null) {
1203             factory = getDefaultDocumentBuilderFactory();
1204         }
1205         return factory;
1206     }
1207     
1208     protected static DocumentBuilderFactory getDefaultDocumentBuilderFactory() {
1209         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
1210         factory.setIgnoringComments(true);
1211         factory.setNamespaceAware(true);
1212         factory.setValidating(false);
1213         factory.setIgnoringElementContentWhitespace(true);
1214         factory.setExpandEntityReferences(true);
1215 
1216         return factory;
1217     }
1218 
1219     protected static ErrorHandler getErrorHandler() {
1220         ErrorHandler handler = implHelper.getRegisteredErrorHandler();
1221         if (handler == null) {
1222             handler = getDefaultErrorHandler();
1223         }
1224         return handler;
1225     }
1226     
1227     protected static ErrorHandler getDefaultErrorHandler() {
1228         return new ErrorHandler() {
1229                 public void error(SAXParseException exception)
1230                         throws SAXException {
1231                     throw exception;
1232                 }
1233 
1234                 public void fatalError(SAXParseException exception)
1235                         throws SAXException {
1236                     throw exception;
1237                 }
1238 
1239                 public void warning(SAXParseException exception)
1240                         throws SAXException {
1241                     // gulp:  ignore warnings
1242                 }
1243             };
1244     }
1245 
1246 
1247     /*** Reads JDO configuration file, creates a Map for each
1248      * persistence-manager-factory, then returns the map.
1249      * @param url URL of a JDO configuration file compliant with
1250      * javax/jdo/jdoconfig.xsd.
1251      * @param requestedPMFName The name of the requested
1252      * persistence unit (allows for fail-fast).
1253      * @param factory The <code>DocumentBuilderFactory</code> to use for XML
1254      * parsing.
1255      * @return a Map<String,Map> holding persistence unit configurations; for
1256      * the anonymous persistence unit, the
1257      * value of the String key is the empty string, "".
1258      */
1259     protected static Map/*<String,Map>*/ readNamedPMFProperties(
1260             URL url,
1261             String requestedPMFName,
1262             DocumentBuilderFactory factory) {
1263         requestedPMFName = requestedPMFName == null
1264             ? ""
1265             : requestedPMFName.trim();
1266 
1267         Map propertiesByName = new HashMap();
1268         InputStream in = null;
1269         try {
1270             DocumentBuilder builder = factory.newDocumentBuilder();
1271             builder.setErrorHandler(getErrorHandler());
1272 
1273             in = openStream(url);
1274             Document doc = builder.parse(in);
1275 
1276             Element root = doc.getDocumentElement();
1277             if (root == null) {
1278                 throw new JDOFatalUserException(
1279                     msg.msg("EXC_InvalidJDOConfigNoRoot", url.toExternalForm())
1280                 );
1281             }
1282 
1283             NodeList pmfs = root.getElementsByTagName(
1284                 ELEMENT_PERSISTENCE_MANAGER_FACTORY);
1285 
1286             for(int i = 0; i < pmfs.getLength(); i++) {
1287                 Node pmfElement = pmfs.item(i);
1288 
1289                 Properties pmfPropertiesFromAttributes
1290                     = readPropertiesFromPMFElementAttributes(pmfElement);
1291 
1292                 Properties pmfPropertiesFromElements
1293                     = readPropertiesFromPMFSubelements(pmfElement, url);
1294 
1295                 // for informative error handling, get name (or names) now
1296                 String pmfNameFromAtts =
1297                     pmfPropertiesFromAttributes.getProperty(PROPERTY_NAME);
1298                 String pmfNameFromElem =
1299                     pmfPropertiesFromElements.getProperty(PROPERTY_NAME);
1300 
1301                 String pmfName = null;
1302                 if (isNullOrBlank(pmfNameFromAtts)) {
1303                     // no PMF name attribute given
1304                     if (!isNullOrBlank(pmfNameFromElem)) {
1305                         // PMF name element was given
1306                         pmfName = pmfNameFromElem;
1307                     }
1308                     else  {
1309                         // PMF name not given at all, means the "anonymous" PMF
1310                         pmfName = ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME;
1311                     }
1312                 }
1313                 else {
1314                     // PMF name given in an attribute
1315                     if (!isNullOrBlank(pmfNameFromElem)) {
1316                         // exception -- PMF name given as both att & elem
1317                         throw new JDOFatalUserException(
1318                             msg.msg(
1319                                 "EXC_DuplicatePMFNamePropertyFoundWithinConfig",
1320                                 pmfNameFromAtts,
1321                                 pmfNameFromElem,
1322                                 url.toExternalForm()));
1323                     }
1324                     pmfName = pmfNameFromAtts;
1325                 }
1326                 pmfName = pmfName == null ? "" : pmfName.trim();
1327 
1328                 // check for duplicate properties among atts & elems
1329                 if (requestedPMFName.equals(pmfName)) {
1330                     Iterator it =
1331                         pmfPropertiesFromAttributes.keySet().iterator();
1332                     while (it.hasNext()) {
1333                         String property = (String) it.next();
1334                         if (pmfPropertiesFromElements.contains(property)) {
1335                             throw new JDOFatalUserException(
1336                                 msg.msg(
1337                                     "EXC_DuplicatePropertyFound",
1338                                     property,
1339                                     pmfName,
1340                                     url.toExternalForm()));
1341                         }
1342                     }
1343                 }
1344                 
1345                 // at this point, we're guaranteed not to have duplicate
1346                 // properties -- merge them
1347                 Properties pmfProps = new Properties();
1348                 pmfProps.putAll(pmfPropertiesFromAttributes);
1349                 pmfProps.putAll(pmfPropertiesFromElements);
1350 
1351                 // check for duplicate requested PMF name
1352                 if (pmfName.equals(requestedPMFName)
1353                     && propertiesByName.containsKey(pmfName)) {
1354 
1355                     throw new JDOFatalUserException(msg.msg(
1356                             "EXC_DuplicateRequestedNamedPMFFoundInSameConfig",
1357                         pmfName,
1358                         url.toExternalForm()));
1359                 }
1360                 propertiesByName.put(pmfName, pmfProps);
1361             }
1362             return propertiesByName;
1363         }
1364         catch (IOException ioe) {
1365             throw new JDOFatalUserException(
1366                 msg.msg("EXC_GetPMFIOExceptionRsrc", url.toString()),
1367                 ioe); //NOI18N
1368         }
1369         catch (ParserConfigurationException e) {
1370             throw new JDOFatalInternalException(
1371                 msg.msg("EXC_ParserConfigException"),
1372                 e);
1373         }
1374         catch (SAXParseException e) {
1375             throw new JDOFatalUserException(
1376                 msg.msg(
1377                     "EXC_SAXParseException",
1378                     url.toExternalForm(),
1379                     new Integer(e.getLineNumber()),
1380                     new Integer(e.getColumnNumber())),
1381                 e);
1382         }
1383         catch (SAXException e) {
1384             throw new JDOFatalUserException(
1385                 msg.msg("EXC_SAXException", url.toExternalForm()),
1386                 e);
1387         }
1388         catch (JDOException e) {
1389             throw e;
1390         }
1391         catch (RuntimeException e) {
1392             throw new JDOFatalUserException(
1393                 msg.msg("EXC_SAXException", url.toExternalForm()),
1394                 e);
1395         }
1396         finally {
1397             if (in != null) {
1398                 try {
1399                     in.close();
1400                 }
1401                 catch (IOException ioe) { /* gulp */ }
1402             }
1403         }
1404     }
1405 
1406     protected static Properties readPropertiesFromPMFElementAttributes(
1407         Node pmfElement) {
1408         Properties p = new Properties();
1409         NamedNodeMap attributes = pmfElement.getAttributes();
1410         if (attributes == null) {
1411             return p;
1412         }
1413 
1414         for(int i = 0; i < attributes.getLength(); i++) {
1415             Node att = attributes.item(i);
1416             String attName = att.getNodeName();
1417             String attValue = att.getNodeValue().trim();
1418 
1419             String jdoPropertyName =
1420                 (String) ATTRIBUTE_PROPERTY_XREF.get(attName);
1421 
1422             p.put(
1423                 jdoPropertyName != null
1424                     ? jdoPropertyName
1425                     : attName,
1426                 attValue);
1427         }
1428 
1429         return p;
1430     }
1431 
1432     protected static Properties readPropertiesFromPMFSubelements(
1433         Node pmfElement, URL url) {
1434         Properties p = new Properties();
1435         NodeList elements = pmfElement.getChildNodes();
1436         if (elements == null) {
1437             return p;
1438         }
1439         for(int i = 0; i < elements.getLength(); i++) {
1440             Node element = elements.item(i);
1441             if (element.getNodeType() != Node.ELEMENT_NODE) {
1442                 continue;
1443             }
1444             
1445             String elementName = element.getNodeName();
1446             NamedNodeMap attributes = element.getAttributes();
1447             if (ELEMENT_PROPERTY.equalsIgnoreCase(elementName)) {
1448                 // <property name="..." value="..."/>
1449 
1450                 // get the "name" attribute's value (required)
1451                 Node nameAtt = attributes.getNamedItem(PROPERTY_ATTRIBUTE_NAME);
1452                 if (nameAtt == null) {
1453                     throw new JDOFatalUserException(
1454                         msg.msg("EXC_PropertyElementHasNoNameAttribute", url));
1455                 }
1456                 String name = nameAtt.getNodeValue().trim();
1457                 if ("".equals(name)) {
1458                     throw new JDOFatalUserException(
1459                         msg.msg(
1460                             "EXC_PropertyElementNameAttributeHasNoValue",
1461                             name,
1462                             url));
1463                 }
1464                 // The next call allows users to use either the
1465                 // <persistence-manager-factory> attribute names or the
1466                 // "javax.jdo" property names in <property> element "name"
1467                 // attributes.  Handy-dandy.
1468                 String jdoPropertyName =
1469                     (String) ATTRIBUTE_PROPERTY_XREF.get(name);
1470                 
1471                 String propertyName = jdoPropertyName != null
1472                         ? jdoPropertyName
1473                         : name;
1474 
1475                 if (p.containsKey(propertyName)) {
1476                     throw new JDOFatalUserException(
1477                         msg.msg(
1478                             "EXC_DuplicatePropertyNameGivenInPropertyElement",
1479                             propertyName,
1480                             url));
1481                 }
1482 
1483                 // get the "value" attribute's value (optional)
1484                 Node valueAtt = attributes.getNamedItem(
1485                     PROPERTY_ATTRIBUTE_VALUE);
1486                 String value = valueAtt == null
1487                     ? null
1488                     : valueAtt.getNodeValue().trim();
1489 
1490                 p.put(propertyName, value);
1491             }
1492             else if (ELEMENT_INSTANCE_LIFECYCLE_LISTENER.equals(elementName)) {
1493                 // <instance-lifecycle-listener listener="..." classes="..."/>
1494 
1495                 // get the "listener" attribute's value
1496                 Node listenerAtt = attributes.getNamedItem(
1497                     INSTANCE_LIFECYCLE_LISTENER_ATTRIBUTE_LISTENER);
1498                 if (listenerAtt == null) {
1499                     throw new JDOFatalUserException(
1500                         msg.msg(
1501                             "EXC_MissingListenerAttribute",
1502                             url));
1503                 }
1504                 String listener = listenerAtt.getNodeValue().trim();
1505                 if ("".equals(listener)) {
1506                     throw new JDOFatalUserException(
1507                         msg.msg(
1508                             "EXC_MissingListenerAttributeValue",
1509                             url));
1510                 }
1511 
1512                 // listener properties are of the form
1513                 // "javax.jdo.option.InstanceLifecycleListener." + listener
1514                 listener =
1515                     PROPERTY_PREFIX_INSTANCE_LIFECYCLE_LISTENER + listener;
1516 
1517                 // get the "classes" attribute's value (optional)
1518                 Node classesAtt = attributes.getNamedItem(
1519                     INSTANCE_LIFECYCLE_LISTENER_ATTRIBUTE_CLASSES);
1520                 String value = classesAtt == null
1521                     ? ""
1522                     : classesAtt.getNodeValue().trim();
1523 
1524                 p.put(listener,  value);
1525             }
1526         }
1527         return p;
1528     }
1529 
1530     protected static boolean isNullOrBlank(String s) {
1531         return s == null || "".equals(s.trim());
1532     }
1533     
1534     /***
1535      * Returns a {@link PersistenceManagerFactory} configured based
1536      * on the properties stored in the file at
1537      * <code>propsFile</code>. This method is equivalent to
1538      * invoking {@link
1539      * #getPersistenceManagerFactory(File,ClassLoader)} with
1540      * <code>Thread.currentThread().getContextClassLoader()</code> as
1541      * the <code>loader</code> argument.
1542      * @since 2.0
1543      * @param propsFile the file containing the Properties
1544      * @return the PersistenceManagerFactory
1545      */
1546     public static PersistenceManagerFactory getPersistenceManagerFactory
1547             (File propsFile) {
1548         return getPersistenceManagerFactory(
1549                 propsFile, getContextClassLoader());
1550     }
1551 
1552     /***
1553      * Returns a {@link PersistenceManagerFactory} configured based
1554      * on the properties stored in the file at
1555      * <code>propsFile</code>. Creates a {@link
1556      * PersistenceManagerFactory} with <code>loader</code>. Any
1557      * <code>IOException</code>s or
1558      * <code>FileNotFoundException</code>s thrown during resource
1559      * loading will be wrapped in a {@link JDOFatalUserException}.
1560      * @since 2.0
1561      * @param propsFile the file containing the Properties
1562      * @param loader the class loader to use to load the 
1563      * <code>PersistenceManagerFactory</code> class
1564      * @return the PersistenceManagerFactory
1565      */
1566     public static PersistenceManagerFactory getPersistenceManagerFactory
1567             (File propsFile, ClassLoader loader) {
1568         if (propsFile == null)
1569             throw new JDOFatalUserException (msg.msg (
1570                 "EXC_GetPMFNullFile")); //NOI18N
1571 
1572         Properties props = new Properties();
1573         InputStream in = null;
1574         try {
1575             in = new FileInputStream(propsFile);
1576             return getPersistenceManagerFactory(in, loader);
1577         } catch (FileNotFoundException fnfe) {
1578             throw new JDOFatalUserException (msg.msg (
1579                 "EXC_GetPMFNoFile", propsFile), fnfe); //NOI18N
1580         } finally {
1581             if (in != null)
1582                 try { 
1583                     in.close (); 
1584                 } catch (IOException ioe) { }
1585         }
1586     }
1587 
1588     /***
1589      * Returns a {@link PersistenceManagerFactory} at the JNDI
1590      * location specified by <code>jndiLocation</code> in the context
1591      * <code>context</code>. If <code>context</code> is
1592      * <code>null</code>, <code>new InitialContext()</code> will be
1593      * used. This method is equivalent to invoking {@link
1594      * #getPersistenceManagerFactory(String,Context,ClassLoader)}
1595      * with <code>Thread.currentThread().getContextClassLoader()</code> as
1596      * the <code>loader</code> argument.
1597      * @since 2.0
1598      * @param jndiLocation the JNDI location containing the 
1599      * PersistenceManagerFactory
1600      * @param context the context in which to find the named
1601      * PersistenceManagerFactory
1602      * @return the PersistenceManagerFactory
1603      */
1604     public static PersistenceManagerFactory getPersistenceManagerFactory
1605             (String jndiLocation, Context context) {
1606         return getPersistenceManagerFactory (jndiLocation, context,
1607             getContextClassLoader());
1608     }
1609 
1610 
1611     /***
1612      * Returns a {@link PersistenceManagerFactory} at the JNDI
1613      * location specified by <code>jndiLocation</code> in the context
1614      * <code>context</code>. If <code>context</code> is
1615      * <code>null</code>, <code>new InitialContext()</code> will be
1616      * used. Creates a {@link PersistenceManagerFactory} with
1617      * <code>loader</code>. Any <code>NamingException</code>s thrown
1618      * will be wrapped in a {@link JDOFatalUserException}.
1619      * @since 2.0
1620      * @param jndiLocation the JNDI location containing the 
1621      * PersistenceManagerFactory
1622      * @param context the context in which to find the named 
1623      * PersistenceManagerFactory
1624      * @param loader the class loader to use to load the 
1625      * <code>PersistenceManagerFactory</code> class
1626      * @return the PersistenceManagerFactory
1627      */
1628     public static PersistenceManagerFactory getPersistenceManagerFactory
1629             (String jndiLocation, Context context, ClassLoader loader) {
1630         if (jndiLocation == null)
1631             throw new JDOFatalUserException (msg.msg (
1632                 "EXC_GetPMFNullJndiLoc")); //NOI18N
1633         if (loader == null)
1634             throw new JDOFatalUserException (msg.msg (
1635                 "EXC_GetPMFNullLoader")); //NOI18N
1636         try {
1637             if (context == null)
1638                 context = new InitialContext ();
1639 
1640             Object o = context.lookup (jndiLocation);
1641             return (PersistenceManagerFactory) PortableRemoteObject.narrow
1642                 (o, PersistenceManagerFactory.class);
1643         } catch (NamingException ne) {
1644             throw new JDOFatalUserException (msg.msg (
1645                 "EXC_GetPMFNamingException", jndiLocation, loader), ne); //NOI18N
1646         }
1647     }
1648     
1649     /***
1650      * Returns a {@link PersistenceManagerFactory} configured based
1651      * on the Properties stored in the input stream at
1652      * <code>stream</code>. This method is equivalent to
1653      * invoking {@link
1654      * #getPersistenceManagerFactory(InputStream,ClassLoader)} with
1655      * <code>Thread.currentThread().getContextClassLoader()</code> as
1656      * the <code>loader</code> argument.
1657      * @since 2.0
1658      * @param stream the stream containing the Properties
1659      * @return the PersistenceManagerFactory
1660      */
1661     public static PersistenceManagerFactory getPersistenceManagerFactory
1662             (InputStream stream) {
1663         return getPersistenceManagerFactory(
1664                 stream, getContextClassLoader());
1665     }
1666 
1667     /***
1668      * Returns a {@link PersistenceManagerFactory} configured based
1669      * on the Properties stored in the input stream at
1670      * <code>stream</code>. Creates a {@link
1671      * PersistenceManagerFactory} with <code>loader</code>. Any
1672      * <code>IOException</code>s thrown during resource
1673      * loading will be wrapped in a {@link JDOFatalUserException}.
1674      * @since 2.0
1675      * @param stream the stream containing the Properties
1676      * @param loader the class loader to use to load the 
1677      * <code>PersistenceManagerFactory</code> class
1678      * @return the PersistenceManagerFactory
1679      */
1680     public static PersistenceManagerFactory getPersistenceManagerFactory
1681             (InputStream stream, ClassLoader loader) {
1682         if (stream == null)
1683             throw new JDOFatalUserException (msg.msg (
1684                 "EXC_GetPMFNullStream")); //NOI18N
1685 
1686         Properties props = new Properties ();
1687         try {
1688             props.load (stream);
1689         } catch (IOException ioe) {
1690             throw new JDOFatalUserException
1691                 (msg.msg ("EXC_GetPMFIOExceptionStream"), ioe); //NOI18N
1692         }
1693         return getPersistenceManagerFactory (props, loader);
1694     }
1695 
1696     /*** Get the context class loader associated with the current thread. 
1697      * This is done in a doPrivileged block because it is a secure method.
1698      * @return the current thread's context class loader.
1699      * @since 2.0
1700      */
1701     private static ClassLoader getContextClassLoader() {
1702         return (ClassLoader)AccessController.doPrivileged(
1703             new PrivilegedAction () {
1704                 public Object run () {
1705                     return Thread.currentThread().getContextClassLoader();
1706                 }
1707             }
1708         );
1709     }
1710 
1711     /*** Get the named resource as a stream from the resource loader.
1712      * Perform this operation in a doPrivileged block.
1713      */
1714     private static InputStream getResourceAsStream(
1715             final ClassLoader resourceLoader, final String name) {
1716         return (InputStream)AccessController.doPrivileged(
1717             new PrivilegedAction() {
1718                 public Object run() {
1719                     return resourceLoader.getResourceAsStream(name);
1720                 }
1721             }
1722         );
1723     }
1724 
1725 
1726     /*** Get the named Method from the named class. 
1727      * Perform this operation in a doPrivileged block.
1728      * 
1729      * @param implClass the class
1730      * @param methodName the name of the method
1731      * @param parameterTypes the parameter types of the method
1732      * @return the Method instance
1733      */
1734     private static Method getMethod(
1735             final Class implClass, 
1736             final String methodName, 
1737             final Class[] parameterTypes) 
1738                 throws NoSuchMethodException {
1739         try {
1740             return (Method) AccessController.doPrivileged(
1741                 new PrivilegedExceptionAction() {
1742                     public Object run() throws NoSuchMethodException {
1743                         return implClass.getMethod(methodName, parameterTypes);
1744                     }
1745                 }
1746             );
1747         } catch (PrivilegedActionException ex) {
1748             throw (NoSuchMethodException)ex.getException();
1749         }
1750     }
1751 
1752     /*** Invoke the method.
1753      * Perform this operation in a doPrivileged block.
1754      */
1755     private static Object invoke(final Method method,
1756             final Object instance, final Object[] parameters) 
1757                 throws IllegalAccessException, InvocationTargetException {
1758         try {
1759             return (Object) AccessController.doPrivileged(
1760                 new PrivilegedExceptionAction() {
1761                     public Object run() 
1762                         throws IllegalAccessException, 
1763                             InvocationTargetException {
1764                         return method.invoke (instance, parameters);
1765                     }
1766                 }
1767             );
1768         } catch (PrivilegedActionException ex) {
1769             Exception cause = (Exception)ex.getException();
1770             if (cause instanceof IllegalAccessException)
1771                 throw (IllegalAccessException)cause;
1772             else //if (cause instanceof InvocationTargetException)
1773                 throw (InvocationTargetException)cause;
1774         }
1775     }
1776 
1777     /*** Get resources of the resource loader. 
1778      * Perform this operation in a doPrivileged block.
1779      * @param resourceLoader
1780      * @param resourceName
1781      * @return the resources
1782      */
1783     protected static Enumeration getResources(
1784             final ClassLoader resourceLoader, 
1785             final String resourceName) 
1786                 throws IOException {
1787         try {
1788             return (Enumeration) AccessController.doPrivileged(
1789                 new PrivilegedExceptionAction() {
1790                     public Object run() throws IOException {
1791                         return resourceLoader.getResources(resourceName);
1792                     }
1793                 }
1794             );
1795         } catch (PrivilegedActionException ex) {
1796             throw (IOException)ex.getException();
1797         }
1798     }
1799 
1800     /*** Get the named class.
1801      * Perform this operation in a doPrivileged block.
1802      * 
1803      * @param name the name of the class
1804      * @param init whether to initialize the class
1805      * @param loader which class loader to use
1806      * @return the class
1807      */
1808     private static Class forName(
1809             final String name, 
1810             final boolean init, 
1811             final ClassLoader loader) 
1812                 throws ClassNotFoundException {
1813         try {
1814             return (Class) AccessController.doPrivileged(
1815                 new PrivilegedExceptionAction() {
1816                     public Object run() throws ClassNotFoundException {
1817                         return Class.forName(name, init, loader);
1818                     }
1819                 }
1820             );
1821         } catch (PrivilegedActionException ex) {
1822             throw (ClassNotFoundException)ex.getException();
1823         }
1824     }
1825 
1826     /*** Open an input stream on the url.
1827      * Perform this operation in a doPrivileged block.
1828      * 
1829      * @param url
1830      * @return the input stream
1831      */
1832     private static InputStream openStream(final URL url) 
1833             throws IOException {
1834         try {
1835             return (InputStream) AccessController.doPrivileged(
1836                 new PrivilegedExceptionAction() {
1837                     public Object run() throws IOException {
1838                         return url.openStream();
1839                     }
1840                 }
1841             );
1842         } catch (PrivilegedActionException ex) {
1843             throw (IOException)ex.getException();
1844         }
1845     }
1846 
1847 }