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