blob: 217c87482484a672fc7440b11b08f047dd372265 [file] [log] [blame]
/*
* TestSecondClassValues.java
*
* Created on October 13, 2006, 5:29 PM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openjpa.persistence.kernel;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.openjpa.persistence.OpenJPAEntityManager;
import org.apache.openjpa.persistence.common.utils.AbstractTestCase;
import org.apache.openjpa.persistence.kernel.common.apps.SCOTest;
import junit.framework.AssertionFailedError;
public class TestSecondClassValues extends BaseKernelTest {
public static double DOUBLE_PRECISION = 0.0001D;
public static float FLOAT_PRECISION = 0.0001F;
// mprudhom: use optimistic so we don't hang on some databases
private OpenJPAEntityManager pm;
private String newline = System.getProperty("line.separator");
/**
* Creates a new instance of TestSecondClassValues
*/
public TestSecondClassValues() {
}
public TestSecondClassValues(String name) {
super(name);
}
@Override
public void setUp() {
pm = getPM(true, false);
}
private int rnd() {
return ((int) (Math.random() * 20)) + 5;
}
public void testMapDeletion() {
OpenJPAEntityManager pm;
pm = getPM();
startTx(pm);
SCOTest test = new SCOTest();
pm.persist(test);
Map map = new HashMap();
map.put("foo", 1);
map.put("bar", 2);
for (int i = 0; i < 10; i++)
map.put("baz#" + i, i);
test.setStrIntMap(map);
Object id = pm.getObjectId(test);
endTx(pm);
startTx(pm);
test = (SCOTest) pm.find(SCOTest.class, id);
assertNotNull(test);
map = test.getStrIntMap();
assertEquals(12, map.size());
assertEquals(1, map.get("foo"));
assertEquals(2, map.get("bar"));
map.remove("bar");
endTx(pm);
startTx(pm);
test = (SCOTest) pm.find(SCOTest.class, id);
assertNotNull(test);
map = test.getStrIntMap();
assertEquals(11, map.size());
assertEquals(1, map.get("foo"));
assertTrue(map.get("bar") == null);
map.clear();
endTx(pm);
startTx(pm);
test = (SCOTest) pm.find(SCOTest.class, id);
assertNotNull(test);
map = test.getStrIntMap();
assertEquals(0, map.size());
endTx(pm);
}
public void testStringCollection()
throws Exception {
ArrayList list = new ArrayList();
for (int i = 0; i < rnd(); i++)
list.add(randomString());
saveSecondClassCollection(list);
}
public void testLongCollection()
throws Exception {
ArrayList list = new ArrayList();
for (int i = 0; i < rnd(); i++)
list.add(randomLong());
try {
saveSecondClassCollection(list);
} catch (AssertionFailedError afe) {
bug(AbstractTestCase.Platform.EMPRESS, 889, afe,
"Empress cannot store large long values");
}
}
public void testShortCollection()
throws Exception {
ArrayList list = new ArrayList();
for (int i = 0; i < rnd(); i++)
list.add(randomShort());
saveSecondClassCollection(list);
}
public void testBigIntegerCollection()
throws Exception {
ArrayList list = new ArrayList();
for (int i = 0; i < rnd(); i++)
list.add(randomBigInteger());
saveSecondClassCollection(list);
}
public void testBigDecimalCollection()
throws Exception {
try {
ArrayList list = new ArrayList();
for (int i = 0; i < rnd(); i++)
list.add(randomBigDecimal());
saveSecondClassCollection(list);
} catch (AssertionFailedError e) {
bug(3, e, "Precision loss for BigDecimals");
}
}
public void testIntegerCollection()
throws Exception {
ArrayList list = new ArrayList();
for (int i = 0; i < rnd(); i++)
list.add(randomInt());
saveSecondClassCollection(list);
}
public void testByteCollection()
throws Exception {
ArrayList list = new ArrayList();
for (int i = 0; i < rnd(); i++)
list.add(randomByte());
saveSecondClassCollection(list);
}
public void testBooleanCollection()
throws Exception {
ArrayList list = new ArrayList();
for (int i = 0; i < rnd(); i++)
list.add(randomBoolean());
saveSecondClassCollection(list, true);
}
public void testFloatCollection()
throws Exception {
try {
ArrayList list = new ArrayList();
for (int i = 0; i < rnd(); i++)
list.add(randomFloat());
saveSecondClassCollection(list);
} catch (AssertionFailedError afe) {
bug(3, afe, "Loss of BigDecimal precision");
}
}
public void testDoubleCollection()
throws Exception {
try {
ArrayList list = new ArrayList();
for (int i = 0; i < rnd(); i++)
list.add(randomDouble());
saveSecondClassCollection(list);
} catch (AssertionFailedError afe) {
bug(3, afe, "Loss of BigDecimal precision");
}
}
public void testDateCollection()
throws Exception {
ArrayList list = new ArrayList();
for (int i = 0; i < rnd(); i++)
list.add(randomDate());
list.add(new Date(472246800000L));
saveSecondClassCollection(list);
}
public void testBigDecimalBigIntegerMap()
throws Exception {
try {
HashMap map = new HashMap();
for (int i = 0; i < rnd(); i++)
map.put(randomBigDecimal(), randomBigInteger());
saveSecondClassMap(map);
} catch (AssertionFailedError e) {
bug(3, e, "Precision loss for BigDecimals");
}
}
public void testStrIntMap()
throws Exception {
HashMap map = new HashMap();
for (int i = 0; i < rnd(); i++)
map.put(randomString(), randomInt());
saveSecondClassMap(map);
}
public void testIntLongMap() throws Exception {
HashMap map = new HashMap();
for (int i = 0; i < rnd(); i++)
map.put(randomInt(), randomLong());
try {
saveSecondClassMap(map);
}
catch (AssertionFailedError afe) {
bug(AbstractTestCase.Platform.EMPRESS, 889, afe,
"Empress cannot store large long values");
}
}
public void testFloatByteMap()
throws Exception {
try {
HashMap map = new HashMap();
for (int i = 0; i < rnd(); i++)
map.put(randomFloat(), randomByte());
saveSecondClassMap(map);
} catch (AssertionFailedError afe) {
bug(3, afe, "Loss of BigDecimal precision");
}
}
public void testByteDoubleMap()
throws Exception {
try {
HashMap map = new HashMap();
for (int i = 0; i < rnd(); i++)
map.put(randomByte(), randomDouble());
saveSecondClassMap(map);
} catch (AssertionFailedError afe) {
bug(3, afe, "Loss of BigDecimal precision");
}
}
public void testDoubleCharMap()
throws Exception {
try {
HashMap map = new HashMap();
for (int i = 0; i < rnd(); i++)
map.put(randomDouble(), randomChar());
saveSecondClassMap(map);
} catch (AssertionFailedError afe) {
bug(3, afe, "Loss of BigDecimal precision");
}
}
public void testCharBooleanMap()
throws Exception {
HashMap map = new HashMap();
for (int i = 0; i < rnd(); i++)
map.put(randomChar(), randomBoolean());
saveSecondClassMap(map);
}
public void testDateStrMap() throws Exception {
HashMap map = new HashMap();
for (int i = 0; i < rnd(); i++)
map.put(randomDate(), randomString());
map.put(new Date(472246800000L),
"PostgreSQL ain't gonna like this date");
assertNotNull("map is null testDateStrMap", map);
saveSecondClassMap(map);
}
private void saveSecondClassMap(HashMap map)
throws Exception {
try {
saveSecondClassMapInternal(map);
} finally {
commit();
}
}
private void commit() {
try {
assertNotNull(pm);
// EntityTransaction trans = pm.getTransaction();
// if (trans != null && trans.isActive()) {
// trans.commit();
endTx(pm);
}
catch (Exception e) {
e.printStackTrace();
}
}
private void begin() {
commit(); // make sure we are clean
// get a fresh PM
pm = getPM(true, false);
startTx(pm);
}
/**
* Save the specified map as a second-class object and validate
* its contents by reading back in the object and comparing
* all the values to the original. Furthermore, this method
* deletes each element of both maps in turn and re-validates
* each time to make sure updating of the map is working correctly.
*/
private void saveSecondClassMapInternal(HashMap map) throws Exception {
begin();
SCOTest test = new SCOTest();
pm.persist(test);
int testID = test.getId();
assertNotNull("Passed Map is null", map);
Map smap = setGetMap(test, map, true);
assertNotNull("Map is null in setGetMap", smap);
commit();
for (Object keyToDelete : ((HashMap) map.clone()).keySet()) {
begin();
SCOTest retrievedObject =
(SCOTest) pm.find(SCOTest.class, testID);
assertNotNull(
"retrievedObject Obj is null - saveSecondClassMapInternal",
retrievedObject);
Map retrievedMap = setGetMap(retrievedObject, map, false);
assertNotNull(
"retrievedMap Obj is null - saveSecondClassMapInternal",
retrievedMap);
assertTrue(map.size() != 0);
assertEquals(map.size(), retrievedMap.size());
assertTrue("Incompatible types", map.keySet().iterator().next().
getClass().isAssignableFrom(retrievedMap.keySet().
iterator().next().getClass()));
// check to make sure all the keys match up to the appropriate
// values.
for (Object key : map.keySet()) {
assertTrue(key != null);
assertTrue(map.get(key) != null);
if (key.getClass() == Date.class
&& retrievedMap.get(key) == null) {
getLog().trace("Time: "
+ (((Date) key).getTime()));
getLog().trace("List: "
+ dumpDates(retrievedMap.keySet()));
/*
bug (6, "Dates lose precision in some data stores "
+ "(" + (((Date)key).getTime ()) + ","
+ "[" + dumpDates (retrievedMap.keySet ()) + "])");
*/
}
if ((key.getClass() == Double.class ||
key.getClass() == Float.class ||
key.getClass() == BigDecimal.class)
&& retrievedMap.get(key) == null) {
/*
bug (3, "Doubles and Floats "
+ " lose precision in some data stores");
*/
}
assertTrue("The original map contained the object (class="
+ key.getClass().getName() + ", value="
+ key.toString() + "), but that object was null "
+ "in the map that was retrieved "
+ dump(retrievedMap.keySet()) + ".",
retrievedMap.get(key) != null);
assertClassAndValueEquals(
map.get(key), retrievedMap.get(key));
}
// now delete the first key in both maps, and make sure
// thinks are still OK.
map.remove(keyToDelete);
retrievedMap.remove(keyToDelete);
}
}
private void saveSecondClassCollection(ArrayList collection)
throws Exception {
saveSecondClassCollection(collection, false);
}
private void saveSecondClassCollection(ArrayList collection,
boolean useCustomCollator)
throws Exception {
try {
saveSecondClassCollectionInternal(collection, useCustomCollator);
} finally {
commit();
}
}
private void saveSecondClassCollectionInternal(ArrayList collection,
boolean useCustomCollator)
throws Exception {
Object elementToDelete = null;
if (useCustomCollator)
Collections.sort(collection,
new CollectionSorter());
else
Collections.sort(collection);
OpenJPAEntityManager pm1 = getPM();
startTx(pm1);
SCOTest test = new SCOTest();
pm1.persist(test);
int testID = test.getId();
Collection storedCollection = setGetCollection(test,
(Collection) ((ArrayList) collection).clone(), true);
assertNotNull("retrieved storedCollection is null", storedCollection);
// make sure the pre-commit collections are identical!
assertEquals("Pre-commit collections were not equal: " + newline
+ dump(collection) + newline + "!=" + newline
+ dump(storedCollection),
collection.size(), storedCollection.size());
endTx(pm1);
int deletionIndex = 0;
OpenJPAEntityManager pm2 = getPM();
while (collection.size() > 0) {
deletionIndex++;
startTx(pm2);
SCOTest retrievedObject =
(SCOTest) pm2.find(SCOTest.class, testID);
assertNotNull(
"retrieved obj is null saveSecondClassCollectionInternal",
retrievedObject);
Collection identityCollection = new LinkedList(collection);
assertNotNull(
"identityCollection is null saveSecondClassCollectionInternal",
identityCollection);
Collection retrievedCollection = setGetCollection(
retrievedObject, identityCollection, false);
assertNotNull(
"retrievedCollection is null saveSecondClassCollectionInternal",
retrievedCollection);
validateCollection(retrievedCollection);
assertNotNull(retrievedCollection);
assertTrue(collection.size() != 0);
assertEquals("Retreived collection does not match original "
+ "after the " + deletionIndex + "th deletion ("
+ elementToDelete + "): "
+ newline
+ dump(collection) + newline + "!=" + newline
+ dump(retrievedCollection) + newline,
collection.size(), retrievedCollection.size());
/*
try
{
assertEquals (collection.size retrievedCollection.size ());
} catch (AssertionFailedError afe) {
bug (AbstractTestCase.Platform.SQLSERVER, 2, afe,
"Second-class collections"
+ " are not being retrieved correctly");
}
*/
// make sure the classes of the keys are the same.
Iterator ci = collection.iterator();
Object co = collection.iterator().next();
Iterator rci = retrievedCollection.iterator();
Object rco = retrievedCollection.iterator().next();
assertNotNull(co);
assertNotNull(rco);
assertEquals(co.getClass(), rco.getClass());
List sortedRetreivedCollection =
new ArrayList(retrievedCollection);
if (useCustomCollator)
Collections.sort(sortedRetreivedCollection,
new CollectionSorter());
else
Collections.sort(sortedRetreivedCollection);
// make sure the collection is OK
for (Iterator i = collection.iterator(),
j = sortedRetreivedCollection.iterator();
i.hasNext() && j.hasNext();) {
assertClassAndValueEquals(i.next(), j.next());
}
elementToDelete = collection.iterator().next();
if (!(collection.remove(elementToDelete)))
fail("Could not delete element "
+ "(<" + elementToDelete.getClass().getName() + ">"
+ elementToDelete + ") "
+ "from " + dump(collection));
if (!(retrievedCollection.remove(elementToDelete)))
fail("Could not delete element (" + elementToDelete + ") "
+ "from " + dump(retrievedCollection));
endTx(pm2);
}
}
private void assertClassAndValueEquals(Object o1, Object o2) {
assertTrue("First object was null", o1 != null);
assertTrue("Second object was null", o2 != null);
assertTrue("Types did not match (class1="
+ o1.getClass().getName() + ", class2="
+ o2.getClass().getName() + ")",
o1.getClass().isAssignableFrom(o2.getClass()));
// floats and doubles are a little special: we only
// compare them to a certain precision, after which
// we give up.
/*
if (o1 instanceof Double)
assertEquals (((Double)o1).doubleValue (),
((Double)o2).doubleValue (),
DOUBLE_PRECISION);
else if (o1 instanceof Float)
assertEquals (((Float)o1).floatValue (),
((Float)o2).floatValue (),
FLOAT_PRECISION);
else if (o1 instanceof BigDecimal)
// BigDecimal equalist is a little special: see
// JDORuntimeTestCase.assertEquals(BigDecimal,BigDecimal)
assertEquals ("BigDecimal did not match",
(BigDecimal)o1, (BigDecimal)o2);
else
*/
assertEquals("Object did not match (class1="
+ o1.getClass().getName() + ", class2="
+ o2.getClass().getName() + ")",
o1, o2);
}
private String dump(Collection coll) {
List list = new LinkedList(coll);
try {
Collections.sort(list);
} catch (RuntimeException e) {
}
StringBuilder buf = new StringBuilder().append("[")
.append("(size=").append(list.size()).append(")");
Iterator it = list.iterator();
if (it.hasNext())
buf.append("<class=" + it.next().getClass().getName() + ">");
for (Iterator i = list.iterator(); i.hasNext();)
buf.append(i.next()).append(i.hasNext() ? "," : "");
return buf.append("]").toString();
}
private String dumpDates(Collection coll) {
StringBuilder buf = new StringBuilder();
for (Iterator i = coll.iterator(); i.hasNext();)
buf.append(((Date) i.next()).getTime()).append(
i.hasNext() ? "," : "");
return buf.toString();
}
/**
* Generic setter/getter for setting the maps purposes.
*/
private Map setGetMap(SCOTest test, HashMap map, boolean doSet) {
if (map == null)
return null;
Object key = map.keySet().iterator().next();
Object val = map.get(key);
if (key instanceof Date && val instanceof String) {
if (doSet)
test.setDateStrMap(map);
return test.getDateStrMap();
} else if (key instanceof Character && val instanceof Boolean) {
if (doSet)
test.setCharBooleanMap(map);
return test.getCharBooleanMap();
} else if (key instanceof Double && val instanceof Character) {
if (doSet)
test.setDoubleCharMap(map);
return test.getDoubleCharMap();
} else if (key instanceof Byte && val instanceof Double) {
if (doSet)
test.setByteDoubleMap(map);
return test.getByteDoubleMap();
} else if (key instanceof Float && val instanceof Byte) {
if (doSet)
test.setFloatByteMap(map);
return test.getFloatByteMap();
} else if (key instanceof Long && val instanceof Float) {
if (doSet)
test.setLongFloatMap(map);
return test.getLongFloatMap();
} else if (key instanceof Integer && val instanceof Long) {
if (doSet)
test.setIntLongMap(map);
return test.getIntLongMap();
} else if (key instanceof String && val instanceof Integer) {
if (doSet)
test.setStrIntMap(map);
return test.getStrIntMap();
} else if (key instanceof BigDecimal && val instanceof BigInteger) {
if (doSet)
test.setBigDecimalBigIntegerMap(map);
return test.getBigDecimalBigIntegerMap();
}
fail("Unknown map type");
return null;
}
/**
* Generic setter/getter for setting the collections purposes.
*/
private Collection setGetCollection(SCOTest test,
Collection collection, boolean doSet) {
if (collection == null)
return null;
Object first = collection.iterator().next();
if (first instanceof BigInteger) {
if (doSet)
test.setCBigInteger(collection);
return test.getCBigInteger();
} else if (first instanceof BigDecimal) {
if (doSet)
test.setCBigDecimal(collection);
return test.getCBigDecimal();
} else if (first instanceof Date) {
if (doSet)
test.setCDate(collection);
return test.getCDate();
} else if (first instanceof Character) {
if (doSet)
test.setCCharacter(collection);
return test.getCCharacter();
} else if (first instanceof Double) {
if (doSet)
test.setCDouble(collection);
return test.getCDouble();
} else if (first instanceof Byte) {
if (doSet)
test.setCByte(collection);
return test.getCByte();
} else if (first instanceof Float) {
if (doSet)
test.setCFloat(collection);
return test.getCFloat();
} else if (first instanceof Long) {
if (doSet)
test.setCLong(collection);
return test.getCLong();
} else if (first instanceof Integer) {
if (doSet)
test.setCInteger(collection);
return test.getCInteger();
} else if (first instanceof String) {
if (doSet)
test.setCString(collection);
return test.getCString();
} else if (first instanceof Short) {
if (doSet)
test.setCShort(collection);
return test.getCShort();
} else if (first instanceof Boolean) {
if (doSet)
test.setCBoolean(collection);
return test.getCBoolean();
}
fail("Unknown collection type");
return null;
}
/**
* A simple sorter that should always return the same sort order.
* The only reason we need ti use this, instead of relying on the
* natural order in Collections.sort is that there seems to be
* a bug somewhere that prevents sorting on collections of Boolean
* objects.
*/
public static class CollectionSorter
implements Comparator {
private Collator collator = Collator.getInstance();
public CollectionSorter() {
}
@Override
public int compare(Object o1, Object o2) {
if (o1 != null && !(o1 instanceof Boolean))
return collator.compare(o1, o2);
return collator.compare(o1.toString(), o2.toString());
}
}
}