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