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  package javax.jdo;
19  
20  import junit.framework.TestSuite;
21  
22  import javax.jdo.util.AbstractTest;
23  import javax.jdo.util.BatchTestRunner;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.net.URL;
27  import java.net.URLClassLoader;
28  import java.util.Enumeration;
29  import java.util.HashMap;
30  import java.util.Iterator;
31  import java.util.Map;
32  import java.util.Random;
33  
34  /**
35   * 
36   * Tests class javax.jdo.JDOHelper for META-INF/jdoconfig.xml compliance.
37   * 
38   */
39  public class JDOHelperConfigTest extends AbstractTest implements Constants {
40  
41      public static void main(String args[]) {
42          BatchTestRunner.run(JDOHelperConfigTest.class);
43      }
44  
45      /**
46       * {@inheritDoc}
47       * @return {@inheritDoc}
48       */
49      public static TestSuite suite() {
50          return new TestSuite(JDOHelperConfigTest.class);
51      }
52  
53      /**
54       * A class path prefix used in the various tests where the class path
55       * needs to be set.
56       */
57      protected static String JDOCONFIG_CLASSPATH_PREFIX
58              = initJDOConfigClasspathPrefix();
59  
60      /**
61       * Returns the JDO configuration class path prefix's default value, which is
62       * the project base directory suffixed by the path to the configuration
63       * directory (<tt>test/schema/jdoconfig</tt>).
64       * 
65       * @return the default class path prefix used by this test suite.
66       * 
67       */
68      protected static String initJDOConfigClasspathPrefix() {
69          return initBasedir() + "test/schema/jdoconfig";
70      }
71  
72      /**
73       * The class path used to specify the location of test class files.
74       * @return the class path where test class files can be found.
75       */
76      protected static String TEST_CLASSPATH =
77              initTestClasspath();
78  
79      /**
80       * Returns the default class path for JDO test class files 
81       * (<tt>target/test-classes/</tt>).
82       * @return the default class path for JDO test class files.
83       */
84      protected static String initTestClasspath() {
85          return initBasedir() + "target/test-classes/";
86      }
87  
88      /**
89       * The class path used to locate the JDO API class files.
90       */
91      protected static String API_CLASSPATH =
92              initAPIClasspath();
93  
94      /**
95       * Returns the default class path for JDO API class files
96       * (<tt>target/classes/</tt>).
97       * @return the default class path for JDO API class files.
98       */
99      protected static String initAPIClasspath() {
100         return initBasedir() + "target/classes/";
101     }
102 
103     /**
104      * Returns the base directory for this project.  This base directory
105      * is used to build up the other class paths defined in this test suite.
106      * The value returned is the value returned by 
107      * <code>System.getProperty("basedir")</code>.
108      * A trailing slash is appended to the path if it doesn't exist.
109      * 
110      * @return the default base directory of the project.
111      */
112     protected static String initBasedir() {
113         String basedir = System.getProperty("basedir");
114         if (basedir != null) {
115             if (!basedir.endsWith("/")) {
116                 basedir += "/";
117             }
118         } else {
119             basedir = "";
120         }
121         return basedir;
122     }
123     
124     /**
125      * A radomizer seeded with the system clock's current time.
126      */
127     protected static Random RANDOM = new Random(System.currentTimeMillis());
128 
129     /**
130      * Builds up a {@link java.util.Map Map} object that contains key 
131      * parameter values specific to a named test.  All of the properties
132      * needed to run a particular test are loaded into this object.
133      * @param testVariant the name of the test to include in the 
134      *        {@link java.util.Map Map} values.
135      * @param listenerCount the number of life cycle listener class names to 
136      *        add to this map.  The listener names will begin with the value
137      *        stored in {@link 
138      *        javax.jdo.Constants.PROPERTY_INSTANCE_LIFECYCLE_LISTENER
139      *        PROPERTY_INSTANCE_LIFECYCLE_LISTENER}.
140      * @param vendorSpecificPropertyCount  the number of properties named of
141      *        the form <pre>"property." + testVariant + ".name"</pre> that
142      *        are added to the map.
143      * @param excludeName if true the property specified by 
144      *        {@link javax.jdo.Constants.PROPERTY_NAME PROPERTY_NAME} is
145      *        not added to the map.
146      * @param excludePUName if true the property specified by 
147      *        {@link javax.jdo.Constants.PROPERTY_PERSISTENCE_UNIT_NAME
148      *        PROPERTY_PERSISTENCE_UNIT_NAME} is not added to the map.
149      * @return a new {@link java.util.Map Map} object populated with properties
150      *         that can be used in this test suite.
151      */
152     protected Map prepareInitialExpectedMap(
153             String testVariant,
154             int listenerCount,
155             int vendorSpecificPropertyCount,
156             boolean excludeName,
157             boolean excludePUName
158     ) {
159         Map<String,String> expected = new HashMap<String,String>();
160 
161         if (!excludeName) {
162             expected.put(
163                     PROPERTY_NAME,
164                     PMF_ATTRIBUTE_NAME + "." + testVariant);
165         }
166         if (!excludePUName) {
167             expected.put(
168                     PROPERTY_PERSISTENCE_UNIT_NAME,
169                     PMF_ATTRIBUTE_PERSISTENCE_UNIT_NAME + "." + testVariant);
170         }
171 
172         expected.put(PROPERTY_PERSISTENCE_MANAGER_FACTORY_CLASS,
173                 PMF_ATTRIBUTE_CLASS + "." + testVariant);
174         expected.put(
175                 PROPERTY_CONNECTION_DRIVER_NAME,
176                 PMF_ATTRIBUTE_CONNECTION_DRIVER_NAME + "." + testVariant);
177         expected.put(
178                 PROPERTY_CONNECTION_FACTORY_NAME,
179                 PMF_ATTRIBUTE_CONNECTION_FACTORY_NAME + "." + testVariant);
180         expected.put(
181                 PROPERTY_CONNECTION_FACTORY2_NAME,
182                 PMF_ATTRIBUTE_CONNECTION_FACTORY2_NAME + "." + testVariant);
183         expected.put(
184                 PROPERTY_CONNECTION_PASSWORD,
185                 PMF_ATTRIBUTE_CONNECTION_PASSWORD + "." + testVariant);
186         expected.put(
187                 PROPERTY_CONNECTION_URL,
188                 PMF_ATTRIBUTE_CONNECTION_URL + "." + testVariant);
189         expected.put(
190                 PROPERTY_CONNECTION_USER_NAME,
191                 PMF_ATTRIBUTE_CONNECTION_USER_NAME + "." + testVariant);
192         expected.put(
193                 PROPERTY_IGNORE_CACHE,
194                 PMF_ATTRIBUTE_IGNORE_CACHE + "." + testVariant);
195         expected.put(
196                 PROPERTY_MAPPING,
197                 PMF_ATTRIBUTE_MAPPING + "." + testVariant);
198         expected.put(
199                 PROPERTY_MULTITHREADED,
200                 PMF_ATTRIBUTE_MULTITHREADED + "." + testVariant);
201         expected.put(
202                 PROPERTY_NONTRANSACTIONAL_READ,
203                 PMF_ATTRIBUTE_NONTRANSACTIONAL_READ + "." + testVariant);
204         expected.put(
205                 PROPERTY_NONTRANSACTIONAL_WRITE,
206                 PMF_ATTRIBUTE_NONTRANSACTIONAL_WRITE + "." + testVariant);
207         expected.put(
208                 PROPERTY_OPTIMISTIC,
209                 PMF_ATTRIBUTE_OPTIMISTIC + "." + testVariant);
210         expected.put(
211                 PROPERTY_RESTORE_VALUES,
212                 PMF_ATTRIBUTE_RESTORE_VALUES + "." + testVariant);
213         expected.put(
214                 PROPERTY_RETAIN_VALUES,
215                 PMF_ATTRIBUTE_RETAIN_VALUES + "." + testVariant);
216         expected.put(
217                 PROPERTY_DETACH_ALL_ON_COMMIT,
218                 PMF_ATTRIBUTE_DETACH_ALL_ON_COMMIT + "." + testVariant);
219         expected.put(
220                 PROPERTY_SERVER_TIME_ZONE_ID,
221                 PMF_ATTRIBUTE_SERVER_TIME_ZONE_ID + "." + testVariant);
222 
223         // listeners
224         for (int i = 0; i < listenerCount; i++) {
225             expected.put(
226                     PROPERTY_PREFIX_INSTANCE_LIFECYCLE_LISTENER +
227                             "listener." + testVariant + ".listener" + i,
228                     "classes." + testVariant + ".classes" + i
229             );
230         }
231 
232         // vendor-specific properties
233         for (int i = 0; i < vendorSpecificPropertyCount; i++) {
234             expected.put(
235                     "property." + testVariant + ".name" + i,
236                     "property." + testVariant + ".value" + i
237             );
238         }
239 
240         return expected;
241     }
242 
243     /**
244      * Fails the test if the number of properties in the two specified
245      * {@link java.util.Map Map} objects are not identical or their values
246      * do not match.
247      * @param expected the first {@link java.util.Map Map} object to test.
248      * @param actual the second {@link java.util.Map Map} object to test.
249      */
250     static void assertEqualProperties(Map expected, Map actual) {
251         Iterator i = expected.entrySet().iterator();
252         while (i.hasNext()) {
253             Map.Entry entry = (Map.Entry) i.next();
254             String key = (String) entry.getKey();
255             String expectedValue = (String) entry.getValue();
256             String actualValue = (String) actual.get(key);
257 
258             assertEquals(
259                     "Actual property at key [" + key + "] with value [" +
260                             actualValue + "] not equal to expected value [" +
261                             expectedValue + "]",
262                     expectedValue,
263                     actualValue);
264         }
265     }
266 
267     /**
268      * Performs a test specified by <tt>testVariantName</tt>, by building
269      * up a property map and executing the test according to the property 
270      * values.  With this version of <tt>doPositiveTest</tt> the property
271      * name ({@link javax.jdo.Constants.PROPERTY_NAME PROPERTY_NAME}) and 
272      * the {@link javax.jdo.Constants.PERSISTENCE_UNIT_NAME 
273      * PERSISTENCE_UNIT_NAME}) are included in the property map that is used
274      * to run the test.
275      * 
276      * @param classpaths class paths to add to the class loader that runs the 
277      *        test that specify where <tt>jdoconfig.xml</tt> can be found.
278      * @param testVariantName the name of the test.
279      * @param listenerCount number of listeners utilized in the test.
280      * @param vendorSpecificPropertyCount number of vendor properties used
281      *        in the test.
282      * @param checkEqualProperties if true the test's properties are tested.
283      * @throws java.io.IOException if an {@java.io.IOException IOException}
284      *         occurs during class loading or any part of the test.
285      */
286     protected void doPositiveTest(
287             String[] classpaths,
288             String testVariantName,
289             int listenerCount,
290             int vendorSpecificPropertyCount,
291             boolean checkEqualProperties)
292             throws IOException {
293 
294         doPositiveTest(
295                 classpaths,
296                 testVariantName,
297                 listenerCount,
298                 vendorSpecificPropertyCount,
299                 checkEqualProperties,
300                 false,
301                 false);
302     }
303 
304     /**
305      * Performs a test specified by <tt>testVariantName</tt>, by building
306      * up a property map and executing the test according to the property 
307      * values.  An assertion exeception is thrown if the test being run 
308      * has a negative (non-true) result. With this version of 
309      * <tt>doPositiveTest</tt> the property
310      * name ({@link javax.jdo.Constants.PROPERTY_NAME PROPERTY_NAME}) and 
311      * the {@link javax.jdo.Constants.PERSISTENCE_UNIT_NAME 
312      * PERSISTENCE_UNIT_NAME}) are included in the property map that is used
313      * to run the test.
314      * 
315      * @param classpaths class paths to add to the class loader that runs the 
316      *        test that specify where <tt>jdoconfig.xml</tt> can be found.
317      * @param testVariantName the name of the test.
318      * @param listenerCount number of listeners utilized in the test.
319      * @param vendorSpecificPropertyCount number of vendor properties used
320      *        in the test.
321      * @param checkEqualProperties if true the test's properties are tested.
322      * @param excludeName if true the property specified by 
323      *        {@link javax.jdo.Constants.PROPERTY_NAME PROPERTY_NAME} is
324      *        not added to the map.
325      * @param excludePUName if true the property specified by 
326      *        {@link javax.jdo.Constants.PROPERTY_PERSISTENCE_UNIT_NAME
327      *        PROPERTY_PERSISTENCE_UNIT_NAME} is not added to the map.
328      * @throws java.io.IOException if an {@java.io.IOException IOException}
329      *         occurs during class loading or any part of the test.
330      */
331     protected void doPositiveTest(
332             String[] classpaths,
333             String testVariantName,
334             int listenerCount,
335             int vendorSpecificPropertyCount,
336             boolean checkEqualProperties,
337             boolean excludeName,
338             boolean excludePUName)
339             throws IOException {
340 
341         URLClassLoader loader = new JDOConfigTestClassLoader(
342                 JDOCONFIG_CLASSPATH_PREFIX,
343                 getClass().getClassLoader());
344 
345         for (int i = 0; i < classpaths.length; i++) {
346             ClasspathHelper.addFile(classpaths[i], loader);
347         }
348 
349         Map expected = prepareInitialExpectedMap(
350                 testVariantName,
351                 listenerCount,
352                 vendorSpecificPropertyCount,
353                 excludeName,
354                 excludePUName);
355         
356         String name = testVariantName == null
357                 ? null
358                 : (String) expected.get(PROPERTY_NAME);
359 
360         Map actual = JDOHelper.getPropertiesFromJdoconfig(name, loader);
361 
362         assertNotNull("No properties found", actual);
363         if (checkEqualProperties) {
364             assertEqualProperties(expected, actual);
365         }
366     }
367 
368     public void testPositive00_PMF0_BasicPMFConfigUsingOnlyStandardAttributesAndListeners()
369             throws IOException {
370         doPositiveTest(
371                 new String[]{JDOCONFIG_CLASSPATH_PREFIX + "/Positive00"},
372                 "positive00.pmf0",
373                 2,
374                 0,
375                 true);
376     }
377 
378     public void testPositive00_PMF1_BasicPMFConfigUsingOnlyPropertyElementsWithStandardJavaxDotJDOProperties()
379             throws IOException {
380         doPositiveTest(
381                 new String[]{JDOCONFIG_CLASSPATH_PREFIX + "/Positive00"},
382                 "positive00.pmf1",
383                 2,
384                 0,
385                 true);
386     }
387 
388     public void testPositive00_PMF2_NestedPropertyElementsWithOnlyStandardAttributeNames()
389             throws IOException {
390         doPositiveTest(
391                 new String[]{JDOCONFIG_CLASSPATH_PREFIX + "/Positive00"},
392                 "positive00.pmf2",
393                 2,
394                 0,
395                 true);
396     }
397 
398     public void testPositive00_PMF3_StandardAttributesPlusNonstandardPropertiesInPropertyElements()
399             throws IOException {
400         doPositiveTest(
401                 new String[]{JDOCONFIG_CLASSPATH_PREFIX + "/Positive00"},
402                 "positive00.pmf3",
403                 2,
404                 2,
405                 true);
406     }
407 
408     public void testPositive00_PMF4_StandardAttributesPlusNonstandardAttributes()
409             throws IOException {
410         doPositiveTest(
411                 new String[]{JDOCONFIG_CLASSPATH_PREFIX + "/Positive00"},
412                 "positive00.pmf4",
413                 0,
414                 2,
415                 true);
416     }
417 
418     public void testPositive01_DuplicatePUsInDifferentConfigFilesButNotRequested()
419             throws IOException {
420 
421         URLClassLoader loader = new JDOConfigTestClassLoader(
422                 JDOCONFIG_CLASSPATH_PREFIX,
423                 getClass().getClassLoader());
424 
425         String[] classpaths = new String[]{
426             JDOCONFIG_CLASSPATH_PREFIX + "/Positive01/1a",
427             JDOCONFIG_CLASSPATH_PREFIX + "/Positive01/1b"
428         };
429         for (int i = 0; i < classpaths.length; i++) {
430             ClasspathHelper.addFile(classpaths[i], loader);
431         }
432 
433         Map actual = JDOHelper.getPropertiesFromJdoconfig(
434                 ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME, loader);
435     }
436 
437     public void testPositive02_GetAnonymousPMFWithNoProperties()
438             throws IOException {
439         
440         URLClassLoader loader = new JDOConfigTestClassLoader(
441                 JDOCONFIG_CLASSPATH_PREFIX,
442                 getClass().getClassLoader());
443 
444             ClasspathHelper.addFile(
445                     JDOCONFIG_CLASSPATH_PREFIX + "/Positive02", loader);
446 
447         Map properties = JDOHelper.getPropertiesFromJdoconfig(
448                 ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME, loader);
449         assertNotNull(
450                 "Anonymous PMF with no properties returned null", properties);
451         assertTrue(
452                 "Anonymous PMF with no properties had properties",
453                 properties.size() == 0);
454     }
455 
456     public void testPositive03_PMF0_PMFClassNameViaServicesLookup()
457             throws IOException {
458 
459         URLClassLoader loader = new JDOConfigTestClassLoader(
460                 JDOCONFIG_CLASSPATH_PREFIX,
461                 getClass().getClassLoader());
462         ClasspathHelper.addFile(JDOCONFIG_CLASSPATH_PREFIX + "/Positive03", loader);
463 
464         String expected = "class.positive03.pmf0";
465         String actual = getPMFClassNameViaServiceLookup(loader);
466 
467         assertNotNull("No PMF name found via services lookup", actual);
468         assertEquals(expected, actual);
469     }
470 
471     public void testPositive04_PMF0_PMFClassNameViaServicesLookup()
472             throws IOException {
473 
474         URLClassLoader loader = new JDOConfigTestClassLoader(
475                 JDOCONFIG_CLASSPATH_PREFIX,
476                 getClass().getClassLoader());
477         ClasspathHelper.addFile(JDOCONFIG_CLASSPATH_PREFIX + "/Positive04", loader);
478 
479         String expected = "class.positive04.pmf0";
480         String actual = getPMFClassNameViaServiceLookup(loader);
481 
482         assertNotNull("No PMF name found via services lookup", actual);
483         assertEquals(expected, actual);
484     }
485 
486     public void testPositive05_PMF0_PMFClassNameViaServicesLookup()
487             throws IOException {
488 
489         URLClassLoader loader = new JDOConfigTestClassLoader(
490                 JDOCONFIG_CLASSPATH_PREFIX,
491                 getClass().getClassLoader());
492         ClasspathHelper.addFile(
493                 JDOCONFIG_CLASSPATH_PREFIX + "/Positive05", loader);
494 
495         String expected = "class.positive05.pmf0";
496         String actual = getPMFClassNameViaServiceLookup(loader);
497 
498         assertNotNull("No PMF name found via services lookup", actual);
499         assertEquals(expected, actual);
500     }
501 
502     public void testPositive06_PMF0_GetAnonymousPMFProperties()
503             throws IOException {
504 
505         URLClassLoader loader = new JDOConfigTestClassLoader(
506                 JDOCONFIG_CLASSPATH_PREFIX,
507                 getClass().getClassLoader());
508 
509         ClasspathHelper.addFile(
510                 JDOCONFIG_CLASSPATH_PREFIX + "/Positive06", loader);
511 
512         Map expected = prepareInitialExpectedMap(
513                 "positive06.pmf0", 2, 0, true, true);
514 
515         Map actual = JDOHelper.getPropertiesFromJdoconfig(
516                 ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME, loader);
517 
518         assertNotNull("No properties found", actual);
519         assertEqualProperties(expected, actual);
520     }
521 
522     public void testPositive07_PMF0_GetAnonymousPMFPropertiesWithPUName()
523             throws IOException {
524 
525         URLClassLoader loader = new JDOConfigTestClassLoader(
526                 JDOCONFIG_CLASSPATH_PREFIX,
527                 getClass().getClassLoader());
528 
529         ClasspathHelper.addFile(
530                 JDOCONFIG_CLASSPATH_PREFIX + "/Positive07", loader);
531 
532         Map expected = prepareInitialExpectedMap(
533                 "positive07.pmf0", 2, 0, true, false);
534 
535         Map actual = JDOHelper.getPropertiesFromJdoconfig(
536                 ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME, loader);
537 
538         assertNotNull("No properties found", actual);
539         assertEqualProperties(expected, actual);
540     }
541 
542     public void testNegative00_EmptyJDOConfigXML() throws IOException {
543         try {
544             URLClassLoader loader = new JDOConfigTestClassLoader(
545                     JDOCONFIG_CLASSPATH_PREFIX,
546                     getClass().getClassLoader());
547             ClasspathHelper.addFile(
548                     JDOCONFIG_CLASSPATH_PREFIX + "/Negative0", loader);
549 
550             JDOHelper.getPersistenceManagerFactory(loader);
551             fail("JDOHelper failed to throw JDOFatalUserException");
552         }
553         catch (JDOFatalUserException x) {
554             // sunny day
555         }
556     }
557 
558     public void testNegative01_NoPersistenceUnitsDefined() throws IOException {
559         try {
560             URLClassLoader loader = new JDOConfigTestClassLoader(
561                     JDOCONFIG_CLASSPATH_PREFIX,
562                     getClass().getClassLoader());
563             ClasspathHelper.addFile(
564                     JDOCONFIG_CLASSPATH_PREFIX + "/Negative01", loader);
565 
566             JDOHelper.getPersistenceManagerFactory(loader);
567             fail("JDOHelper failed to throw JDOFatalUserException");
568         }
569         catch (JDOFatalUserException x) {
570             // joy, sweet joy
571         }
572     }
573 
574     public void testNegative02_DuplicateAnonymousPersistenceUnitsInSameConfig()
575             throws IOException {
576         try {
577             URLClassLoader loader = new JDOConfigTestClassLoader(
578                     JDOCONFIG_CLASSPATH_PREFIX,
579                     getClass().getClassLoader());
580             ClasspathHelper.addFile(
581                     JDOCONFIG_CLASSPATH_PREFIX + "/Negative02", loader);
582 
583             JDOHelper.getPersistenceManagerFactory(loader);
584             fail("JDOHelper failed to throw JDOFatalUserException");
585         }
586         catch (JDOFatalUserException x) {
587             // the cockles of my heart warmeth
588         }
589     }
590 
591     public void testNegative03_DuplicateNamedPersistenceUnitsInSameConfig()
592             throws IOException {
593         try {
594             URLClassLoader loader = new JDOConfigTestClassLoader(
595                     JDOCONFIG_CLASSPATH_PREFIX,
596                     getClass().getClassLoader());
597             ClasspathHelper.addFile(
598                     JDOCONFIG_CLASSPATH_PREFIX + "/Negative03", loader);
599 
600             JDOHelper.getPersistenceManagerFactory(
601                     "name.negative03",
602                     loader);
603 
604             fail("JDOHelper failed to throw JDOFatalUserException");
605         }
606         catch (JDOFatalUserException x) {
607             // warm fuzzies
608         }
609     }
610 
611     public void testNegative04_DuplicatePUNamePropertyInAttributeAndElement()
612             throws IOException {
613         try {
614             URLClassLoader loader = new JDOConfigTestClassLoader(
615                     JDOCONFIG_CLASSPATH_PREFIX,
616                     getClass().getClassLoader());
617             ClasspathHelper.addFile(
618                     JDOCONFIG_CLASSPATH_PREFIX + "/Negative04", loader);
619 
620             JDOHelper.getPersistenceManagerFactory(
621                     "name.negative04.value0",
622                     loader);
623 
624             fail("JDOHelper failed to throw JDOFatalUserException");
625         }
626         catch (JDOFatalUserException x) {
627             // no cold pricklies
628         }
629     }
630 
631     public void testNegative05_DuplicatePropertyInAttributeAndElement()
632             throws IOException {
633         try {
634             URLClassLoader loader = new JDOConfigTestClassLoader(
635                     JDOCONFIG_CLASSPATH_PREFIX,
636                     getClass().getClassLoader());
637             ClasspathHelper.addFile(
638                     JDOCONFIG_CLASSPATH_PREFIX + "/Negative05", loader);
639 
640             JDOHelper.getPersistenceManagerFactory(loader);
641 
642             fail("JDOHelper failed to throw JDOFatalUserException");
643         }
644         catch (JDOFatalUserException x) {
645             // party!
646         }
647     }
648 
649     public void testNegative06_DuplicatePUInDifferentConfigFiles()
650             throws IOException {
651         try {
652             URLClassLoader loader = new JDOConfigTestClassLoader(
653                     JDOCONFIG_CLASSPATH_PREFIX,
654                     getClass().getClassLoader());
655             ClasspathHelper.addFile(
656                     JDOCONFIG_CLASSPATH_PREFIX + "/Negative06/6a", loader);
657             ClasspathHelper.addFile(
658                     JDOCONFIG_CLASSPATH_PREFIX + "/Negative06/6b", loader);
659 
660             JDOHelper.getPersistenceManagerFactory(
661                     "name.negative06",
662                     loader);
663 
664             fail("JDOHelper failed to throw JDOFatalUserException");
665         }
666         catch (JDOFatalUserException x) {
667             // clear blue sky
668         }
669     }
670 
671     
672     public void testNegative07_EmptyServicesFile()
673             throws IOException {
674         JDOConfigTestClassLoader testLoader = new JDOConfigTestClassLoader(
675                 new String[]{JDOCONFIG_CLASSPATH_PREFIX},
676                 getClass().getClassLoader());
677         ClasspathHelper.addFile(
678                 JDOCONFIG_CLASSPATH_PREFIX + "/Negative07", testLoader);
679         String shouldBeNull =
680                getPMFClassNameViaServiceLookup(testLoader);
681         assertNull(shouldBeNull);
682     }
683 
684     public void testNegative08_NoResourcesFound() {
685         String resource = "" + RANDOM.nextLong();
686 
687         InputStream in =
688                 getClass().getClassLoader().getResourceAsStream(resource);
689         assertNull(in);
690 
691         // resource pretty much guaranteed not to exist
692         try {
693             JDOHelper.getPersistenceManagerFactory(resource);
694             fail("JDOHelper failed to throw JDOFatalUserException");
695         }
696         catch (JDOFatalUserException x) {
697             // happy path
698         }
699     }
700 
701     public void testNegative08_ServicesFileWithOnlyComments()
702             throws IOException {
703         JDOConfigTestClassLoader testLoader = new JDOConfigTestClassLoader(
704                 new String[]{JDOCONFIG_CLASSPATH_PREFIX},
705                 getClass().getClassLoader());
706         ClasspathHelper.addFile(
707                 JDOCONFIG_CLASSPATH_PREFIX + "/Negative08", testLoader);
708         String shouldBeNull =
709                 getPMFClassNameViaServiceLookup(testLoader);
710         assertNull(shouldBeNull);
711     }
712     
713     public void testNegative09_MultipleInvalidClassesInDifferentServicesFiles()
714             throws IOException {
715 
716         // no class name in Negative09/jdoconfig.xml
717         // 9a and 9b include services/javax.jdo.PersistenceManagerFactory
718         // with bad implementations
719         try {
720             URLClassLoader loader = new JDOConfigTestClassLoader(
721                     JDOCONFIG_CLASSPATH_PREFIX,
722                     getClass().getClassLoader());
723             ClasspathHelper.addFile(
724                     JDOCONFIG_CLASSPATH_PREFIX + "/Negative09/9a", loader);
725             ClasspathHelper.addFile(
726                     JDOCONFIG_CLASSPATH_PREFIX + "/Negative09/9b", loader);
727             ClasspathHelper.addFile(
728                     TEST_CLASSPATH, loader);
729             ClasspathHelper.addFile(
730                     API_CLASSPATH, loader);
731 
732             JDOHelper.getPersistenceManagerFactory("name.negative09", loader);
733 
734             fail("JDOHelper failed to throw JDOFatalUserException");
735         }
736         catch (JDOFatalException x) {
737 
738             Throwable[] nestedExceptions = x.getNestedExceptions();
739             if (nestedExceptions.length != 2) {
740                 appendMessage(
741                 "JDOHelper.getPersistenceManagerFactory wrong number of " +
742                 "nested exceptions. Expected 2, got " + nestedExceptions.length +
743                         "\n" + x);
744             }
745             for (int i = 0; i < nestedExceptions.length; ++i) {
746                 Throwable exception = nestedExceptions[i];
747                 if (!(exception instanceof JDOFatalException)) {
748                     appendMessage("Nested exception " + 
749                             exception.getClass().getName() + 
750                             " is not a JDOFatalException.");
751                 }
752             }
753         }
754         failOnError();
755     }
756 
757     private String getPMFClassNameViaServiceLookup(ClassLoader loader) {
758         try {
759             Enumeration urls = JDOHelper.getResources(loader, 
760                 SERVICE_LOOKUP_PMF_RESOURCE_NAME);
761             while (urls.hasMoreElements()) {
762                 // return the first one found
763                 return JDOHelper.getClassNameFromURL((URL)urls.nextElement());
764             }
765         } catch (Exception ex) {
766             // ignore exceptions from i/o errors
767         }
768         return null;            
769     }
770 }