1   /*
2    * Copyright 2005 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at 
7    * 
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software 
11   * distributed under the License is distributed on an "AS IS" BASIS, 
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License.
15   */
16  
17  package javax.jdo.spi;
18  
19  import java.lang.reflect.InvocationHandler;
20  import java.lang.reflect.Proxy;
21  
22  import javax.jdo.JDOHelper;
23  import javax.jdo.PersistenceManager;
24  
25  import javax.jdo.util.AbstractTest;
26  import javax.jdo.util.BatchTestRunner;
27  
28  /***
29   * This class tests the StateInterrogation interface. The test is in
30   * several parts:
31   * <ul><li>  Add and remove the StateInterrogation instance
32   * </li><li> test interrogatives to return the correct answer
33   * </li><li> test getters to return the correct answer
34   * </li><li> test makeDirty to return.
35   * </li></ul>
36   * We use an mock implementation of StateInterrogation interface to 
37   * log when calls are received and to return the correct replies.
38   * We use a java.lang.reflect.Proxy for the getPersistenceManager call
39   * because it's too much work to mock it.
40   */
41  public class StateInterrogationTest extends AbstractTest {
42      
43      private JDOImplHelper implHelper = JDOImplHelper.getInstance();
44      
45      private JDOHelper helper = new JDOHelper();
46      
47      /*** Creates a new instance of StateInterrogationTest */
48      public StateInterrogationTest() {
49      }
50      
51      /***
52       * @param args the command line arguments
53       */
54      public static void main(String[] args) {
55          BatchTestRunner.run(StateInterrogationTest.class);
56      }
57      
58      public void testGetObjectIdNull() {
59          Object id2 = helper.getObjectId(nbcpc2);
60          assertNull("ObjectId should be null before addStateInterrogations",
61                  id2);
62          addStateInterrogations();
63          Object id = helper.getObjectId(Boolean.TRUE);
64          assertNull("ObjectId should be null for non-pc instances", 
65                  id);
66      }
67  
68      public void testGetObjectId() {
69          addStateInterrogations();
70          Object id2 = helper.getObjectId(nbcpc2);
71          assertNotNull("ObjectId should not be null", 
72                  id2);
73          assertEquals("ObjectId should be 2", 
74                  2, id2.hashCode());
75          Object id20 = helper.getObjectId(nbcpc2);
76          assertEquals("ObjectIds from same object should be equal", 
77                  id2, id20);
78      }
79  
80      public void testRemoveStateInterrogation() {
81          addStateInterrogations();
82          Object id2 = helper.getObjectId(nbcpc2);
83          assertNotNull("ObjectId should not be null", 
84                  id2);
85          assertEquals("ObjectId should be 2", 
86                  2, id2.hashCode());
87          implHelper.removeStateInterrogation(si2);
88          implHelper.removeStateInterrogation(si0);
89          Object id21 = helper.getObjectId(nbcpc2);
90          assertNull("ObjectId should be null after RemoveStateInterrogation",
91                  id21);
92          Object id1 = helper.getObjectId(nbcpc1);
93          assertNotNull("ObjectId should not be null", 
94                  id1);
95          assertEquals("ObjectId should be 1", 
96                  1, id1.hashCode());
97      }
98  
99      public void testGetTransactionalObjectIdNull() {
100         Object id2 = helper.getTransactionalObjectId(nbcpc2);
101         assertNull("TransactionalObjectId should be null before addStateInterrogations",
102                 id2);
103         addStateInterrogations();
104         Object id = helper.getTransactionalObjectId(Boolean.TRUE);
105         assertNull("TransactionalObjectId should be null for non-pc instances", 
106                 id);
107     }
108 
109     public void testGetTransactionalObjectId() {
110         addStateInterrogations();
111         Object id2 = helper.getTransactionalObjectId(nbcpc2);
112         assertNotNull("TransactionalObjectId should not be null", 
113                 id2);
114         assertEquals("TransactionalObjectId should be 2", 
115                 2, id2.hashCode());
116         Object id20 = helper.getTransactionalObjectId(nbcpc2);
117         assertEquals("TransactionalObjectIds from same object should be equal", 
118                 id2, id20);
119     }
120 
121     public void testGetPersistenceManagerNull() {
122         Object pm2 = helper.getPersistenceManager(nbcpc2);
123         assertNull("PersistenceManager should be null before addStateInterrogations",
124                 pm2);
125         addStateInterrogations();
126         Object pm = helper.getPersistenceManager(Boolean.TRUE);
127         assertNull("PersistenceManager should be null for non-pc instances", 
128                 pm);
129     }
130 
131     public void testGetPersistenceManager() {
132         addStateInterrogations();
133         Object pm2 = helper.getPersistenceManager(nbcpc2);
134         assertNotNull("PersistenceManager should not be null", 
135                 pm2);
136         assertEquals("PersistenceManager should be 2", 
137                 2, pm2.hashCode());
138         Object pm20 = helper.getPersistenceManager(nbcpc2);
139         assertEquals("PersistenceManagers from same object should be equal", 
140                 pm2, pm20);
141     }
142 
143     public void testGetVersionNull() {
144         Object id2 = helper.getVersion(nbcpc2);
145         assertNull("Version should be null before addStateInterrogations",
146                 id2);
147         addStateInterrogations();
148         Object id = helper.getVersion(Boolean.TRUE);
149         assertNull("Version should be null for non-pc instances", 
150                 id);
151     }
152 
153     public void testGetVersion() {
154         addStateInterrogations();
155         Object id2 = helper.getVersion(nbcpc2);
156         assertNotNull("Version should not be null", 
157                 id2);
158         assertEquals("Version should be 2", 
159                 2, id2.hashCode());
160         Object id20 = helper.getVersion(nbcpc2);
161         assertEquals("Versions from same object should be equal", 
162                 id2, id20);
163     }
164 
165     public void testIsDeletedFalse() {
166         assertFalse("IsDeleted should be false before addStateInterrogations",
167                 helper.isDeleted(nbcpc2));
168         addStateInterrogations();
169         assertFalse("IsDeleted should be false for non-pc instances", 
170                 helper.isDeleted(Boolean.TRUE));
171         implHelper.removeStateInterrogation(si2);
172         assertFalse("IsDeleted should be false after removeStateInterrogations", 
173                 helper.isDeleted(nbcpc2));
174     }
175 
176     public void testIsDeletedMine() {
177         addStateInterrogations();
178         assertTrue("IsDeleted should be true for nbcpc1", 
179                 helper.isDeleted(nbcpc1));
180         assertFalse("IsDeleted should be false for nbcpc2", 
181                 helper.isDeleted(nbcpc2));
182     }
183 
184     public void testIsDetachedFalse() {
185         assertFalse("IsDetached should be false before addStateInterrogations",
186                 helper.isDetached(nbcpc2));
187         addStateInterrogations();
188         assertFalse("IsDetached should be false for non-pc instances", 
189                 helper.isDetached(Boolean.TRUE));
190         implHelper.removeStateInterrogation(si2);
191         assertFalse("IsDetached should be false after removeStateInterrogations", 
192                 helper.isDetached(nbcpc2));
193     }
194 
195     public void testIsDetachedMine() {
196         addStateInterrogations();
197         assertTrue("IsDetached should be true for nbcpc1", 
198                 helper.isDetached(nbcpc1));
199         assertFalse("IsDetached should be false for nbcpc2", 
200                 helper.isDetached(nbcpc2));
201     }
202 
203     public void testIsDirtyFalse() {
204         assertFalse("IsDirty should be false before addStateInterrogations",
205                 helper.isDirty(nbcpc2));
206         addStateInterrogations();
207         assertFalse("IsDirty should be false for non-pc instances", 
208                 helper.isDirty(Boolean.TRUE));
209         implHelper.removeStateInterrogation(si2);
210         nbcpc2.setDirty(true);
211         assertFalse("IsDirty should be false after removeStateInterrogations", 
212                 helper.isDirty(nbcpc2));
213         nbcpc2.setDirty(false);
214     }
215 
216     public void testIsDirtyMine() {
217         addStateInterrogations();
218         nbcpc1.setDirty(true);
219         assertTrue("IsDirty should be true for nbcpc1 after setDirty(true)", 
220                 helper.isDirty(nbcpc1));
221         nbcpc1.setDirty(false);
222         assertFalse("IsDirty should be false for nbcpc1 after setDirty(false)", 
223                 helper.isDirty(nbcpc1));
224         assertFalse("IsDirty should be false for nbcpc2", 
225                 helper.isDirty(nbcpc2));
226     }
227 
228     public void testIsNewFalse() {
229         assertFalse("IsNew should be false before addStateInterrogations",
230                 helper.isNew(nbcpc2));
231         addStateInterrogations();
232         assertFalse("IsNew should be false for non-pc instances", 
233                 helper.isNew(Boolean.TRUE));
234         implHelper.removeStateInterrogation(si2);
235         assertFalse("IsNew should be false after removeStateInterrogations", 
236                 helper.isNew(nbcpc2));
237     }
238 
239     public void testIsNewMine() {
240         addStateInterrogations();
241         assertTrue("IsNew should be true for nbcpc1", 
242                 helper.isNew(nbcpc1));
243         assertFalse("IsNew should be false for nbcpc2", 
244                 helper.isNew(nbcpc2));
245     }
246 
247     public void testIsPersistentFalse() {
248         assertFalse("IsPersistent should be false before addStateInterrogations",
249                 helper.isPersistent(nbcpc2));
250         addStateInterrogations();
251         assertFalse("IsPersistent should be false for non-pc instances", 
252                 helper.isPersistent(Boolean.TRUE));
253         implHelper.removeStateInterrogation(si2);
254         assertFalse("IsPersistent should be false after removeStateInterrogations", 
255                 helper.isPersistent(nbcpc2));
256     }
257 
258     public void testIsPersistentMine() {
259         addStateInterrogations();
260         assertTrue("IsPersistent should be true for nbcpc1", 
261                 helper.isPersistent(nbcpc1));
262         assertFalse("IsPersistent should be false for nbcpc2", 
263                 helper.isPersistent(nbcpc2));
264     }
265 
266     public void testIsTransactionalFalse() {
267         assertFalse("IsTransactional should be false before addStateInterrogations",
268                 helper.isTransactional(nbcpc2));
269         addStateInterrogations();
270         assertFalse("IsTransactional should be false for non-pc instances", 
271                 helper.isTransactional(Boolean.TRUE));
272         implHelper.removeStateInterrogation(si2);
273         assertFalse("IsTransactional should be false after removeStateInterrogations", 
274                 helper.isTransactional(nbcpc2));
275     }
276 
277     public void testIsTransactionalMine() {
278         addStateInterrogations();
279         assertTrue("IsTransactional should be true for nbcpc1", 
280                 helper.isTransactional(nbcpc1));
281         assertFalse("IsTransactional should be false for nbcpc2", 
282                 helper.isTransactional(nbcpc2));
283     }
284 
285     public void testMakeDirtyFalse() {
286         helper.makeDirty(nbcpc2, "");
287         nbcpc2.setDirty(true);
288         assertFalse("IsDirty should be false before addStateInterrogations",
289                 helper.isDirty(nbcpc2));
290         addStateInterrogations();
291         implHelper.removeStateInterrogation(si2);
292         nbcpc2.setDirty(false);
293         helper.makeDirty(nbcpc2, "");
294         assertFalse("IsDirty should be false after removeStateInterrogations", 
295                 helper.isDirty(nbcpc2));
296     }
297 
298     public void testMakeDirtyMine() {
299         addStateInterrogations();
300         helper.makeDirty(nbcpc1, "");
301         assertTrue("IsDirty should be true for nbcpc1", 
302                 helper.isDirty(nbcpc1));
303         nbcpc1.setDirty(false);
304         assertFalse("IsDirty should be false after setDirty(false)",
305                 helper.isDirty(nbcpc1));
306     }
307 
308     public void tearDown() {
309         removeStateInterrogations();
310     }
311     
312     public void addStateInterrogations() {
313         implHelper.addStateInterrogation(si0);
314         implHelper.addStateInterrogation(si1);
315         implHelper.addStateInterrogation(si2);
316     }
317     
318     public void removeStateInterrogations() {
319         implHelper.removeStateInterrogation(si0);
320         implHelper.removeStateInterrogation(si1);
321         implHelper.removeStateInterrogation(si2);
322     }
323     
324     /*** 
325      * The non-binary-compatible PersistenceManager class instances.
326      */
327     static private PersistenceManager pmProxy0 = (PersistenceManager)
328             Proxy.newProxyInstance(
329                 PersistenceManager.class.getClassLoader(),
330                 new Class[] {PersistenceManager.class},
331                 new InvocationHandlerImpl(0));
332 
333     /*** 
334      * The non-binary-compatible PersistenceManager class instances.
335      */
336     static private PersistenceManager pmProxy1 = (PersistenceManager)
337             Proxy.newProxyInstance(
338                 PersistenceManager.class.getClassLoader(),
339                 new Class[] {PersistenceManager.class},
340                 new InvocationHandlerImpl(1));
341 
342     /*** 
343      * The non-binary-compatible PersistenceManager class instances.
344      */
345     static private PersistenceManager pmProxy2 = (PersistenceManager)
346             Proxy.newProxyInstance(
347                 PersistenceManager.class.getClassLoader(),
348                 new Class[] {PersistenceManager.class},
349                 new InvocationHandlerImpl(2));
350 
351     /***
352      * The array of PersistenceManager proxies
353      */
354     static PersistenceManager[] pmProxies = {pmProxy0, pmProxy1, pmProxy2};
355     
356     /*** 
357      * The array of NonBinaryCompatiblePersistenceCapable instances.
358      */
359     NonBinaryCompatiblePersistenceCapable nbcpc0 = 
360        new NonBinaryCompatiblePersistenceCapable(0);
361     NonBinaryCompatiblePersistenceCapable nbcpc1 = 
362        new NonBinaryCompatiblePersistenceCapable(1);
363     NonBinaryCompatiblePersistenceCapable nbcpc2 = 
364        new NonBinaryCompatiblePersistenceCapable(2);
365     
366     NonBinaryCompatiblePersistenceCapable[] nbcpcs = {nbcpc0, nbcpc1, nbcpc2};
367     
368     /*** 
369      * The array of StateInterrogations
370      */
371     static StateInterrogation si0 = new StateInterrogationImpl(0);
372     static StateInterrogation si1 = new StateInterrogationImpl(1);
373     static StateInterrogation si2 = new StateInterrogationImpl(2);
374     static StateInterrogation[] sis = {si0, si1, si2};
375     
376     /*** 
377      * The StateInterrogation implementation manages 
378      * NonBinaryCompatiblePersistenceCapable instances that have a 
379      * hashCode equal to their own.
380      *
381      * <P>For the methods returning Object, return null if the object
382      * is not managed by this StateInterrogation.
383      *
384      * <P>For the methods returning Boolean, return null if the object
385      * is not managed by this StateInterrogation.
386      *
387      * <P>For the makeDirty method, return false if the object
388      * is not managed by this StateInterrogation.
389      */
390     private static class StateInterrogationImpl implements StateInterrogation {
391         
392         private int id;
393         
394         public int hashCode() {
395             return id;
396         }
397         
398         private StateInterrogationImpl(int id) {
399             this.id = id;
400         }
401         
402         public boolean equals(Object other) {
403             if (other.getClass() != StateInterrogationImpl.class) return false;
404             return (other.hashCode() == id);
405         }
406 
407         private boolean isMine(Object pc) {
408             return pc.hashCode() == id;
409         }
410 
411         public javax.jdo.PersistenceManager getPersistenceManager(Object pc) {
412             return isMine(pc)?pmProxies[id]:null;
413         }
414 
415         public Object getObjectId(Object pc) {
416             return isMine(pc)?new ObjectIdImpl(id):null;
417         }
418         
419         public Object getTransactionalObjectId(Object pc) {
420             return isMine(pc)?new ObjectIdImpl(id):null;
421         }
422 
423         public Object getVersion(Object pc) {
424             return isMine(pc)?new ObjectIdImpl(id):null;
425         }
426 
427         public Boolean isDeleted(Object pc) {
428             if (isMine(pc)) {
429                 return (pc.hashCode()==1)?Boolean.TRUE:Boolean.FALSE;
430             } else return null;
431         }
432 
433         public Boolean isDetached(Object pc) {
434             if (isMine(pc)) {
435                 return (pc.hashCode()==1)?Boolean.TRUE:Boolean.FALSE;
436             } else return null;
437         }
438 
439         public Boolean isDirty(Object pc) {
440             if (isMine(pc)) {
441                 return ((NonBinaryCompatiblePersistenceCapable)pc).isDirty()
442                     ?Boolean.TRUE:Boolean.FALSE;
443             } else return null;
444         }
445 
446         public Boolean isNew(Object pc) {
447             if (isMine(pc)) {
448                 return (pc.hashCode()==1)?Boolean.TRUE:Boolean.FALSE;
449             } else return null;
450         }
451 
452         public Boolean isPersistent(Object pc) {
453             if (isMine(pc)) {
454                 return (pc.hashCode()==1)?Boolean.TRUE:Boolean.FALSE;
455             } else return null;
456         }
457 
458         public Boolean isTransactional(Object pc) {
459             if (isMine(pc)) {
460                 return (pc.hashCode()==1)?Boolean.TRUE:Boolean.FALSE;
461             } else return null;
462         }
463 
464         public boolean makeDirty(Object pc, String fieldName) {
465             if (isMine(pc)) {
466                 ((NonBinaryCompatiblePersistenceCapable)pc).setDirty(true);
467                 return true;
468             } else return false;
469         }
470     }
471     
472     /*** 
473      * The non-binary-compatible PersistenceCapable class.
474      */
475     public static class NonBinaryCompatiblePersistenceCapable {
476         private int id;
477         private boolean dirty = false;
478         private NonBinaryCompatiblePersistenceCapable(int id) {
479             this.id = id;
480         }
481         public int hashCode() {
482             return id;
483         }
484         public void setDirty(boolean dirty) {
485             this.dirty = dirty;
486         }
487         public boolean isDirty() {
488             return dirty;
489         }
490     }
491     
492     /*** 
493      * The non-binary-compatible object id class.
494      */
495     public static class ObjectIdImpl {
496         private int id;
497         private ObjectIdImpl(int id) {
498             this.id = id;
499         }
500         public int hashCode() {
501             return id;
502         }
503         public boolean equals(Object other) {
504             if (other.getClass() != ObjectIdImpl.class) return false;
505             return (other.hashCode() == id);            
506         }
507     }
508     
509     /*** 
510      * The non-binary-compatible InvocationHandler class
511      *  for PersistenceManager proxy.
512      */
513     private static class InvocationHandlerImpl implements InvocationHandler {
514         private int id;
515         private InvocationHandlerImpl(int id) {
516             this.id = id;
517         }
518          public int hashCode() {
519             return id;
520         }
521         public boolean equals(Object other) {
522             if (other.getClass() != ObjectIdImpl.class) return false;
523             return (other.hashCode() == id); 
524         }
525         public Object invoke(Object obj, java.lang.reflect.Method method, 
526                 Object[] params) throws Throwable {
527             String name = method.getName();
528             if (name == "hashCode") {
529                 return new Integer(id);
530             } else if (name == "equals") {
531                 Object other = params[0];
532                 if (!(other instanceof PersistenceManager)) return Boolean.FALSE;
533                 int otherid = Proxy.getInvocationHandler(other).hashCode();
534                 if (otherid == id) return Boolean.TRUE;
535                 return Boolean.FALSE;
536             }
537             return null;
538         }
539     }
540 }