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   * Tests class javax.jdo.JDOHelper for META-INF/jdoconfig.xml compliance.
36   */
37  public class JDOHelperConfigTest extends AbstractTest implements Constants {
38  
39      public static void main(String args[]) {
40          BatchTestRunner.run(JDOHelperConfigTest.class);
41      }
42  
43      public static TestSuite suite() {
44          return new TestSuite(JDOHelperConfigTest.class);
45      }
46  
47      protected static String JDOCONFIG_CLASSPATH_PREFIX
48              = initJDOConfigClasspathPrefix();
49  
50      protected static String initJDOConfigClasspathPrefix() {
51          return initBasedir() + "test/schema/jdoconfig";
52      }
53  
54      protected static String TEST_CLASSPATH =
55              initTestClasspath();
56  
57      protected static String initTestClasspath() {
58          return initBasedir() + "target/test-classes/";
59      }
60  
61      protected static String API_CLASSPATH =
62              initAPIClasspath();
63  
64      protected static String initAPIClasspath() {
65          return initBasedir() + "target/classes/";
66      }
67  
68      protected static String initBasedir() {
69          String basedir = System.getProperty("basedir");
70          if (basedir != null) {
71              if (!basedir.endsWith("/")) {
72                  basedir += "/";
73              }
74          } else {
75              basedir = "";
76          }
77          return basedir;
78      }
79  
80      protected static Random RANDOM = new Random(System.currentTimeMillis());
81  
82      protected Map prepareInitialExpectedMap(
83              String testVariant,
84              int listenerCount,
85              int vendorSpecificPropertyCount,
86              boolean excludeName,
87              boolean excludePUName
88      ) {
89          Map expected = new HashMap();
90  
91          if (!excludeName) {
92              expected.put(
93                      PROPERTY_NAME,
94                      PMF_ATTRIBUTE_NAME + "." + testVariant);
95          }
96          if (!excludePUName) {
97              expected.put(
98                      PROPERTY_PERSISTENCE_UNIT_NAME,
99                      PMF_ATTRIBUTE_PERSISTENCE_UNIT_NAME + "." + testVariant);
100         }
101 
102         expected.put(PROPERTY_PERSISTENCE_MANAGER_FACTORY_CLASS,
103                 PMF_ATTRIBUTE_CLASS + "." + testVariant);
104         expected.put(
105                 PROPERTY_CONNECTION_DRIVER_NAME,
106                 PMF_ATTRIBUTE_CONNECTION_DRIVER_NAME + "." + testVariant);
107         expected.put(
108                 PROPERTY_CONNECTION_FACTORY_NAME,
109                 PMF_ATTRIBUTE_CONNECTION_FACTORY_NAME + "." + testVariant);
110         expected.put(
111                 PROPERTY_CONNECTION_FACTORY2_NAME,
112                 PMF_ATTRIBUTE_CONNECTION_FACTORY2_NAME + "." + testVariant);
113         expected.put(
114                 PROPERTY_CONNECTION_PASSWORD,
115                 PMF_ATTRIBUTE_CONNECTION_PASSWORD + "." + testVariant);
116         expected.put(
117                 PROPERTY_CONNECTION_URL,
118                 PMF_ATTRIBUTE_CONNECTION_URL + "." + testVariant);
119         expected.put(
120                 PROPERTY_CONNECTION_USER_NAME,
121                 PMF_ATTRIBUTE_CONNECTION_USER_NAME + "." + testVariant);
122         expected.put(
123                 PROPERTY_IGNORE_CACHE,
124                 PMF_ATTRIBUTE_IGNORE_CACHE + "." + testVariant);
125         expected.put(
126                 PROPERTY_MAPPING,
127                 PMF_ATTRIBUTE_MAPPING + "." + testVariant);
128         expected.put(
129                 PROPERTY_MULTITHREADED,
130                 PMF_ATTRIBUTE_MULTITHREADED + "." + testVariant);
131         expected.put(
132                 PROPERTY_NONTRANSACTIONAL_READ,
133                 PMF_ATTRIBUTE_NONTRANSACTIONAL_READ + "." + testVariant);
134         expected.put(
135                 PROPERTY_NONTRANSACTIONAL_WRITE,
136                 PMF_ATTRIBUTE_NONTRANSACTIONAL_WRITE + "." + testVariant);
137         expected.put(
138                 PROPERTY_OPTIMISTIC,
139                 PMF_ATTRIBUTE_OPTIMISTIC + "." + testVariant);
140         expected.put(
141                 PROPERTY_RESTORE_VALUES,
142                 PMF_ATTRIBUTE_RESTORE_VALUES + "." + testVariant);
143         expected.put(
144                 PROPERTY_RETAIN_VALUES,
145                 PMF_ATTRIBUTE_RETAIN_VALUES + "." + testVariant);
146         expected.put(
147                 PROPERTY_DETACH_ALL_ON_COMMIT,
148                 PMF_ATTRIBUTE_DETACH_ALL_ON_COMMIT + "." + testVariant);
149         expected.put(
150                 PROPERTY_SERVER_TIME_ZONE_ID,
151                 PMF_ATTRIBUTE_SERVER_TIME_ZONE_ID + "." + testVariant);
152 
153         // listeners
154         for (int i = 0; i < listenerCount; i++) {
155             expected.put(
156                     PROPERTY_PREFIX_INSTANCE_LIFECYCLE_LISTENER +
157                             "listener." + testVariant + ".listener" + i,
158                     "classes." + testVariant + ".classes" + i
159             );
160         }
161 
162         // vendor-specific properties
163         for (int i = 0; i < vendorSpecificPropertyCount; i++) {
164             expected.put(
165                     "property." + testVariant + ".name" + i,
166                     "property." + testVariant + ".value" + i
167             );
168         }
169 
170         return expected;
171     }
172 
173     static void assertEqualProperties(Map expected, Map actual) {
174         Iterator i = expected.entrySet().iterator();
175         while (i.hasNext()) {
176             Map.Entry entry = (Map.Entry) i.next();
177             String key = (String) entry.getKey();
178             String expectedValue = (String) entry.getValue();
179             String actualValue = (String) actual.get(key);
180 
181             assertEquals(
182                     "Actual property at key [" + key + "] with value [" +
183                             actualValue + "] not equal to expected value [" +
184                             expectedValue + "]",
185                     expectedValue,
186                     actualValue);
187         }
188     }
189 
190     protected void doPositiveTest(
191             String[] classpaths,
192             String testVariantName,
193             int listenerCount,
194             int vendorSpecificPropertyCount,
195             boolean checkEqualProperties)
196             throws IOException {
197 
198         doPositiveTest(
199                 classpaths,
200                 testVariantName,
201                 listenerCount,
202                 vendorSpecificPropertyCount,
203                 checkEqualProperties,
204                 false,
205                 false);
206     }
207 
208     protected void doPositiveTest(
209             String[] classpaths,
210             String testVariantName,
211             int listenerCount,
212             int vendorSpecificPropertyCount,
213             boolean checkEqualProperties,
214             boolean excludeName,
215             boolean excludePUName)
216             throws IOException {
217 
218         URLClassLoader loader = new JDOConfigTestClassLoader(
219                 JDOCONFIG_CLASSPATH_PREFIX,
220                 getClass().getClassLoader());
221 
222         for (int i = 0; i < classpaths.length; i++) {
223             ClasspathHelper.addFile(classpaths[i], loader);
224         }
225 
226         Map expected = prepareInitialExpectedMap(
227                 testVariantName,
228                 listenerCount,
229                 vendorSpecificPropertyCount,
230                 excludeName,
231                 excludePUName);
232         
233         String name = testVariantName == null
234                 ? null
235                 : (String) expected.get(PROPERTY_NAME);
236 
237         Map actual = JDOHelper.getPropertiesFromJdoconfig(name, loader);
238 
239         assertNotNull("No properties found", actual);
240         if (checkEqualProperties) {
241             assertEqualProperties(expected, actual);
242         }
243     }
244 
245     public void testPositive00_PMF0_BasicPMFConfigUsingOnlyStandardAttributesAndListeners()
246             throws IOException {
247         doPositiveTest(
248                 new String[]{JDOCONFIG_CLASSPATH_PREFIX + "/Positive00"},
249                 "positive00.pmf0",
250                 2,
251                 0,
252                 true);
253     }
254 
255     public void testPositive00_PMF1_BasicPMFConfigUsingOnlyPropertyElementsWithStandardJavaxDotJDOProperties()
256             throws IOException {
257         doPositiveTest(
258                 new String[]{JDOCONFIG_CLASSPATH_PREFIX + "/Positive00"},
259                 "positive00.pmf1",
260                 2,
261                 0,
262                 true);
263     }
264 
265     public void testPositive00_PMF2_NestedPropertyElementsWithOnlyStandardAttributeNames()
266             throws IOException {
267         doPositiveTest(
268                 new String[]{JDOCONFIG_CLASSPATH_PREFIX + "/Positive00"},
269                 "positive00.pmf2",
270                 2,
271                 0,
272                 true);
273     }
274 
275     public void testPositive00_PMF3_StandardAttributesPlusNonstandardPropertiesInPropertyElements()
276             throws IOException {
277         doPositiveTest(
278                 new String[]{JDOCONFIG_CLASSPATH_PREFIX + "/Positive00"},
279                 "positive00.pmf3",
280                 2,
281                 2,
282                 true);
283     }
284 
285     public void testPositive00_PMF4_StandardAttributesPlusNonstandardAttributes()
286             throws IOException {
287         doPositiveTest(
288                 new String[]{JDOCONFIG_CLASSPATH_PREFIX + "/Positive00"},
289                 "positive00.pmf4",
290                 0,
291                 2,
292                 true);
293     }
294 
295     public void testPositive01_DuplicatePUsInDifferentConfigFilesButNotRequested()
296             throws IOException {
297 
298         URLClassLoader loader = new JDOConfigTestClassLoader(
299                 JDOCONFIG_CLASSPATH_PREFIX,
300                 getClass().getClassLoader());
301 
302         String[] classpaths = new String[]{
303             JDOCONFIG_CLASSPATH_PREFIX + "/Positive01/1a",
304             JDOCONFIG_CLASSPATH_PREFIX + "/Positive01/1b"
305         };
306         for (int i = 0; i < classpaths.length; i++) {
307             ClasspathHelper.addFile(classpaths[i], loader);
308         }
309 
310         Map actual = JDOHelper.getPropertiesFromJdoconfig(
311                 ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME, loader);
312     }
313 
314     public void testPositive02_GetAnonymousPMFWithNoProperties()
315             throws IOException {
316         
317         URLClassLoader loader = new JDOConfigTestClassLoader(
318                 JDOCONFIG_CLASSPATH_PREFIX,
319                 getClass().getClassLoader());
320 
321             ClasspathHelper.addFile(
322                     JDOCONFIG_CLASSPATH_PREFIX + "/Positive02", loader);
323 
324         Map properties = JDOHelper.getPropertiesFromJdoconfig(
325                 ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME, loader);
326         assertNotNull(
327                 "Anonymous PMF with no properties returned null", properties);
328         assertTrue(
329                 "Anonymous PMF with no properties had properties",
330                 properties.size() == 0);
331     }
332 
333     public void testPositive03_PMF0_PMFClassNameViaServicesLookup()
334             throws IOException {
335 
336         URLClassLoader loader = new JDOConfigTestClassLoader(
337                 JDOCONFIG_CLASSPATH_PREFIX,
338                 getClass().getClassLoader());
339         ClasspathHelper.addFile(JDOCONFIG_CLASSPATH_PREFIX + "/Positive03", loader);
340 
341         String expected = "class.positive03.pmf0";
342         String actual = getPMFClassNameViaServiceLookup(loader);
343 
344         assertNotNull("No PMF name found via services lookup", actual);
345         assertEquals(expected, actual);
346     }
347 
348     public void testPositive04_PMF0_PMFClassNameViaServicesLookup()
349             throws IOException {
350 
351         URLClassLoader loader = new JDOConfigTestClassLoader(
352                 JDOCONFIG_CLASSPATH_PREFIX,
353                 getClass().getClassLoader());
354         ClasspathHelper.addFile(JDOCONFIG_CLASSPATH_PREFIX + "/Positive04", loader);
355 
356         String expected = "class.positive04.pmf0";
357         String actual = getPMFClassNameViaServiceLookup(loader);
358 
359         assertNotNull("No PMF name found via services lookup", actual);
360         assertEquals(expected, actual);
361     }
362 
363     public void testPositive05_PMF0_PMFClassNameViaServicesLookup()
364             throws IOException {
365 
366         URLClassLoader loader = new JDOConfigTestClassLoader(
367                 JDOCONFIG_CLASSPATH_PREFIX,
368                 getClass().getClassLoader());
369         ClasspathHelper.addFile(
370                 JDOCONFIG_CLASSPATH_PREFIX + "/Positive05", loader);
371 
372         String expected = "class.positive05.pmf0";
373         String actual = getPMFClassNameViaServiceLookup(loader);
374 
375         assertNotNull("No PMF name found via services lookup", actual);
376         assertEquals(expected, actual);
377     }
378 
379     public void testPositive06_PMF0_GetAnonymousPMFProperties()
380             throws IOException {
381 
382         URLClassLoader loader = new JDOConfigTestClassLoader(
383                 JDOCONFIG_CLASSPATH_PREFIX,
384                 getClass().getClassLoader());
385 
386         ClasspathHelper.addFile(
387                 JDOCONFIG_CLASSPATH_PREFIX + "/Positive06", loader);
388 
389         Map expected = prepareInitialExpectedMap(
390                 "positive06.pmf0", 2, 0, true, true);
391 
392         Map actual = JDOHelper.getPropertiesFromJdoconfig(
393                 ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME, loader);
394 
395         assertNotNull("No properties found", actual);
396         assertEqualProperties(expected, actual);
397     }
398 
399     public void testPositive07_PMF0_GetAnonymousPMFPropertiesWithPUName()
400             throws IOException {
401 
402         URLClassLoader loader = new JDOConfigTestClassLoader(
403                 JDOCONFIG_CLASSPATH_PREFIX,
404                 getClass().getClassLoader());
405 
406         ClasspathHelper.addFile(
407                 JDOCONFIG_CLASSPATH_PREFIX + "/Positive07", loader);
408 
409         Map expected = prepareInitialExpectedMap(
410                 "positive07.pmf0", 2, 0, true, false);
411 
412         Map actual = JDOHelper.getPropertiesFromJdoconfig(
413                 ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME, loader);
414 
415         assertNotNull("No properties found", actual);
416         assertEqualProperties(expected, actual);
417     }
418 
419     public void testNegative00_EmptyJDOConfigXML() throws IOException {
420         try {
421             URLClassLoader loader = new JDOConfigTestClassLoader(
422                     JDOCONFIG_CLASSPATH_PREFIX,
423                     getClass().getClassLoader());
424             ClasspathHelper.addFile(
425                     JDOCONFIG_CLASSPATH_PREFIX + "/Negative0", loader);
426 
427             JDOHelper.getPersistenceManagerFactory(loader);
428             fail("JDOHelper failed to throw JDOFatalUserException");
429         }
430         catch (JDOFatalUserException x) {
431             // sunny day
432         }
433     }
434 
435     public void testNegative01_NoPersistenceUnitsDefined() throws IOException {
436         try {
437             URLClassLoader loader = new JDOConfigTestClassLoader(
438                     JDOCONFIG_CLASSPATH_PREFIX,
439                     getClass().getClassLoader());
440             ClasspathHelper.addFile(
441                     JDOCONFIG_CLASSPATH_PREFIX + "/Negative01", loader);
442 
443             JDOHelper.getPersistenceManagerFactory(loader);
444             fail("JDOHelper failed to throw JDOFatalUserException");
445         }
446         catch (JDOFatalUserException x) {
447             // joy, sweet joy
448         }
449     }
450 
451     public void testNegative02_DuplicateAnonymousPersistenceUnitsInSameConfig()
452             throws IOException {
453         try {
454             URLClassLoader loader = new JDOConfigTestClassLoader(
455                     JDOCONFIG_CLASSPATH_PREFIX,
456                     getClass().getClassLoader());
457             ClasspathHelper.addFile(
458                     JDOCONFIG_CLASSPATH_PREFIX + "/Negative02", loader);
459 
460             JDOHelper.getPersistenceManagerFactory(loader);
461             fail("JDOHelper failed to throw JDOFatalUserException");
462         }
463         catch (JDOFatalUserException x) {
464             // the cockles of my heart warmeth
465         }
466     }
467 
468     public void testNegative03_DuplicateNamedPersistenceUnitsInSameConfig()
469             throws IOException {
470         try {
471             URLClassLoader loader = new JDOConfigTestClassLoader(
472                     JDOCONFIG_CLASSPATH_PREFIX,
473                     getClass().getClassLoader());
474             ClasspathHelper.addFile(
475                     JDOCONFIG_CLASSPATH_PREFIX + "/Negative03", loader);
476 
477             JDOHelper.getPersistenceManagerFactory(
478                     "name.negative03",
479                     loader);
480 
481             fail("JDOHelper failed to throw JDOFatalUserException");
482         }
483         catch (JDOFatalUserException x) {
484             // warm fuzzies
485         }
486     }
487 
488     public void testNegative04_DuplicatePUNamePropertyInAttributeAndElement()
489             throws IOException {
490         try {
491             URLClassLoader loader = new JDOConfigTestClassLoader(
492                     JDOCONFIG_CLASSPATH_PREFIX,
493                     getClass().getClassLoader());
494             ClasspathHelper.addFile(
495                     JDOCONFIG_CLASSPATH_PREFIX + "/Negative04", loader);
496 
497             JDOHelper.getPersistenceManagerFactory(
498                     "name.negative04.value0",
499                     loader);
500 
501             fail("JDOHelper failed to throw JDOFatalUserException");
502         }
503         catch (JDOFatalUserException x) {
504             // no cold pricklies
505         }
506     }
507 
508     public void testNegative05_DuplicatePropertyInAttributeAndElement()
509             throws IOException {
510         try {
511             URLClassLoader loader = new JDOConfigTestClassLoader(
512                     JDOCONFIG_CLASSPATH_PREFIX,
513                     getClass().getClassLoader());
514             ClasspathHelper.addFile(
515                     JDOCONFIG_CLASSPATH_PREFIX + "/Negative05", loader);
516 
517             JDOHelper.getPersistenceManagerFactory(loader);
518 
519             fail("JDOHelper failed to throw JDOFatalUserException");
520         }
521         catch (JDOFatalUserException x) {
522             // party!
523         }
524     }
525 
526     public void testNegative06_DuplicatePUInDifferentConfigFiles()
527             throws IOException {
528         try {
529             URLClassLoader loader = new JDOConfigTestClassLoader(
530                     JDOCONFIG_CLASSPATH_PREFIX,
531                     getClass().getClassLoader());
532             ClasspathHelper.addFile(
533                     JDOCONFIG_CLASSPATH_PREFIX + "/Negative06/6a", loader);
534             ClasspathHelper.addFile(
535                     JDOCONFIG_CLASSPATH_PREFIX + "/Negative06/6b", loader);
536 
537             JDOHelper.getPersistenceManagerFactory(
538                     "name.negative06",
539                     loader);
540 
541             fail("JDOHelper failed to throw JDOFatalUserException");
542         }
543         catch (JDOFatalUserException x) {
544             // clear blue sky
545         }
546     }
547 
548     
549     public void testNegative07_EmptyServicesFile()
550             throws IOException {
551         JDOConfigTestClassLoader testLoader = new JDOConfigTestClassLoader(
552                 new String[]{JDOCONFIG_CLASSPATH_PREFIX},
553                 getClass().getClassLoader());
554         ClasspathHelper.addFile(
555                 JDOCONFIG_CLASSPATH_PREFIX + "/Negative07", testLoader);
556         String shouldBeNull =
557                getPMFClassNameViaServiceLookup(testLoader);
558         assertNull(shouldBeNull);
559     }
560 
561     public void testNegative08_NoResourcesFound() {
562         String resource = "" + RANDOM.nextLong();
563 
564         InputStream in =
565                 getClass().getClassLoader().getResourceAsStream(resource);
566         assertNull(in);
567 
568         // resource pretty much guaranteed not to exist
569         try {
570             JDOHelper.getPersistenceManagerFactory(resource);
571             fail("JDOHelper failed to throw JDOFatalUserException");
572         }
573         catch (JDOFatalUserException x) {
574             // happy path
575         }
576     }
577 
578     public void testNegative08_ServicesFileWithOnlyComments()
579             throws IOException {
580         JDOConfigTestClassLoader testLoader = new JDOConfigTestClassLoader(
581                 new String[]{JDOCONFIG_CLASSPATH_PREFIX},
582                 getClass().getClassLoader());
583         ClasspathHelper.addFile(
584                 JDOCONFIG_CLASSPATH_PREFIX + "/Negative08", testLoader);
585         String shouldBeNull =
586                 getPMFClassNameViaServiceLookup(testLoader);
587         assertNull(shouldBeNull);
588     }
589     
590     public void testNegative09_MultipleInvalidClassesInDifferentServicesFiles()
591             throws IOException {
592 
593         // no class name in Negative09/jdoconfig.xml
594         // 9a and 9b include services/javax.jdo.PersistenceManagerFactory
595         // with bad implementations
596         try {
597             URLClassLoader loader = new JDOConfigTestClassLoader(
598                     JDOCONFIG_CLASSPATH_PREFIX,
599                     getClass().getClassLoader());
600             ClasspathHelper.addFile(
601                     JDOCONFIG_CLASSPATH_PREFIX + "/Negative09/9a", loader);
602             ClasspathHelper.addFile(
603                     JDOCONFIG_CLASSPATH_PREFIX + "/Negative09/9b", loader);
604             ClasspathHelper.addFile(
605                     TEST_CLASSPATH, loader);
606             ClasspathHelper.addFile(
607                     API_CLASSPATH, loader);
608 
609             JDOHelper.getPersistenceManagerFactory("name.negative09", loader);
610 
611             fail("JDOHelper failed to throw JDOFatalUserException");
612         }
613         catch (JDOFatalException x) {
614 
615             Throwable[] nestedExceptions = x.getNestedExceptions();
616             if (nestedExceptions.length != 2) {
617                 appendMessage(
618                 "JDOHelper.getPersistenceManagerFactory wrong number of " +
619                 "nested exceptions. Expected 2, got " + nestedExceptions.length +
620                         "\n" + x);
621             }
622             for (int i = 0; i < nestedExceptions.length; ++i) {
623                 Throwable exception = nestedExceptions[i];
624                 if (!(exception instanceof JDOFatalException)) {
625                     appendMessage("Nested exception " + 
626                             exception.getClass().getName() + 
627                             " is not a JDOFatalException.");
628                 }
629             }
630         }
631         failOnError();
632     }
633 
634     private String getPMFClassNameViaServiceLookup(ClassLoader loader) {
635         try {
636             Enumeration urls = JDOHelper.getResources(loader, 
637                 SERVICE_LOOKUP_PMF_RESOURCE_NAME);
638             while (urls.hasMoreElements()) {
639                 // return the first one found
640                 return JDOHelper.getClassNameFromURL((URL)urls.nextElement());
641             }
642         } catch (Exception ex) {
643             // ignore exceptions from i/o errors
644         }
645         return null;            
646     }
647 }