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 java.io.IOException;
21  import java.io.InputStream;
22  import java.net.URLClassLoader;
23  import java.util.HashMap;
24  import java.util.Map;
25  
26  import javax.jdo.util.BatchTestRunner;
27  
28  import junit.framework.TestSuite;
29  
30  /**
31   * 
32   * Tests class javax.jdo.JDOHelper for META-INF/jdoconfig.xml compliance.
33   * 
34   */
35  public class JDOHelperConfigTest extends AbstractJDOConfigTest implements
36  	Constants {
37  
38      public static void main(String args[]) {
39  	BatchTestRunner.run(JDOHelperConfigTest.class);
40      }
41  
42      /**
43       * {@inheritDoc}
44       * 
45       * @return {@inheritDoc}
46       */
47      public static TestSuite suite() {
48  	return new TestSuite(JDOHelperConfigTest.class);
49      }
50  
51      /**
52       * Builds up a {@link java.util.Map Map} object that contains key parameter
53       * values specific to a named test. All of the properties needed to run a
54       * particular test are loaded into this object.
55       * 
56       * @param testVariant
57       *            the name of the test to include in the {@link java.util.Map
58       *            Map} values.
59       * @param listenerCount
60       *            the number of life cycle listener class names to add to this
61       *            map. The listener names will begin with the value stored in
62       *            {@link javax.jdo.Constants.PROPERTY_INSTANCE_LIFECYCLE_LISTENER
63       *            PROPERTY_INSTANCE_LIFECYCLE_LISTENER}.
64       * @param vendorSpecificPropertyCount
65       *            the number of properties named of the form
66       * 
67       *            <pre>
68       * &quot;property.&quot; + testVariant + &quot;.name&quot;
69       * </pre>
70       * 
71       *            that are added to the map.
72       * @param excludeName
73       *            if true the property specified by
74       *            {@link javax.jdo.Constants.PROPERTY_NAME PROPERTY_NAME} is not
75       *            added to the map.
76       * @param excludePUName
77       *            if true the property specified by
78       *            {@link javax.jdo.Constants.PROPERTY_PERSISTENCE_UNIT_NAME
79       *            PROPERTY_PERSISTENCE_UNIT_NAME} is not added to the map.
80       * @return a new {@link java.util.Map Map} object populated with properties
81       *         that can be used in this test suite.
82       */
83      protected Map prepareInitialExpectedMap(String testVariant,
84  	    int listenerCount, int vendorSpecificPropertyCount,
85  	    boolean excludeName, boolean excludePUName) {
86  	Map<String, String> expected = new HashMap<String, String>();
87  
88  	if (!excludeName) {
89  	    expected.put(PROPERTY_NAME, PMF_ATTRIBUTE_NAME + "." + testVariant);
90  	}
91  	if (!excludePUName) {
92  	    expected.put(PROPERTY_PERSISTENCE_UNIT_NAME,
93  		    PMF_ATTRIBUTE_PERSISTENCE_UNIT_NAME + "." + testVariant);
94  	}
95  
96  	expected.put(PROPERTY_PERSISTENCE_MANAGER_FACTORY_CLASS,
97  		PMF_ATTRIBUTE_CLASS + "." + testVariant);
98  	expected.put(PROPERTY_CONNECTION_DRIVER_NAME,
99  		PMF_ATTRIBUTE_CONNECTION_DRIVER_NAME + "." + testVariant);
100 	expected.put(PROPERTY_CONNECTION_FACTORY_NAME,
101 		PMF_ATTRIBUTE_CONNECTION_FACTORY_NAME + "." + testVariant);
102 	expected.put(PROPERTY_CONNECTION_FACTORY2_NAME,
103 		PMF_ATTRIBUTE_CONNECTION_FACTORY2_NAME + "." + testVariant);
104 	expected.put(PROPERTY_CONNECTION_PASSWORD,
105 		PMF_ATTRIBUTE_CONNECTION_PASSWORD + "." + testVariant);
106 	expected.put(PROPERTY_CONNECTION_URL, PMF_ATTRIBUTE_CONNECTION_URL
107 		+ "." + testVariant);
108 	expected.put(PROPERTY_CONNECTION_USER_NAME,
109 		PMF_ATTRIBUTE_CONNECTION_USER_NAME + "." + testVariant);
110 	expected.put(PROPERTY_IGNORE_CACHE, PMF_ATTRIBUTE_IGNORE_CACHE + "."
111 		+ testVariant);
112 	expected.put(PROPERTY_MAPPING, PMF_ATTRIBUTE_MAPPING + "."
113 		+ testVariant);
114 	expected.put(PROPERTY_MULTITHREADED, PMF_ATTRIBUTE_MULTITHREADED + "."
115 		+ testVariant);
116 	expected.put(PROPERTY_NONTRANSACTIONAL_READ,
117 		PMF_ATTRIBUTE_NONTRANSACTIONAL_READ + "." + testVariant);
118 	expected.put(PROPERTY_NONTRANSACTIONAL_WRITE,
119 		PMF_ATTRIBUTE_NONTRANSACTIONAL_WRITE + "." + testVariant);
120 	expected.put(PROPERTY_OPTIMISTIC, PMF_ATTRIBUTE_OPTIMISTIC + "."
121 		+ testVariant);
122 	expected.put(PROPERTY_RESTORE_VALUES, PMF_ATTRIBUTE_RESTORE_VALUES
123 		+ "." + testVariant);
124 	expected.put(PROPERTY_RETAIN_VALUES, PMF_ATTRIBUTE_RETAIN_VALUES + "."
125 		+ testVariant);
126 	expected.put(PROPERTY_DETACH_ALL_ON_COMMIT,
127 		PMF_ATTRIBUTE_DETACH_ALL_ON_COMMIT + "." + testVariant);
128 	expected.put(PROPERTY_SERVER_TIME_ZONE_ID,
129 		PMF_ATTRIBUTE_SERVER_TIME_ZONE_ID + "." + testVariant);
130 
131 	// listeners
132 	for (int i = 0; i < listenerCount; i++) {
133 	    expected.put(PROPERTY_PREFIX_INSTANCE_LIFECYCLE_LISTENER
134 		    + "listener." + testVariant + ".listener" + i, "classes."
135 		    + testVariant + ".classes" + i);
136 	}
137 
138 	// vendor-specific properties
139 	for (int i = 0; i < vendorSpecificPropertyCount; i++) {
140 	    expected.put("property." + testVariant + ".name" + i, "property."
141 		    + testVariant + ".value" + i);
142 	}
143 
144 	return expected;
145     }
146 
147     /**
148      * Performs a test specified by <tt>testVariantName</tt>, by building up a
149      * property map and executing the test according to the property values.
150      * With this version of <tt>doPositiveTest</tt> the property name (
151      * {@link javax.jdo.Constants.PROPERTY_NAME PROPERTY_NAME}) and the
152      * {@link javax.jdo.Constants.PERSISTENCE_UNIT_NAME PERSISTENCE_UNIT_NAME})
153      * are included in the property map that is used to run the test.
154      * 
155      * @param classpaths
156      *            class paths to add to the class loader that runs the test that
157      *            specify where <tt>jdoconfig.xml</tt> can be found.
158      * @param testVariantName
159      *            the name of the test.
160      * @param listenerCount
161      *            number of listeners utilized in the test.
162      * @param vendorSpecificPropertyCount
163      *            number of vendor properties used in the test.
164      * @param checkEqualProperties
165      *            if true the test's properties are tested.
166      * @throws java.io.IOException
167      *             if an {@java.io.IOException
168      *             IOException} occurs during class loading or any part of the
169      *             test.
170      */
171     protected void doPositiveTest(String[] classpaths, String testVariantName,
172 	    int listenerCount, int vendorSpecificPropertyCount,
173 	    boolean checkEqualProperties) throws IOException {
174 
175 	doPositiveTest(classpaths, testVariantName, listenerCount,
176 		vendorSpecificPropertyCount, checkEqualProperties, false, false);
177     }
178 
179     /**
180      * Performs a test specified by <tt>testVariantName</tt>, by building up a
181      * property map and executing the test according to the property values. An
182      * assertion exeception is thrown if the test being run has a negative
183      * (non-true) result. With this version of <tt>doPositiveTest</tt> the
184      * property name ({@link javax.jdo.Constants.PROPERTY_NAME PROPERTY_NAME})
185      * and the {@link javax.jdo.Constants.PERSISTENCE_UNIT_NAME
186      * PERSISTENCE_UNIT_NAME}) are included in the property map that is used to
187      * run the test.
188      * 
189      * @param classpaths
190      *            class paths to add to the class loader that runs the test that
191      *            specify where <tt>jdoconfig.xml</tt> can be found.
192      * @param testVariantName
193      *            the name of the test.
194      * @param listenerCount
195      *            number of listeners utilized in the test.
196      * @param vendorSpecificPropertyCount
197      *            number of vendor properties used in the test.
198      * @param checkEqualProperties
199      *            if true the test's properties are tested.
200      * @param excludeName
201      *            if true the property specified by
202      *            {@link javax.jdo.Constants.PROPERTY_NAME PROPERTY_NAME} is not
203      *            added to the map.
204      * @param excludePUName
205      *            if true the property specified by
206      *            {@link javax.jdo.Constants.PROPERTY_PERSISTENCE_UNIT_NAME
207      *            PROPERTY_PERSISTENCE_UNIT_NAME} is not added to the map.
208      * @throws java.io.IOException
209      *             if an {@java.io.IOException
210      *             IOException} occurs during class loading or any part of the
211      *             test.
212      */
213     protected void doPositiveTest(String[] classpaths, String testVariantName,
214 	    int listenerCount, int vendorSpecificPropertyCount,
215 	    boolean checkEqualProperties, boolean excludeName,
216 	    boolean excludePUName) throws IOException {
217 
218 	URLClassLoader loader = new JDOConfigTestClassLoader(getClass()
219 		.getClassLoader(), classpaths);
220 
221 	Map expected = prepareInitialExpectedMap(testVariantName,
222 		listenerCount, vendorSpecificPropertyCount, excludeName,
223 		excludePUName);
224 
225 	String name = testVariantName == null ? null : (String) expected
226 		.get(PROPERTY_NAME);
227 
228 	Map actual = JDOHelper.getPropertiesFromJdoconfig(name, loader);
229 
230 	assertNotNull("No properties found", actual);
231 	if (checkEqualProperties) {
232 	    assertEqualProperties(expected, actual);
233 	}
234     }
235 
236     public void testPositive00_PMF0_BasicPMFConfigUsingOnlyStandardAttributesAndListeners()
237 	    throws IOException {
238 	doPositiveTest(new String[] { JDOCONFIG_CLASSPATH_PREFIX
239 		+ "/Positive00" }, "positive00.pmf0", 2, 0, true);
240     }
241 
242     public void testPositive00_PMF1_BasicPMFConfigUsingOnlyPropertyElementsWithStandardJavaxDotJDOProperties()
243 	    throws IOException {
244 	doPositiveTest(new String[] { JDOCONFIG_CLASSPATH_PREFIX
245 		+ "/Positive00" }, "positive00.pmf1", 2, 0, true);
246     }
247 
248     public void testPositive00_PMF2_NestedPropertyElementsWithOnlyStandardAttributeNames()
249 	    throws IOException {
250 	doPositiveTest(new String[] { JDOCONFIG_CLASSPATH_PREFIX
251 		+ "/Positive00" }, "positive00.pmf2", 2, 0, true);
252     }
253 
254     public void testPositive00_PMF3_StandardAttributesPlusNonstandardPropertiesInPropertyElements()
255 	    throws IOException {
256 	doPositiveTest(new String[] { JDOCONFIG_CLASSPATH_PREFIX
257 		+ "/Positive00" }, "positive00.pmf3", 2, 2, true);
258     }
259 
260     public void testPositive00_PMF4_StandardAttributesPlusNonstandardAttributes()
261 	    throws IOException {
262 	doPositiveTest(new String[] { JDOCONFIG_CLASSPATH_PREFIX
263 		+ "/Positive00" }, "positive00.pmf4", 0, 2, true);
264     }
265 
266     public void testPositive01_DuplicatePUsInDifferentConfigFilesButNotRequested()
267 	    throws IOException {
268 
269 	String[] classpaths = new String[] {
270 		JDOCONFIG_CLASSPATH_PREFIX + "/Positive01/1a",
271 		JDOCONFIG_CLASSPATH_PREFIX + "/Positive01/1b" };
272 	URLClassLoader loader = new JDOConfigTestClassLoader(getClass()
273 		.getClassLoader(), classpaths);
274 
275 	Map actual = JDOHelper.getPropertiesFromJdoconfig(
276 		ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME, loader);
277     }
278 
279     public void testPositive02_GetAnonymousPMFWithNoProperties()
280 	    throws IOException {
281 
282 	URLClassLoader loader = new JDOConfigTestClassLoader(getClass()
283 		.getClassLoader(), JDOCONFIG_CLASSPATH_PREFIX + "/Positive02/");
284 
285 	Map properties = JDOHelper.getPropertiesFromJdoconfig(
286 		ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME, loader);
287 	assertNotNull("Anonymous PMF with no properties returned null",
288 		properties);
289 	assertTrue("Anonymous PMF with no properties had properties",
290 		properties.size() == 0);
291     }
292 
293     public void testPositive03_PMF0_PMFClassNameViaServicesLookup()
294 	    throws IOException {
295 
296 	URLClassLoader loader = new JDOConfigTestClassLoader(getClass()
297 		.getClassLoader(), JDOCONFIG_CLASSPATH_PREFIX + "/Positive03/");
298 
299 	String expected = "class.positive03.pmf0";
300 	String actual = getPMFClassNameViaServiceLookup(loader);
301 
302 	assertNotNull("No PMF name found via services lookup", actual);
303 	assertEquals(expected, actual);
304     }
305 
306     public void testPositive04_PMF0_PMFClassNameViaServicesLookup()
307 	    throws IOException {
308 
309 	URLClassLoader loader = new JDOConfigTestClassLoader(getClass()
310 		.getClassLoader(), JDOCONFIG_CLASSPATH_PREFIX + "/Positive04/");
311 
312 	String expected = "class.positive04.pmf0";
313 	String actual = getPMFClassNameViaServiceLookup(loader);
314 
315 	assertNotNull("No PMF name found via services lookup", actual);
316 	assertEquals(expected, actual);
317     }
318 
319     public void testPositive05_PMF0_PMFClassNameViaServicesLookup()
320 	    throws IOException {
321 
322 	URLClassLoader loader = new JDOConfigTestClassLoader(getClass()
323 		.getClassLoader(), JDOCONFIG_CLASSPATH_PREFIX + "/Positive05/");
324 
325 	String expected = "class.positive05.pmf0";
326 	String actual = getPMFClassNameViaServiceLookup(loader);
327 
328 	assertNotNull("No PMF name found via services lookup", actual);
329 	assertEquals(expected, actual);
330     }
331 
332     public void testPositive06_PMF0_GetAnonymousPMFProperties()
333 	    throws IOException {
334 
335 	URLClassLoader loader = new JDOConfigTestClassLoader(getClass()
336 		.getClassLoader(), JDOCONFIG_CLASSPATH_PREFIX + "/Positive06/");
337 
338 	Map expected = prepareInitialExpectedMap("positive06.pmf0", 2, 0, true,
339 		true);
340 
341 	Map actual = JDOHelper.getPropertiesFromJdoconfig(
342 		ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME, loader);
343 
344 	assertNotNull("No properties found", actual);
345 	assertEqualProperties(expected, actual);
346     }
347 
348     public void testPositive07_PMF0_GetAnonymousPMFPropertiesWithPUName()
349 	    throws IOException {
350 
351 	URLClassLoader loader = new JDOConfigTestClassLoader(getClass()
352 		.getClassLoader(), JDOCONFIG_CLASSPATH_PREFIX + "/Positive07/");
353 
354 	Map expected = prepareInitialExpectedMap("positive07.pmf0", 2, 0, true,
355 		false);
356 
357 	Map actual = JDOHelper.getPropertiesFromJdoconfig(
358 		ANONYMOUS_PERSISTENCE_MANAGER_FACTORY_NAME, loader);
359 
360 	assertNotNull("No properties found", actual);
361 	assertEqualProperties(expected, actual);
362     }
363 
364     public void testNegative00_EmptyJDOConfigXML() throws IOException {
365 	try {
366 	    URLClassLoader loader = new JDOConfigTestClassLoader(getClass()
367 		    .getClassLoader(), JDOCONFIG_CLASSPATH_PREFIX
368 		    + "/Negative0/");
369 
370 	    JDOHelper.getPersistenceManagerFactory(loader);
371 	    fail("JDOHelper failed to throw JDOFatalUserException");
372 	} catch (JDOFatalUserException x) {
373 	    // sunny day
374 	}
375     }
376 
377     public void testNegative01_NoPersistenceUnitsDefined() throws IOException {
378 	try {
379 	    URLClassLoader loader = new JDOConfigTestClassLoader(getClass()
380 		    .getClassLoader(), JDOCONFIG_CLASSPATH_PREFIX
381 		    + "/Negative01/");
382 
383 	    JDOHelper.getPersistenceManagerFactory(loader);
384 	    fail("JDOHelper failed to throw JDOFatalUserException");
385 	} catch (JDOFatalUserException x) {
386 	    // joy, sweet joy
387 	}
388     }
389 
390     public void testNegative02_DuplicateAnonymousPersistenceUnitsInSameConfig()
391 	    throws IOException {
392 	try {
393 	    URLClassLoader loader = new JDOConfigTestClassLoader(getClass()
394 		    .getClassLoader(), JDOCONFIG_CLASSPATH_PREFIX
395 		    + "/Negative02/");
396 
397 	    JDOHelper.getPersistenceManagerFactory(loader);
398 	    fail("JDOHelper failed to throw JDOFatalUserException");
399 	} catch (JDOFatalUserException x) {
400 	    // the cockles of my heart warmeth
401 	}
402     }
403 
404     public void testNegative03_DuplicateNamedPersistenceUnitsInSameConfig()
405 	    throws IOException {
406 	try {
407 	    URLClassLoader loader = new JDOConfigTestClassLoader(getClass()
408 		    .getClassLoader(), JDOCONFIG_CLASSPATH_PREFIX
409 		    + "/Negative03/");
410 
411 	    JDOHelper.getPersistenceManagerFactory("name.negative03", loader);
412 
413 	    fail("JDOHelper failed to throw JDOFatalUserException");
414 	} catch (JDOFatalUserException x) {
415 	    // warm fuzzies
416 	}
417     }
418 
419     public void testNegative04_DuplicatePUNamePropertyInAttributeAndElement()
420 	    throws IOException {
421 	try {
422 	    URLClassLoader loader = new JDOConfigTestClassLoader(getClass()
423 		    .getClassLoader(), JDOCONFIG_CLASSPATH_PREFIX
424 		    + "/Negative04/");
425 
426 	    JDOHelper.getPersistenceManagerFactory("name.negative04.value0",
427 		    loader);
428 
429 	    fail("JDOHelper failed to throw JDOFatalUserException");
430 	} catch (JDOFatalUserException x) {
431 	    // no cold pricklies
432 	}
433     }
434 
435     public void testNegative05_DuplicatePropertyInAttributeAndElement()
436 	    throws IOException {
437 	try {
438 	    URLClassLoader loader = new JDOConfigTestClassLoader(getClass()
439 		    .getClassLoader(), JDOCONFIG_CLASSPATH_PREFIX
440 		    + "/Negative05/");
441 
442 	    JDOHelper.getPersistenceManagerFactory(loader);
443 
444 	    fail("JDOHelper failed to throw JDOFatalUserException");
445 	} catch (JDOFatalUserException x) {
446 	    // party!
447 	}
448     }
449 
450     public void testNegative06_DuplicatePUInDifferentConfigFiles()
451 	    throws IOException {
452 	try {
453 	    URLClassLoader loader = new JDOConfigTestClassLoader(getClass()
454 		    .getClassLoader(), JDOCONFIG_CLASSPATH_PREFIX
455 		    + "/Negative06/6a/", JDOCONFIG_CLASSPATH_PREFIX
456 		    + "/Negative06/6b/");
457 
458 	    JDOHelper.getPersistenceManagerFactory("name.negative06", loader);
459 
460 	    fail("JDOHelper failed to throw JDOFatalUserException");
461 	} catch (JDOFatalUserException x) {
462 	    // clear blue sky
463 	}
464     }
465 
466     public void testNegative07_EmptyServicesFile() throws IOException {
467 	JDOConfigTestClassLoader testLoader = new JDOConfigTestClassLoader(
468 		getClass().getClassLoader(), JDOCONFIG_CLASSPATH_PREFIX
469 			+ "/Negative07/");
470 	String shouldBeNull = getPMFClassNameViaServiceLookup(testLoader);
471 	assertNull(shouldBeNull);
472     }
473 
474     public void testNegative08_NoResourcesFound() {
475 	String resource = "" + RANDOM.nextLong();
476 
477 	InputStream in = getClass().getClassLoader().getResourceAsStream(
478 		resource);
479 	assertNull(in);
480 
481 	// resource pretty much guaranteed not to exist
482 	try {
483 	    JDOHelper.getPersistenceManagerFactory(resource);
484 	    fail("JDOHelper failed to throw JDOFatalUserException");
485 	} catch (JDOFatalUserException x) {
486 	    // happy path
487 	}
488     }
489 
490     public void testNegative08_ServicesFileWithOnlyComments()
491 	    throws IOException {
492 	JDOConfigTestClassLoader testLoader = new JDOConfigTestClassLoader(
493 		getClass().getClassLoader(), JDOCONFIG_CLASSPATH_PREFIX
494 			+ "/Negative08/");
495 	String shouldBeNull = getPMFClassNameViaServiceLookup(testLoader);
496 	assertNull(shouldBeNull);
497     }
498 
499     public void testNegative09_MultipleInvalidClassesInDifferentServicesFiles()
500 	    throws IOException {
501 
502 	// no class name in Negative09/jdoconfig.xml
503 	// 9a and 9b include services/javax.jdo.PersistenceManagerFactory
504 	// with bad implementations
505 	try {
506 	    URLClassLoader loader = new JDOConfigTestClassLoader(getClass()
507 		    .getClassLoader(), JDOCONFIG_CLASSPATH_PREFIX
508 		    + "/Negative09/9a/", JDOCONFIG_CLASSPATH_PREFIX
509 		    + "/Negative09/9b/", TEST_CLASSPATH, API_CLASSPATH);
510 
511 	    JDOHelper.getPersistenceManagerFactory("name.negative09", loader);
512 
513 	    fail("JDOHelper failed to throw JDOFatalUserException");
514 	} catch (JDOFatalException x) {
515 
516 	    Throwable[] nestedExceptions = x.getNestedExceptions();
517 	    if (nestedExceptions.length != 2) {
518 		appendMessage("JDOHelper.getPersistenceManagerFactory wrong number of "
519 			+ "nested exceptions. Expected 2, got "
520 			+ nestedExceptions.length + "\n" + x);
521 	    }
522 	    for (int i = 0; i < nestedExceptions.length; ++i) {
523 		Throwable exception = nestedExceptions[i];
524 		if (!(exception instanceof JDOFatalException)) {
525 		    appendMessage("Nested exception "
526 			    + exception.getClass().getName()
527 			    + " is not a JDOFatalException.");
528 		}
529 	    }
530 	}
531 	failOnError();
532     }
533 }