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