1 /*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002, 2014 Oracle and/or its affiliates. All rights reserved. 5 * 6 */ 7 8 package com.sleepycat.persist.test; 9 10 import static org.junit.Assert.assertEquals; 11 import static org.junit.Assert.assertNotNull; 12 13 import java.io.File; 14 import java.io.IOException; 15 16 import org.junit.After; 17 import org.junit.Before; 18 import org.junit.Test; 19 20 import com.sleepycat.je.DatabaseException; 21 import com.sleepycat.je.Environment; 22 import com.sleepycat.je.EnvironmentConfig; 23 import com.sleepycat.je.util.TestUtils; 24 import com.sleepycat.persist.EntityStore; 25 import com.sleepycat.persist.PrimaryIndex; 26 import com.sleepycat.persist.StoreConfig; 27 import com.sleepycat.persist.evolve.Mutations; 28 import com.sleepycat.persist.evolve.Renamer; 29 import com.sleepycat.persist.model.Entity; 30 import com.sleepycat.persist.model.Persistent; 31 import com.sleepycat.persist.model.PrimaryKey; 32 import com.sleepycat.util.test.SharedTestUtils; 33 import com.sleepycat.util.test.TestBase; 34 import com.sleepycat.util.test.TestEnv; 35 36 /** 37 * Test a bug fix for an evolution error when a class is evolved and then 38 * changed back to its original version. Say there are two versions of a 39 * class A1 and A2 in the catalog, plus a new version A3 of the class. The 40 * problem occurs when A2 is different than A3 and must be evolved, but A1 41 * happens to be identical to A3 and no evolution is needed. In that case, A3 42 * was never added to the format list in the catalog (never assigned a format 43 * ID), but was still used as the "latest version" of A2. This caused all 44 * kinds of trouble since the class catalog was effectively corrupt. [#16467] 45 * 46 * We reproduce this scenario using type Other[], which is represented using 47 * ArrayObjectFormat internally. By renaming Other to Other2, and then back to 48 * Other, we create the scenario described above for the array format itself. 49 * Array formats are only evolved if their component class name has changed 50 * (see ArrayObjectFormat.evolve). 51 * 52 * A modified version of this program was run manually with JE 3.3.71 to 53 * produce a log, which is the result of the testSetup() test. The sole log 54 * file was renamed from 00000000.jdb to DevolutionTest.jdb and added to CVS 55 * in this directory. When that log file is opened here, the bug is 56 * reproduced. 57 * 58 * This test should be excluded from the BDB build because it uses a stored JE 59 * log file and it tests a fix for a bug that was never present in BDB. 60 * 61 * @author Mark Hayes 62 */ 63 public class DevolutionTest extends TestBase { 64 65 private static final String STORE_NAME = "test"; 66 67 private File envHome; 68 private Environment env; 69 70 @Before setUp()71 public void setUp() 72 throws Exception { 73 74 envHome = SharedTestUtils.getTestDir(); 75 super.setUp(); 76 } 77 78 @After tearDown()79 public void tearDown() { 80 if (env != null) { 81 try { 82 env.close(); 83 } catch (Throwable e) { 84 System.out.println("During tearDown: " + e); 85 } 86 } 87 envHome = null; 88 env = null; 89 } 90 open()91 private EntityStore open() 92 throws DatabaseException { 93 94 EnvironmentConfig envConfig = TestEnv.BDB.getConfig(); 95 envConfig.setAllowCreate(true); 96 env = new Environment(envHome, envConfig); 97 98 /* 99 * When version 0 of Other is used, no renamer is configured. When 100 * version 1 is used, a renamer from Other version 0 to Other2 is used. 101 * For version 2, the current version, a renamer from Other2 version 1 102 * to Other is used. 103 */ 104 String clsName = getClass().getName() + "$Other"; 105 Renamer renamer = new Renamer(clsName + '2', 1, clsName); 106 Mutations mutations = new Mutations(); 107 mutations.addRenamer(renamer); 108 109 StoreConfig storeConfig = new StoreConfig(); 110 storeConfig.setAllowCreate(true); 111 storeConfig.setMutations(mutations); 112 return new EntityStore(env, "foo", storeConfig); 113 } 114 close(EntityStore store)115 private void close(EntityStore store) 116 throws DatabaseException { 117 118 store.close(); 119 env.close(); 120 env = null; 121 } 122 123 @Test testDevolution()124 public void testDevolution() 125 throws DatabaseException, IOException { 126 127 /* Copy log file resource to log file zero. */ 128 TestUtils.loadLog(getClass(), "DevolutionTest.jdb", envHome); 129 130 EntityStore store = open(); 131 132 PrimaryIndex<Long, MyEntity> index = 133 store.getPrimaryIndex(Long.class, MyEntity.class); 134 135 MyEntity entity = index.get(1L); 136 assertNotNull(entity); 137 assertEquals(123, entity.b); 138 139 close(store); 140 } 141 xtestSetup()142 public void xtestSetup() 143 throws DatabaseException { 144 145 EntityStore store = open(); 146 147 PrimaryIndex<Long, MyEntity> index = 148 store.getPrimaryIndex(Long.class, MyEntity.class); 149 150 MyEntity entity = new MyEntity(); 151 entity.key = 1L; 152 entity.b = 123; 153 index.put(entity); 154 155 close(store); 156 } 157 158 /** 159 * This class name is changed from Other to Other2 in version 1 and back to 160 * Other in the version 2. testSetup is executed for versions 0 and 1, 161 * which evolves the format. testDevolution is run with version 2. 162 */ 163 @Persistent(version=2) 164 static class Other { 165 } 166 167 @Entity(version=0) 168 static class MyEntity { 169 170 @PrimaryKey 171 long key; 172 173 Other[] a; 174 175 int b; 176 MyEntity()177 private MyEntity() {} 178 } 179 } 180