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