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.lang.reflect.InvocationHandler;
21  import java.lang.reflect.Method;
22  import java.lang.reflect.Proxy;
23  
24  import javax.jdo.spi.PersistenceCapable;
25  
26  import javax.jdo.util.AbstractTest;
27  import javax.jdo.util.BatchTestRunner;
28  
29  /*
30   * ObjectStateTest.java
31   * This test class verifies the proper returned ObjectState for
32   * each life cycle state. See Table 3 in section 7.4.
33   *
34   * @since 2.1
35   */
36  public class ObjectStateTest extends AbstractTest {
37  
38      static final int PERSISTENT = 1;
39      static final int TRANSACTIONAL = 2;
40      static final int DIRTY = 4;
41      static final int NEW = 8;
42      static final int DELETED = 16;
43      static final int DETACHED = 32;
44  
45      private static final Method jdoIsPersistent = getDeclaredMethod(
46              PersistenceCapable.class,
47              "jdoIsPersistent",
48              null);
49      private static final Method jdoIsTransactional = getDeclaredMethod(
50              PersistenceCapable.class,
51              "jdoIsTransactional",
52              null);
53      private static final Method jdoIsDirty = getDeclaredMethod(
54              PersistenceCapable.class,
55              "jdoIsDirty",
56              null);
57      private static final Method jdoIsNew = getDeclaredMethod(
58              PersistenceCapable.class,
59              "jdoIsNew",
60              null);
61      private static final Method jdoIsDeleted = getDeclaredMethod(
62              PersistenceCapable.class,
63              "jdoIsDeleted",
64              null);
65      private static final Method jdoIsDetached = getDeclaredMethod(
66              PersistenceCapable.class,
67              "jdoIsDetached",
68              null);
69  
70      /** */
71      public static void main(String args[]) {
72          BatchTestRunner.run(ObjectStateTest.class);
73      }
74  
75      public void testNull() {
76          PersistenceCapable mock = null;
77          assertObjectState("null", null, mock);
78      }
79  
80      public void testTransient() {
81          PersistenceCapable mock = newMock(0);
82          assertObjectState("transient", ObjectState.TRANSIENT, mock);
83      }
84  
85      public void testTransientClean() {
86          PersistenceCapable mock = newMock(TRANSACTIONAL);
87          assertObjectState("transient-clean", ObjectState.TRANSIENT_CLEAN, mock);
88      }
89  
90      public void testTransientDirty() {
91          PersistenceCapable mock = newMock(TRANSACTIONAL+DIRTY);
92          assertObjectState("transient-dirty", ObjectState.TRANSIENT_DIRTY, mock);
93      }
94  
95      public void testPersistentNew() {
96          PersistenceCapable mock = newMock(PERSISTENT+TRANSACTIONAL+NEW+DIRTY);
97          assertObjectState("persistent-new", ObjectState.PERSISTENT_NEW, mock);
98      }
99  
100     public void testPersistentNontransactional() {
101         PersistenceCapable mock = newMock(PERSISTENT);
102         assertObjectState("persistent-nontransactional", ObjectState.HOLLOW_PERSISTENT_NONTRANSACTIONAL, mock);
103     }
104 
105     public void testPersistentNontransactionalDirty() {
106         PersistenceCapable mock = newMock(PERSISTENT+DIRTY);
107         assertObjectState("persistent-nontransactional-dirty", ObjectState.PERSISTENT_NONTRANSACTIONAL_DIRTY, mock);
108     }
109 
110     public void testPersistentClean() {
111         PersistenceCapable mock = newMock(PERSISTENT+TRANSACTIONAL);
112         assertObjectState("persistent-clean", ObjectState.PERSISTENT_CLEAN, mock);
113     }
114 
115     public void testPersistentDirty() {
116         PersistenceCapable mock = newMock(PERSISTENT+TRANSACTIONAL+DIRTY);
117         assertObjectState("persistent-dirty", ObjectState.PERSISTENT_DIRTY, mock);
118     }
119 
120     public void testPersistentDeleted() {
121         PersistenceCapable mock = newMock(PERSISTENT+TRANSACTIONAL+DIRTY+DELETED);
122         assertObjectState("persistent-deleted", ObjectState.PERSISTENT_DELETED, mock);
123     }
124 
125     public void testPersistentNewDeleted() {
126         PersistenceCapable mock = newMock(PERSISTENT+TRANSACTIONAL+NEW+DIRTY+DELETED);
127         assertObjectState("persistent-new-deleted", ObjectState.PERSISTENT_NEW_DELETED, mock);
128     }
129 
130     public void testDetachedClean() {
131         PersistenceCapable mock = newMock(DETACHED);
132         assertObjectState("detached-clean", ObjectState.DETACHED_CLEAN, mock);
133     }
134 
135     public void testDetachedDirty() {
136         PersistenceCapable mock = newMock(DETACHED+DIRTY);
137         assertObjectState("detached-dirty", ObjectState.DETACHED_DIRTY, mock);
138     }
139 
140     private void assertObjectState(String string, 
141             ObjectState expected, 
142             PersistenceCapable pc) {
143         ObjectState actual = JDOHelper.getObjectState(pc);
144         // test for == here because enums should be singleton
145         if (actual == expected)
146             return;
147         fail("ObjectState failure for " + string + NL +
148                 "expected: " + expected +
149                 ", actual: " + actual);
150     }
151 
152     /**
153      * Construct a new mock instance of PersistenceCapable that responds
154      * to methods jdoIsXXX by returning the bit value of its constructor
155      * masked with the value of XXX.
156      * @param i the sum of bit masks representing the life cycle state
157      * @return a mock instance of PersistenceCapable
158      */
159     private PersistenceCapable newMock(final int i) {
160         return (PersistenceCapable)
161             Proxy.newProxyInstance(
162                 PersistenceCapable.class.getClassLoader(),
163                 new Class[] {PersistenceCapable.class},
164                 new MockInvocationHandler(i));
165     }
166 
167     private class MockInvocationHandler implements InvocationHandler {
168         /** States is the sum of all life cycle interrogatives.
169          */
170         private int states;
171 
172         /**
173          * Constructs an invocation handler with the specified bit fields set
174          * according to the sum of values of PERSISTENT, TRANSACTIONAL, DIRTY, 
175          * NEW, DELETED, and DETACHED.
176          * @param i the bit field values for the life cycle interrogatives
177          */
178         private MockInvocationHandler(int i) {
179             states = i;
180         }
181 
182         /**
183          * 
184          * @param object the PersistenceCapable instance
185          * @param method the method being invoked
186          * @param parameters parameters (should be null)
187          * @throws java.lang.Throwable unused
188          * @return for jdoIsXXX methods only, returns whether the 
189          * bit field selected by the method is set in the
190          * mock handler
191          */
192         public Object invoke(Object object, Method method, Object[] parameters) 
193                 throws Throwable {
194             if (method.equals(jdoIsPersistent)) {
195                 return (0 != (states & PERSISTENT));
196             }
197             if (method.equals(jdoIsTransactional)) {
198                 return (0 != (states & TRANSACTIONAL));
199             }
200             if (method.equals(jdoIsDirty)) {
201                 return (0 != (states & DIRTY));
202             }
203             if (method.equals(jdoIsNew)) {
204                 return (0 != (states & NEW));
205             }
206             if (method.equals(jdoIsDeleted)) {
207                 return (0 != (states & DELETED));
208             }
209             if (method.equals(jdoIsDetached)) {
210                 return (0 != (states & DETACHED));
211             }
212             fail("Unexpected method called: " + method.getName());
213             return Boolean.FALSE; // not reached
214         }
215     }
216 
217     private static Method getDeclaredMethod
218             (Class clazz, String methodName, Class[] parameters) {
219         try {
220             Method result = clazz.getDeclaredMethod(methodName, parameters);
221             return result;
222         } catch (Exception ex) {
223             // human-readable class.methodName(parameter[,parameter])
224             StringBuffer sb = new StringBuffer(methodName);
225             String delimiter = "(";
226             for (Class parameter: parameters) {
227                 sb.append(delimiter);
228                 sb.append(parameter.getName());
229                 delimiter = ",";
230             }
231             sb.append(")");
232             throw new RuntimeException
233                 ("getDeclaredMethod for " + clazz.getName() + "." +
234                     sb + " threw..." + ex);
235         }
236     }
237 }