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