001: /*
002: * TestSecondClassValues.java
003: *
004: * Created on October 13, 2006, 5:29 PM
005: *
006: * To change this template, choose Tools | Template Manager
007: * and open the template in the editor.
008: */
009: /*
010: * Licensed to the Apache Software Foundation (ASF) under one
011: * or more contributor license agreements. See the NOTICE file
012: * distributed with this work for additional information
013: * regarding copyright ownership. The ASF licenses this file
014: * to you under the Apache License, Version 2.0 (the
015: * "License"); you may not use this file except in compliance
016: * with the License. You may obtain a copy of the License at
017: *
018: * http://www.apache.org/licenses/LICENSE-2.0
019: *
020: * Unless required by applicable law or agreed to in writing,
021: * software distributed under the License is distributed on an
022: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
023: * KIND, either express or implied. See the License for the
024: * specific language governing permissions and limitations
025: * under the License.
026: */
027: package org.apache.openjpa.persistence.kernel;
028:
029: import java.math.BigDecimal;
030: import java.math.BigInteger;
031: import java.text.Collator;
032: import java.util.ArrayList;
033: import java.util.Collection;
034: import java.util.Collections;
035: import java.util.Comparator;
036: import java.util.Date;
037: import java.util.HashMap;
038: import java.util.Iterator;
039: import java.util.LinkedList;
040: import java.util.List;
041: import java.util.Map;
042:
043: import org.apache.openjpa.persistence.kernel.common.apps.SCOTest;
044: import org.apache.openjpa.persistence.common.utils.AbstractTestCase;
045: import junit.framework.AssertionFailedError;
046:
047: import org.apache.openjpa.persistence.OpenJPAEntityManager;
048:
049: public class TestSecondClassValues extends BaseKernelTest {
050:
051: public static double DOUBLE_PRECISION = 0.0001D;
052: public static float FLOAT_PRECISION = 0.0001F;
053: // mprudhom: use optimistic so we don't hang on some databases
054: private OpenJPAEntityManager pm;
055:
056: private String newline = System.getProperty("line.separator");
057:
058: /**
059: * Creates a new instance of TestSecondClassValues
060: */
061: public TestSecondClassValues() {
062: }
063:
064: public TestSecondClassValues(String name) {
065: super (name);
066: }
067:
068: public void setUp() {
069: pm = getPM(true, false);
070: }
071:
072: private int rnd() {
073: return ((int) (Math.random() * 20)) + 5;
074: }
075:
076: public void testMapDeletion() {
077: OpenJPAEntityManager pm;
078: pm = getPM();
079: startTx(pm);
080: SCOTest test = new SCOTest();
081: pm.persist(test);
082: Map map = new HashMap();
083: map.put("foo", new Integer(1));
084: map.put("bar", new Integer(2));
085: for (int i = 0; i < 10; i++)
086: map.put("baz#" + i, new Integer(i));
087:
088: test.setStrIntMap(map);
089: Object id = pm.getObjectId(test);
090: endTx(pm);
091:
092: startTx(pm);
093: test = (SCOTest) pm.find(SCOTest.class, id);
094: assertNotNull(test);
095: map = test.getStrIntMap();
096: assertEquals(12, map.size());
097: assertEquals(new Integer(1), map.get("foo"));
098: assertEquals(new Integer(2), map.get("bar"));
099: map.remove("bar");
100: endTx(pm);
101:
102: startTx(pm);
103: test = (SCOTest) pm.find(SCOTest.class, id);
104: assertNotNull(test);
105: map = test.getStrIntMap();
106: assertEquals(11, map.size());
107: assertEquals(new Integer(1), map.get("foo"));
108: assertTrue(map.get("bar") == null);
109:
110: map.clear();
111:
112: endTx(pm);
113:
114: startTx(pm);
115: test = (SCOTest) pm.find(SCOTest.class, id);
116: assertNotNull(test);
117: map = test.getStrIntMap();
118: assertEquals(0, map.size());
119: endTx(pm);
120: }
121:
122: public void testStringCollection() throws Exception {
123: ArrayList list = new ArrayList();
124: for (int i = 0; i < rnd(); i++)
125: list.add(randomString());
126:
127: saveSecondClassCollection(list);
128: }
129:
130: public void testLongCollection() throws Exception {
131: ArrayList list = new ArrayList();
132: for (int i = 0; i < rnd(); i++)
133: list.add(randomLong());
134: try {
135: saveSecondClassCollection(list);
136: } catch (AssertionFailedError afe) {
137: bug(AbstractTestCase.Platform.EMPRESS, 889, afe,
138: "Empress cannot store large long values");
139: }
140: }
141:
142: public void testShortCollection() throws Exception {
143: ArrayList list = new ArrayList();
144: for (int i = 0; i < rnd(); i++)
145: list.add(randomShort());
146: saveSecondClassCollection(list);
147: }
148:
149: public void testBigIntegerCollection() throws Exception {
150: ArrayList list = new ArrayList();
151: for (int i = 0; i < rnd(); i++)
152: list.add(randomBigInteger());
153: saveSecondClassCollection(list);
154: }
155:
156: public void testBigDecimalCollection() throws Exception {
157: try {
158: ArrayList list = new ArrayList();
159: for (int i = 0; i < rnd(); i++)
160: list.add(randomBigDecimal());
161: saveSecondClassCollection(list);
162: } catch (AssertionFailedError e) {
163: bug(3, e, "Precision loss for BigDecimals");
164: }
165: }
166:
167: public void testIntegerCollection() throws Exception {
168: ArrayList list = new ArrayList();
169: for (int i = 0; i < rnd(); i++)
170: list.add(randomInt());
171: saveSecondClassCollection(list);
172: }
173:
174: public void testByteCollection() throws Exception {
175: ArrayList list = new ArrayList();
176: for (int i = 0; i < rnd(); i++)
177: list.add(randomByte());
178: saveSecondClassCollection(list);
179: }
180:
181: public void testBooleanCollection() throws Exception {
182: ArrayList list = new ArrayList();
183: for (int i = 0; i < rnd(); i++)
184: list.add(randomBoolean());
185: saveSecondClassCollection(list, true);
186: }
187:
188: public void testFloatCollection() throws Exception {
189: try {
190: ArrayList list = new ArrayList();
191: for (int i = 0; i < rnd(); i++)
192: list.add(randomFloat());
193: saveSecondClassCollection(list);
194: } catch (AssertionFailedError afe) {
195: bug(3, afe, "Loss of BigDecimal precision");
196: }
197: }
198:
199: public void testDoubleCollection() throws Exception {
200: try {
201: ArrayList list = new ArrayList();
202: for (int i = 0; i < rnd(); i++)
203: list.add(randomDouble());
204: saveSecondClassCollection(list);
205: } catch (AssertionFailedError afe) {
206: bug(3, afe, "Loss of BigDecimal precision");
207: }
208: }
209:
210: public void testDateCollection() throws Exception {
211: ArrayList list = new ArrayList();
212: for (int i = 0; i < rnd(); i++)
213: list.add(randomDate());
214:
215: list.add(new Date(472246800000L));
216:
217: saveSecondClassCollection(list);
218: }
219:
220: public void testBigDecimalBigIntegerMap() throws Exception {
221: try {
222: HashMap map = new HashMap();
223: for (int i = 0; i < rnd(); i++)
224: map.put(randomBigDecimal(), randomBigInteger());
225: saveSecondClassMap(map);
226: } catch (AssertionFailedError e) {
227: bug(3, e, "Precision loss for BigDecimals");
228: }
229: }
230:
231: public void testStrIntMap() throws Exception {
232: HashMap map = new HashMap();
233: for (int i = 0; i < rnd(); i++)
234: map.put(randomString(), randomInt());
235: saveSecondClassMap(map);
236: }
237:
238: public void testIntLongMap() throws Exception {
239: HashMap map = new HashMap();
240: for (int i = 0; i < rnd(); i++)
241: map.put(randomInt(), randomLong());
242: try {
243: saveSecondClassMap(map);
244: } catch (AssertionFailedError afe) {
245: bug(AbstractTestCase.Platform.EMPRESS, 889, afe,
246: "Empress cannot store large long values");
247: }
248: }
249:
250: public void testFloatByteMap() throws Exception {
251: try {
252: HashMap map = new HashMap();
253: for (int i = 0; i < rnd(); i++)
254: map.put(randomFloat(), randomByte());
255: saveSecondClassMap(map);
256: } catch (AssertionFailedError afe) {
257: bug(3, afe, "Loss of BigDecimal precision");
258: }
259: }
260:
261: public void testByteDoubleMap() throws Exception {
262: try {
263: HashMap map = new HashMap();
264: for (int i = 0; i < rnd(); i++)
265: map.put(randomByte(), randomDouble());
266: saveSecondClassMap(map);
267: } catch (AssertionFailedError afe) {
268: bug(3, afe, "Loss of BigDecimal precision");
269: }
270: }
271:
272: public void testDoubleCharMap() throws Exception {
273: try {
274: HashMap map = new HashMap();
275: for (int i = 0; i < rnd(); i++)
276: map.put(randomDouble(), randomChar());
277: saveSecondClassMap(map);
278: } catch (AssertionFailedError afe) {
279: bug(3, afe, "Loss of BigDecimal precision");
280: }
281: }
282:
283: public void testCharBooleanMap() throws Exception {
284: HashMap map = new HashMap();
285: for (int i = 0; i < rnd(); i++)
286: map.put(randomChar(), randomBoolean());
287: saveSecondClassMap(map);
288: }
289:
290: public void testDateStrMap() throws Exception {
291: HashMap map = new HashMap();
292: for (int i = 0; i < rnd(); i++)
293: map.put(randomDate(), randomString());
294:
295: map.put(new Date(472246800000L),
296: "PostgreSQL ain't gonna like this date");
297: assertNotNull("map is null testDateStrMap", map);
298: saveSecondClassMap(map);
299: }
300:
301: private void saveSecondClassMap(HashMap map) throws Exception {
302: try {
303: saveSecondClassMapInternal(map);
304: } finally {
305: commit();
306: }
307: }
308:
309: private void commit() {
310: try {
311: assertNotNull(pm);
312:
313: // EntityTransaction trans = pm.getTransaction();
314: // if (trans != null && trans.isActive()) {
315: // trans.commit();
316: endTx(pm);
317: } catch (Exception e) {
318: e.printStackTrace();
319: }
320: }
321:
322: private void begin() {
323: commit(); // make sure we are clean
324:
325: // get a fresh PM
326: pm = getPM(true, false);
327: startTx(pm);
328: }
329:
330: /**
331: * Save the specified map as a second-class object and validate
332: * its contents by reading back in the object and comparing
333: * all the values to the original. Furthermore, this method
334: * deletes each element of both maps in turn and re-validates
335: * each time to make sure updating of the map is working correctly.
336: */
337: private void saveSecondClassMapInternal(HashMap map)
338: throws Exception {
339: begin();
340: SCOTest test = new SCOTest();
341: pm.persist(test);
342: int testID = test.getId();
343: assertNotNull("Passed Map is null", map);
344: Map smap = setGetMap(test, map, true);
345: assertNotNull("Map is null in setGetMap", smap);
346: commit();
347:
348: for (Iterator mapKey = ((HashMap) map.clone()).keySet()
349: .iterator(); mapKey.hasNext();) {
350: Object keyToDelete = mapKey.next();
351:
352: begin();
353: SCOTest retrievedObject = (SCOTest) pm.find(SCOTest.class,
354: testID);
355:
356: assertNotNull(
357: "retrievedObject Obj is null - saveSecondClassMapInternal",
358: retrievedObject);
359:
360: Map retrievedMap = setGetMap(retrievedObject, map, false);
361:
362: assertNotNull(
363: "retrievedMap Obj is null - saveSecondClassMapInternal",
364: retrievedMap);
365:
366: assertTrue(map.size() != 0);
367: assertEquals(map.size(), retrievedMap.size());
368:
369: assertTrue("Incompatible types", map.keySet().iterator()
370: .next().getClass().isAssignableFrom(
371: retrievedMap.keySet().iterator().next()
372: .getClass()));
373:
374: // check to make sure all the keys match up to the appropriate
375: // values.
376: for (Iterator i = map.keySet().iterator(); i.hasNext();) {
377: Object key = i.next();
378: assertTrue(key != null);
379: assertTrue(map.get(key) != null);
380: if (key.getClass() == Date.class
381: && retrievedMap.get(key) == null) {
382: getLog().trace("Time: " + (((Date) key).getTime()));
383: getLog()
384: .trace(
385: "List: "
386: + dumpDates(retrievedMap
387: .keySet()));
388:
389: /*
390: bug (6, "Dates lose precision in some data stores "
391: + "(" + (((Date)key).getTime ()) + ","
392: + "[" + dumpDates (retrievedMap.keySet ()) + "])");
393: */
394: }
395: if ((key.getClass() == Double.class
396: || key.getClass() == Float.class || key
397: .getClass() == BigDecimal.class)
398: && retrievedMap.get(key) == null) {
399: /*
400: bug (3, "Doubles and Floats "
401: + " lose precision in some data stores");
402: */
403: }
404:
405: assertTrue(
406: "The original map contained the object (class="
407: + key.getClass().getName() + ", value="
408: + key.toString()
409: + "), but that object was null "
410: + "in the map that was retrieved "
411: + dump(retrievedMap.keySet()) + ".",
412: retrievedMap.get(key) != null);
413: assertClassAndValueEquals(map.get(key), retrievedMap
414: .get(key));
415: }
416:
417: // now delete the first key in both maps, and make sure
418: // thinks are still OK.
419: map.remove(keyToDelete);
420: retrievedMap.remove(keyToDelete);
421: }
422: }
423:
424: private void saveSecondClassCollection(ArrayList collection)
425: throws Exception {
426: saveSecondClassCollection(collection, false);
427: }
428:
429: private void saveSecondClassCollection(ArrayList collection,
430: boolean useCustomCollator) throws Exception {
431: try {
432: saveSecondClassCollectionInternal(collection,
433: useCustomCollator);
434: } finally {
435: commit();
436: }
437: }
438:
439: private void saveSecondClassCollectionInternal(
440: ArrayList collection, boolean useCustomCollator)
441: throws Exception {
442: Object elementToDelete = null;
443:
444: if (useCustomCollator)
445: Collections.sort(collection, new CollectionSorter());
446: else
447: Collections.sort(collection);
448:
449: OpenJPAEntityManager pm1 = getPM();
450: startTx(pm1);
451:
452: SCOTest test = new SCOTest();
453: pm1.persist(test);
454: int testID = test.getId();
455:
456: Collection storedCollection = setGetCollection(test,
457: (Collection) ((ArrayList) collection).clone(), true);
458:
459: assertNotNull("retrieved storedCollection is null",
460: storedCollection);
461:
462: // make sure the pre-commit collections are identical!
463: assertEquals("Pre-commit collections were not equal: "
464: + newline + dump(collection) + newline + "!=" + newline
465: + dump(storedCollection), collection.size(),
466: storedCollection.size());
467:
468: endTx(pm1);
469:
470: int deletionIndex = 0;
471:
472: OpenJPAEntityManager pm2 = getPM();
473:
474: while (collection.size() > 0) {
475: deletionIndex++;
476:
477: startTx(pm2);
478: SCOTest retrievedObject = (SCOTest) pm2.find(SCOTest.class,
479: testID);
480:
481: assertNotNull(
482: "retrieved obj is null saveSecondClassCollectionInternal",
483: retrievedObject);
484:
485: Collection identityCollection = new LinkedList(collection);
486: assertNotNull(
487: "identityCollection is null saveSecondClassCollectionInternal",
488: identityCollection);
489: Collection retrievedCollection = setGetCollection(
490: retrievedObject, identityCollection, false);
491:
492: assertNotNull(
493: "retrievedCollection is null saveSecondClassCollectionInternal",
494: retrievedCollection);
495:
496: validateCollection(retrievedCollection);
497:
498: assertNotNull(retrievedCollection);
499: assertTrue(collection.size() != 0);
500:
501: assertEquals(
502: "Retreived collection does not match original "
503: + "after the " + deletionIndex
504: + "th deletion (" + elementToDelete + "): "
505: + newline + dump(collection) + newline
506: + "!=" + newline
507: + dump(retrievedCollection) + newline,
508: collection.size(), retrievedCollection.size());
509:
510: /*
511: try
512: {
513: assertEquals (collection.size retrievedCollection.size ());
514: } catch (AssertionFailedError afe) {
515: bug (AbstractTestCase.Platform.SQLSERVER, 2, afe, "Second-class collections"
516: + " are not being retrieved correctly");
517: }
518: */
519:
520: // make sure the classes of the keys are the same.
521: Iterator ci = collection.iterator();
522: Object co = collection.iterator().next();
523:
524: Iterator rci = retrievedCollection.iterator();
525: Object rco = retrievedCollection.iterator().next();
526:
527: assertNotNull(co);
528: assertNotNull(rco);
529: assertEquals(co.getClass(), rco.getClass());
530:
531: List sortedRetreivedCollection = new ArrayList(
532: retrievedCollection);
533:
534: if (useCustomCollator)
535: Collections.sort(sortedRetreivedCollection,
536: new CollectionSorter());
537: else
538: Collections.sort(sortedRetreivedCollection);
539:
540: // make sure the collection is OK
541: for (Iterator i = collection.iterator(), j = sortedRetreivedCollection
542: .iterator(); i.hasNext() && j.hasNext();) {
543: assertClassAndValueEquals(i.next(), j.next());
544: }
545:
546: elementToDelete = collection.iterator().next();
547: if (!(collection.remove(elementToDelete)))
548: fail("Could not delete element " + "(<"
549: + elementToDelete.getClass().getName() + ">"
550: + elementToDelete + ") " + "from "
551: + dump(collection));
552:
553: if (!(retrievedCollection.remove(elementToDelete)))
554: fail("Could not delete element (" + elementToDelete
555: + ") " + "from " + dump(retrievedCollection));
556:
557: endTx(pm2);
558: }
559: }
560:
561: private void assertClassAndValueEquals(Object o1, Object o2) {
562: assertTrue("First object was null", o1 != null);
563: assertTrue("Second object was null", o2 != null);
564:
565: assertTrue("Types did not match (class1="
566: + o1.getClass().getName() + ", class2="
567: + o2.getClass().getName() + ")", o1.getClass()
568: .isAssignableFrom(o2.getClass()));
569:
570: // floats and doubles are a little special: we only
571: // compare them to a certain precision, after which
572: // we give up.
573: /*
574: if (o1 instanceof Double)
575: assertEquals (((Double)o1).doubleValue (),
576: ((Double)o2).doubleValue (),
577: DOUBLE_PRECISION);
578: else if (o1 instanceof Float)
579: assertEquals (((Float)o1).floatValue (),
580: ((Float)o2).floatValue (),
581: FLOAT_PRECISION);
582: else if (o1 instanceof BigDecimal)
583: // BigDecimal equalist is a little special: see
584: // JDORuntimeTestCase.assertEquals(BigDecimal,BigDecimal)
585: assertEquals ("BigDecimal did not match",
586: (BigDecimal)o1, (BigDecimal)o2);
587: else
588: */
589: assertEquals("Object did not match (class1="
590: + o1.getClass().getName() + ", class2="
591: + o2.getClass().getName() + ")", o1, o2);
592: }
593:
594: private String dump(Collection coll) {
595: List list = new LinkedList(coll);
596: try {
597: Collections.sort(list);
598: } catch (RuntimeException e) {
599:
600: }
601:
602: StringBuffer buf = new StringBuffer().append("[").append(
603: "(size=").append(list.size()).append(")");
604:
605: Iterator it = list.iterator();
606: if (it.hasNext())
607: buf
608: .append("<class=" + it.next().getClass().getName()
609: + ">");
610:
611: for (Iterator i = list.iterator(); i.hasNext();)
612: buf.append(i.next()).append(i.hasNext() ? "," : "");
613:
614: return buf.append("]").toString();
615: }
616:
617: private String dumpDates(Collection coll) {
618: StringBuffer buf = new StringBuffer();
619: for (Iterator i = coll.iterator(); i.hasNext();)
620: buf.append(((Date) i.next()).getTime()).append(
621: i.hasNext() ? "," : "");
622:
623: return buf.toString();
624: }
625:
626: /**
627: * Generic setter/getter for setting the maps purposes.
628: */
629: private Map setGetMap(SCOTest test, HashMap map, boolean doSet) {
630: if (map == null)
631: return null;
632:
633: Object key = map.keySet().iterator().next();
634: Object val = map.get(key);
635:
636: if (key instanceof Date && val instanceof String) {
637: if (doSet)
638: test.setDateStrMap(map);
639: return test.getDateStrMap();
640: } else if (key instanceof Character && val instanceof Boolean) {
641: if (doSet)
642: test.setCharBooleanMap(map);
643: return test.getCharBooleanMap();
644: } else if (key instanceof Double && val instanceof Character) {
645: if (doSet)
646: test.setDoubleCharMap(map);
647: return test.getDoubleCharMap();
648: } else if (key instanceof Byte && val instanceof Double) {
649: if (doSet)
650: test.setByteDoubleMap(map);
651: return test.getByteDoubleMap();
652: } else if (key instanceof Float && val instanceof Byte) {
653: if (doSet)
654: test.setFloatByteMap(map);
655: return test.getFloatByteMap();
656: } else if (key instanceof Long && val instanceof Float) {
657: if (doSet)
658: test.setLongFloatMap(map);
659: return test.getLongFloatMap();
660: } else if (key instanceof Integer && val instanceof Long) {
661: if (doSet)
662: test.setIntLongMap(map);
663: return test.getIntLongMap();
664: } else if (key instanceof String && val instanceof Integer) {
665: if (doSet)
666: test.setStrIntMap(map);
667: return test.getStrIntMap();
668: } else if (key instanceof BigDecimal
669: && val instanceof BigInteger) {
670: if (doSet)
671: test.setBigDecimalBigIntegerMap(map);
672: return test.getBigDecimalBigIntegerMap();
673: }
674:
675: fail("Unknown map type");
676: return null;
677: }
678:
679: /**
680: * Generic setter/getter for setting the collections purposes.
681: */
682: private Collection setGetCollection(SCOTest test,
683: Collection collection, boolean doSet) {
684: if (collection == null)
685: return null;
686:
687: Object first = collection.iterator().next();
688:
689: if (first instanceof BigInteger) {
690: if (doSet)
691: test.setCBigInteger(collection);
692: return test.getCBigInteger();
693: } else if (first instanceof BigDecimal) {
694: if (doSet)
695: test.setCBigDecimal(collection);
696: return test.getCBigDecimal();
697: } else if (first instanceof Date) {
698: if (doSet)
699: test.setCDate(collection);
700: return test.getCDate();
701: } else if (first instanceof Character) {
702: if (doSet)
703: test.setCCharacter(collection);
704: return test.getCCharacter();
705: } else if (first instanceof Double) {
706: if (doSet)
707: test.setCDouble(collection);
708: return test.getCDouble();
709: } else if (first instanceof Byte) {
710: if (doSet)
711: test.setCByte(collection);
712: return test.getCByte();
713: } else if (first instanceof Float) {
714: if (doSet)
715: test.setCFloat(collection);
716: return test.getCFloat();
717: } else if (first instanceof Long) {
718: if (doSet)
719: test.setCLong(collection);
720: return test.getCLong();
721: } else if (first instanceof Integer) {
722: if (doSet)
723: test.setCInteger(collection);
724: return test.getCInteger();
725: } else if (first instanceof String) {
726: if (doSet)
727: test.setCString(collection);
728: return test.getCString();
729: } else if (first instanceof Short) {
730: if (doSet)
731: test.setCShort(collection);
732: return test.getCShort();
733: } else if (first instanceof Boolean) {
734: if (doSet)
735: test.setCBoolean(collection);
736: return test.getCBoolean();
737: }
738:
739: fail("Unknown collection type");
740: return null;
741: }
742:
743: /**
744: * A simple sorter that should always return the same sort order.
745: * The only reason we need ti use this, instead of relying on the
746: * natural order in Collections.sort is that there seems to be
747: * a bug somewhere that prevents sorting on collections of Boolean
748: * objects.
749: */
750: public static class CollectionSorter implements Comparator {
751:
752: private Collator collator = Collator.getInstance();
753:
754: public CollectionSorter() {
755:
756: }
757:
758: public int compare(Object o1, Object o2) {
759: if (o1 != null && !(o1 instanceof Boolean))
760: return collator.compare(o1, o2);
761:
762: return collator.compare(o1.toString(), o2.toString());
763: }
764: }
765: }
|